[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\n\njobs:\n  # Build Website\n  doc:\n    working_directory: ~/fabric8io/dmp-docs\n    docker:\n    - image: circleci/node:9-browsers\n    steps:\n    - checkout\n    - run:\n      - git config --global user.email \"circleci@fabric8.io\"\n      - git config --global user.name \"CircleCI\"\n    - run: ./doc/ci-docs.sh\n\n  # Run unit tests\n  build:\n    working_directory: ~/fabric8io/docker-maven-plugin\n    docker:\n    - image: circleci/openjdk:11\n    steps:\n    - checkout\n    - restore_cache:\n        key: dmp-{{ checksum \"pom.xml\" }}\n    - run: mvn install -Pjacoco\n    - run: bash <(curl -s https://codecov.io/bash)\n    - save_cache:\n        key: dmp-{{ checksum \"pom.xml\" }}\n        paths:\n        - ~/.m2\n\n  # Run integration tests\n  e2e-tests:\n    working_directory: ~/fabric8io/docker-maven-plugin\n    machine: true\n    steps:\n    - checkout\n    - restore_cache:\n        key: dmp-{{ checksum \"pom.xml\" }}\n    - run:\n        command: |\n          mvn clean install -DskipTests\n          PROJECT_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)\n          cd it/\n          find . -type f -exec sed -i 's/0.29-SNAPSHOT/'\"$PROJECT_VERSION\"'/g' {} +\n          mvn clean install\n    - save_cache:\n        key: dmp-{{ checksum \"pom.xml\" }}\n        paths:\n        - ~/.m2\n\n  sonar-pr:\n    working_directory: ~/fabric8io/dmp-sonar-pr\n    docker:\n    - image: circleci/openjdk:11\n    steps:\n    - checkout\n    - restore_cache:\n        key: dmp-sonar-pr-{{ checksum \"pom.xml\" }}\n    - run: |\n        if [ -n \"${CIRCLE_PR_NUMBER}\" ]; then\n          mvn clean -Pjacoco org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar \\\n             -Dsonar.pullrequest.base=master \\\n             -Dsonar.pullrequest.branch=${CIRCLE_BRANCH} \\\n             -Dsonar.pullrequest.key=${CIRCLE_PR_NUMBER} \\\n             -Dsonar.pullrequest.provider=GitHub \\\n             -Dsonar.pullrequest.github.repository=fabric8io/docker-maven-plugin \\\n             -Dsonar.github.oauth=${GITHUB_COMMENT_TOKEN} \\\n             -Dsonar.host.url=https://sonarcloud.io \\\n             -Dsonar.organization=default \\\n             -Dsonar.login=${SONARQUBE_TOKEN}\n        else\n          echo \"No Sonar PR analysis as this is not a pull request\"\n        fi\n\n    - save_cache:\n        key: dmp-sonar-pr-{{ checksum \"pom.xml\" }}\n        paths:\n        - ~/.m2\n\n  sonar:\n    working_directory: ~/fabric8io/dmp-sonar\n    docker:\n    - image: circleci/openjdk:11\n    steps:\n    - checkout\n    - restore_cache:\n        key: dmp-sonar-{{ checksum \"pom.xml\" }}\n    - run: |\n        mvn clean -Pjacoco org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar \\\n        -Dsonar.host.url=https://sonarcloud.io \\\n        -Dsonar.organization=default \\\n        -Dsonar.login=${SONARQUBE_TOKEN}\n    - save_cache:\n        key: dmp-sonar-{{ checksum \"pom.xml\" }}\n        paths:\n        - ~/.m2\n\nworkflows:\n  version: 2\n  all:\n    jobs:\n    - doc:\n        filters:\n          branches:\n            only: dmp.fabric8.io\n    - build\n    - e2e-tests\n    - sonar-pr\n    - sonar:\n        filters:\n          branches:\n            only: master\n"
  },
  {
    "path": ".codecov.yml",
    "content": "coverage:\n  range: \"45...90\"\n  precision: 2\n  round: down\n  status:\n    project:\n      default:\n        threshold: \"2%\"\ncomment:\n  layout: \"header, diff, tree, sunburst\"\n  require_changes: true\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!-- Please fill out the following information to help us in analyzing the issue, but feel free to skip it if you don't have the information at hand or if it does not apply. Please remove everything which does not apply to the issue. -->\n\n### Description\n\n### Info\n\n* d-m-p version :\n* Maven version (`mvn -v`) :\n```\n\n```\n* Docker version :\n* If it's a bug, how to reproduce :\n* If it's a feature request, what is your use case :\n* Sample project : *[GitHub Clone URL]*\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n.DS_Store\ntarget/\n*.iml\n.classpath\n.project\n.settings\n.*.md.html\ntemp"
  },
  {
    "path": ".mvn/jvm.config",
    "content": "-Xmx2048m -Djava.awt.headless=true -XX:+UseG1GC -XX:+UseStringDeduplication\n"
  },
  {
    "path": ".mvn/maven.config",
    "content": "-Dmaven.artifact.threads=8\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing \n\nYou want to contribute ? Awesome ! We **♥︎♥︎ LOVE ♥︎♥︎** contributions ;-)\n\nHere some things to check out when doing a PR:\n\n* Please sign-off your commits as described below.\n* If adding a new feature please [update the documentation](https://github.com/fabric8io/docker-maven-plugin/blob/master/src/main/asciidoc/), too.\n* Don't forget the unit tests.\n* If adding a new configuration option, don't forget to add this to the [PropertyHandler](https://github.com/fabric8io/docker-maven-plugin/blob/master/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java), too.\n\nHowever, if you can't do some of the points above, please still consider contributing. Simply ask us on `#fabric8` at Freenode or via an GitHub [issue](https://github.com/fabric8io/docker-maven-plugin/issues). We are not dogmatic.\n\n### Signing off your commits\n\nPull requests are highly appreciated and most of them get applied. However, you\nmust sign-off your code so that you certify that your  contributions is compatible with the\nlicense of this project (which is the [Apache Public License 2](../LICENSE)). The sign-off also certifies\nthat you wrote it or otherwise have the right to\npass it on as an open-source patch under the APL 2.  The rules are simple: if you\ncan certify the below (from\n[developercertificate.org](http://developercertificate.org/)):\n\n```\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n660 York Street, Suite 102,\nSan Francisco, CA 94110 USA\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n```\n\nthen you just add a line to every git commit message:\n\n    Signed-off-by: Joe Smith <joe.smith@email.com>\n\nwith your real name (first and last name)\n\nIf you set your `user.name` and `user.email` git configs, you can sign your\ncommit automatically with `git commit -s`. If you forgot this you can\nuse `git commit -s --amend` to add this in retrospective for the last commit.\nIf you need to sign-off multiple commits within a branch, you need to do an interactive\nrebase with `git rebase -i`. A nice shortcut for signing off every commit in a branch can\nbe provided with this [alias](http://stackoverflow.com/questions/25570947/how-to-use-git-interactive-rebase-for-signing-off-a-series-of-commits)\nwhich you can put into your `~/.gitconfig`:\n\n````\n[alias]\n  # Usage: git signoff-rebase [base-commit]\n  signoff-rebase = \"!EDITOR='sed -i -re s/^pick/e/' sh -c 'git rebase -i $1 && while test -f .git/rebase-merge/interactive; do git commit --amend --signoff --no-edit && git rebase --continue; done' -\"\n  # Ideally we would use GIT_SEQUENCE_EDITOR in the above instead of EDITOR but that's not supported for git < 1.7.8.\n````\n\nWhen sending pull request we prefer that to be a single commit. So please squash your commits\nwith an interactive rebase before sending the pull request.  This is nicely explained [here](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request).\n\nSaid all this, don't hesitate to ask when there are any problems or you have an issue with this process.\n"
  },
  {
    "path": "Jenkinsfile",
    "content": "#!/usr/bin/groovy\n@Library('github.com/fabric8io/fabric8-pipeline-library@master')\ndef dummy\nmavenNode {\n  dockerNode {\n    checkout scm\n    sh \"git remote set-url origin git@github.com:fabric8io/docker-maven-plugin.git\"\n\n    def pipeline = load 'release.groovy'\n\n    stage 'Stage'\n    def stagedProject = pipeline.stage()\n\n    stage 'Promote'\n    pipeline.release(stagedProject)\n\n  }\n}\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."
  },
  {
    "path": "README.md",
    "content": "# docker-maven-plugin\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.fabric8/docker-maven-plugin/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/io.fabric8/docker-maven-plugin/)\n[![Circle CI](https://circleci.com/gh/fabric8io/docker-maven-plugin/tree/master.svg?style=shield)](https://circleci.com/gh/fabric8io/docker-maven-plugin/tree/master)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=io.fabric8%3Adocker-maven-plugin&metric=coverage)](https://sonarcloud.io/dashboard?id=io.fabric8%3Adocker-maven-plugin)\n[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=io.fabric8%3Adocker-maven-plugin&metric=sqale_index)](https://sonarcloud.io/dashboard?id=io.fabric8%3Adocker-maven-plugin)\n\nThis is a Maven plugin for building Docker images and managing containers for integration tests.\nIt works with Maven 3.0.5 and Docker 1.6.0 or later.\n\n#### Goals\n\n| Goal                                                                                            | Description                                      | Default Lifecycle Phase |\n| ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------- |\n| [`docker:start`](https://fabric8io.github.io/docker-maven-plugin/#docker:start)                 | Create and start containers                      | pre-integration-test    |\n| [`docker:stop`](https://fabric8io.github.io/docker-maven-plugin/#docker:stop)                   | Stop and destroy containers                      | post-integration-test   |\n| [`docker:build`](https://fabric8io.github.io/docker-maven-plugin/#docker:build)                 | Build images                                     | install                 |\n| [`docker:watch`](https://fabric8io.github.io/docker-maven-plugin/#docker:watch)                 | Watch for doing rebuilds and restarts            |                         |\n| [`docker:push`](https://fabric8io.github.io/docker-maven-plugin/#docker:push)                   | Push images to a registry                        | deploy                  |\n| [`docker:remove`](https://fabric8io.github.io/docker-maven-plugin/#docker:remove)               | Remove images from local docker host             | post-integration-test   |\n| [`docker:logs`](https://fabric8io.github.io/docker-maven-plugin/#docker:logs)                   | Show container logs                              |                         |\n| [`docker:source`](https://fabric8io.github.io/docker-maven-plugin/#docker:source)               | Attach docker build archive to Maven project     | package                 |\n| [`docker:save`](https://fabric8io.github.io/docker-maven-plugin/#docker:save)                   | Save image to a file                             |                         |\n| [`docker:volume-create`](https://fabric8io.github.io/docker-maven-plugin/#docker:volume-create) | Create a volume to share data between containers | pre-integration-test    |\n| [`docker:volume-remove`](https://fabric8io.github.io/docker-maven-plugin/#docker:volume-remove) | Remove a created volume                          | post-integration-test   |\n\n#### Documentation\n\n* The **[User Manual](https://fabric8io.github.io/docker-maven-plugin)** [[PDF](https://fabric8io.github.io/docker-maven-plugin/docker-maven-plugin.pdf)] has a detailed reference for all and everything.\n* The [Introduction](doc/intro.md) is a high level\n  overview of this plugin's features and provides an usage example.\n  provided goals and possible configuration parameters.\n* [Examples](doc/examples.md) are below `samples/` and contain example\n  setups which you can use as blueprints for your own projects.\n* [ChangeLog](doc/changelog.md) has the release history of this plugin.\n* [Contributing](CONTRIBUTING.md) explains how you can contribute to this project. Pull requests are highly appreciated!\n\n\n#### Docker API Support\n\n* Docker 1.6 (**v1.18**) is the minimal required version\n* Docker 1.8.1 (**v1.20**) is required for `docker:watch`\n* Docker 1.9 (**v1.21**) is required for using custom networks and build args.\n"
  },
  {
    "path": "doc/changelog.md",
    "content": "# ChangeLog\n\n* **0.34.0** (2020-09-13)\n  - Support `ARG` in `FROM` ([#859](https://github.com/fabric8io/docker-maven-plugin/issues/859))\n  - Handle authentication tokens returned from credential helpers ([#1348](https://github.com/fabric8io/docker-maven-plugin/issues/1348))\n  - Migrate from joda-time to java.time ([#1025](https://github.com/fabric8io/docker-maven-plugin/issues/1025))\n    The handling of Y changes when the week straddle the New year ([Stack Overflow](https://stackoverflow.com/questions/26431882/difference-between-year-of-era-and-week-based-year))\n  - Fix JSON error when parsin tafs ([#1354](https://github.com/fabric8io/docker-maven-plugin/issues/1354))\n  - Add `skipPush` option to build image configuration ([#1243](https://github.com/fabric8io/docker-maven-plugin/issues/1243))\n  - docker.container.<alias>.ip property is no longer set ([#1242](https://github.com/fabric8io/docker-maven-plugin/issues/1242))\n  - Support `squash` in build options to squash newly built layers into a single layer ([#785](https://github.com/fabric8io/docker-maven-plugin/issues/785)) \n  - Support for JIB mode([#1277](https://github.com/fabric8io/docker-maven-plugin/pull/1277))\n\n* **0.33.0** (2020-01-21)\n  - Update to jnr-unixsocket 0.25 to solve concurrency issues ([#552](https://github.com/fabric8io/docker-maven-plugin/issues/552))\n  - Udate ECR AuthorizationToken URL to new endpoint ([#1317](https://github.com/fabric8io/docker-maven-plugin/issues/1317))\n  - Allow including `com.amazonaws:aws-java-sdk-core` as plugin dependency to pick up various forms of AWS credentials with which to authenticate at AWS ECR ([#1311](https://github.com/fabric8io/docker-maven-plugin/issues/1311))\n\n* **0.32.0** (2020-01-08)\n  - Support building dockerFile without pushing it to docker server ([#1197](https://github.com/fabric8io/docker-maven-plugin/issues/1197))\n  - Update to jnr-unixsocket 0.23\n  - Add null check for null instance in config.json for email ([#1262](https://github.com/fabric8io/docker-maven-plugin/issues/1262))\n  - Allow merging of image configurations using `<imagesMap>` ([#1259](https://github.com/fabric8io/docker-maven-plugin/issues/1259))\n  - Update to joda-time 2.10.4 ([#706](https://github.com/fabric8io/docker-maven-plugin/issues/706))\n  - Add docker:build support for 'network' option ([#1030](https://github.com/fabric8io/docker-maven-plugin/issues/1030))\n  - Avoiding chown to reduce the image size ([#544](https://github.com/fabric8io/docker-maven-plugin/issues/544))\n    (_Note: Assembly user format `user:user:user` with the third user option has been marked deprecated\n    and will not be available in future versions of plugin_)\n  - Failure referencing a previous staged image in FROM clause ([#1264](https://github.com/fabric8io/docker-maven-plugin/issues/1264))\n  - Treat bridged and default network mode the same ([#1234](https://github.com/fabric8io/docker-maven-plugin/issues/1234))\n  - Fix NPE when cacheFrom is missing from config ([#1274](https://github.com/fabric8io/docker-maven-plugin/issues/1274))\n  - Fix healthy option regression introduced in 0.25.0 ([#1279](https://github.com/fabric8io/docker-maven-plugin/issues/1279))\n  - Allow killing and removing all spawned containers ([#1182](https://github.com/fabric8io/docker-maven-plugin/issues/1182))\n  - Deprecated \"authToken\" for ECR authentication in favor of \"auth\" ([#1286](https://github.com/fabric8io/docker-maven-plugin/issues/1286))\n  - Allow overriding of existing image in creation of temporary one with same tag before push ([#838](https://github.com/fabric8io/docker-maven-plugin/issues/838))\n  - Pick up AWS credentials from ENV variables ([#1310](https://github.com/fabric8io/docker-maven-plugin/issues/1310))\n  - Fix accidentally disabled logging under certain conditions ([#1291](https://github.com/fabric8io/docker-maven-plugin/issues/1291))\n  - When stopping containers, try harder to remove containers, even if one fails ([#1251](https://github.com/fabric8io/docker-maven-plugin/issues/1251))\n  - Added integration tests ([#1209](https://github.com/fabric8io/docker-maven-plugin/issues/1209))\n\n* **0.31.0** (2019-08-10)\n  - Fix test cases on Windows ([#1220](https://github.com/fabric8io/docker-maven-plugin/issues/1220))\n  - ECR credentials from IAM Task role for ECS Fargate deployment ([#1233](https://github.com/fabric8io/docker-maven-plugin/issues/1233))\n  - Fix bug in properties names extracted from docker config json file ([#1237](https://github.com/fabric8io/docker-maven-plugin/issues/1237))\n  - Fix that portPropertyFile is not written anymore ([#1112](https://github.com/fabric8io/docker-maven-plugin/issues/1112))\n  - Use identity token if found in Docker config.json ([#1249](https://github.com/fabric8io/docker-maven-plugin/issues/1249))\n  - Allow also starting with an environment variable in `targetDir` of an aseembly config instead of insisting on only absolute path-names ([#1244](https://github.com/fabric8io/docker-maven-plugin/issues/1244))\n  - Support for pattern matching in `docker:stop` and `docker:remove` ([#1215](https://github.com/fabric8io/docker-maven-plugin/issues/1215))\n  - Increase interoperability with docker-java by accepting `registry.username` and `registry.password`, too ([#1245](https://github.com/fabric8io/docker-maven-plugin/issues/1245))\n\n* **0.30.0** (2019-04-21)\n  - Restore ANSI color to Maven logging if disabled during plugin execution and enable color for Windows with Maven 3.5.0 or later. Color logging is enabled by default, but disabled if the Maven CLI disables color (e.g. in batch mode) ([#1108](https://github.com/fabric8io/docker-maven-plugin/issues/1108))\n  - Fix NPE if docker:save is called with -Dfile=file-name-only.tar ([#1203](https://github.com/fabric8io/docker-maven-plugin/issues/1203))\n  - Fix NPE in BuildImageConfiguration ([#1200](https://github.com/fabric8io/docker-maven-plugin/issues/1200))\n  - Improve GZIP compression performance for docker:save ([#1205](https://github.com/fabric8io/docker-maven-plugin/issues/1205))\n  - Allow docker:save to attach image archive as a project artifact ([#1210](https://github.com/fabric8io/docker-maven-plugin/pull/1210))\n  - Use pattern to detect image name in archive loaded during build and tag with image name from the project configuration ([#1207](https://github.com/fabric8io/docker-maven-plugin/issues/1207))\n  - Add 'cacheFrom' option to specify images to use as cache sources ([#1132](https://github.com/fabric8io/docker-maven-plugin/issues/1132))\n\nRenamed \"nocache\" to \"noCache\" for consistencies reason. \"nocache\" is still supported but deprecated and will be removed in a future version. Same is true for the global system property \"docker.nocache\" which is renamed to \"docker.noCache\" \n\n* **0.29.0** (2019-04-08)\n  - Avoid failing docker:save when no images with build configuration are present ([#1185](https://github.com/fabric8io/docker-maven-plugin/issues/1185))\n  - Reintroduce minimal API-VERSION parameter in order to support docker versions below apiVersion 1.25\n  - docs: Correct default image naming\n  - Proxy settings are being ignored ([#1148](https://github.com/fabric8io/docker-maven-plugin/issues/1148))\n  - close api version http connection ([#1152](https://github.com/fabric8io/docker-maven-plugin/issues/1152))\n  - Log more information when verbose=true  ([#917](https://github.com/fabric8io/docker-maven-plugin/issues/917))\n  - Obtain container ip address from custom network for tcp/http wait\n  - Fix http (SSL) ping with 'allowAllHosts' flag enabled\n  - Update to jnr-unixsocket 0.22\n  - Enhance @sha256 digest for tags in FROM (image_name:image_tag@sha256<digest>) ([#541](https://github.com/fabric8io/docker-maven-plugin/issues/541))\n  - Support docker SHELL setting for runCmds ([#1157](https://github.com/fabric8io/docker-maven-plugin/issues/1157))\n  - Added 'autoRemove' option for running containers ([#1179](https://github.com/fabric8io/docker-maven-plugin/issues/1179))\n  - Added support for AWS EC2 instance roles when pushing to AWS ECR ([#1186](https://github.com/fabric8io/docker-maven-plugin/issues/1186))\n  - Introduce `contextDir` configuration option which would be used to specify docker build context ([#1189](https://github.com/fabric8io/docker-maven-plugin/issues/1189))\n  - Add support for auto-pulling multiple base image for multi stage builds ([#1057](https://github.com/fabric8io/docker-maven-plugin/issues/1057))\n  - Fix usage of credential helper that do not support 'version' command ([#1159](https://github.com/fabric8io/docker-maven-plugin/issues/1159))\n\nPlease note that `dockerFileDir` is now deprecated in favor of `contextDir` which also allows absolute paths to Dockerfile with \n`dockerFile` and it will be removed in 1.0.0. It's still supported in this release but users are suggested to migrate to \n`contextDir` instead.\n  \n* **0.28.0** (2018-12-13)\n  - Update to JMockit 1.43\n  - Compiles with Java 11\n  - Update to jnr-unixsocket version to 0.21 ([#1089](https://github.com/fabric8io/docker-maven-plugin/issues/1089))\n  - Add 'readOnly' option for docker:run cto mount container's root fs read-only ([#1125](https://github.com/fabric8io/docker-maven-plugin/issues/1125))\n  - Provide container properties to the wait configuration execution ([#1111](https://github.com/fabric8io/docker-maven-plugin/issues/1111))\n  - Allow @sha256 digest for tags in FROM ([#541](https://github.com/fabric8io/docker-maven-plugin/issues/541))\n\n* **0.27.2** (2018-10-05)\n  - Fix NPE regression related to volumes (again) ([#1091](https://github.com/fabric8io/docker-maven-plugin/issues/1091))\n  - Fix NPE when stopping containers with autoCreateCustomNetworks ([#1097](https://github.com/fabric8io/docker-maven-plugin/issues/1097))\n  - Smarter API version handling ([#1060](https://github.com/fabric8io/docker-maven-plugin/issues/1060))\n  - Fix regression when calling the credential helper for authentication, leading to an exception because of the usage of an already shutdown executor service ([#1098](https://github.com/fabric8io/docker-maven-plugin/issues/1098))\n  - Add support for CPU configurations with compose ([#1102](https://github.com/fabric8io/docker-maven-plugin/issues/1102))\n\n* **0.27.1** (2018-09-28)\n  - Fix NPE when no volume configuration is present ([#1091](https://github.com/fabric8io/docker-maven-plugin/issues/1091))\n  - Allow credentialhelper look up the registry without scheme prefix ([#1068](https://github.com/fabric8io/docker-maven-plugin/issues/1068))\n\n* **0.27.0** (2018-09-26)\n  - Jump to Java 8 as minimal Java version\n  - Fix NPE in docker:remove-volumes when no volume configuration is given ([#1086](https://github.com/fabric8io/docker-maven-plugin/issues/1086))\n  - Fix NPE when no networks are configured ([#1055](https://github.com/fabric8io/docker-maven-plugin/issues/1055))\n  - Fix Base64 encoding for X-Registry-Auth used for Docker authentication ([#1084](https://github.com/fabric8io/docker-maven-plugin/issues/1084))\n  - Fix property configuration based's build detection ([#1078](https://github.com/fabric8io/docker-maven-plugin/issues/1078))\n  - Introduce container name patterns for naming containers ([#931](https://github.com/fabric8io/docker-maven-plugin/issues/931))\n  - Respect environment variables DOCKER_CONFIG, KUBECONFIG for looking up credentials ([#1083](https://github.com/fabric8io/docker-maven-plugin/issues/1083))\n  - Change from org.json with Gson for less restrictive licensing ([#1016](https://github.com/fabric8io/docker-maven-plugin/issues/1016)) ([#1064](https://github.com/fabric8io/docker-maven-plugin/issues/1064))\n  - Fix missing actions in a watch restart ([#1070](https://github.com/fabric8io/docker-maven-plugin/issues/1070))\n  - Fix for creating volumes with proper configuration during \"docker:start\" ([#986](https://github.com/fabric8io/docker-maven-plugin/issues/986))\n  - Fix logging failure on Windows ([#873](https://github.com/fabric8io/docker-maven-plugin/issues/873))\n\n* **0.26.1** (2018-07-20)\n  - Simple Dockerfile triggered also when only a single run section is given\n  - Sample added for how to use run-java-sh in simple dockerfile mode\n  - Allow both cred helpers and auth in Docker config ([#1041](https://github.com/fabric8io/docker-maven-plugin/issues/1041))\n\n* **0.26.0** (2018-05-16)\n  - Always create missing target directory for docker:save ([#1013](https://github.com/fabric8io/docker-maven-plugin/issues/1013))\n  - d-m-p plugins for adding extra files introduced. See documentation for more information.\n  - Update assembly plugin to 3.1.0 ([#1021](https://github.com/fabric8io/docker-maven-plugin/issues/1021))\n  - Add option for regenerating certificates after starting Docker Machine ([#1019](https://github.com/fabric8io/docker-maven-plugin/issues/1019))\n  - Add `startPeriod` to `healthCheck` ([#961](https://github.com/fabric8io/docker-maven-plugin/issues/961))\n  - Unbreak setting of entrypoint in `exec` form when property mode is enabled ([#1020](https://github.com/fabric8io/docker-maven-plugin/issues/1020))\n  - Fix enabling of log configuration ([#1010](https://github.com/fabric8io/docker-maven-plugin/issues/1010))\n  - Add possibility to use `docker.imagePropertyConfiguration` with multiple images ([#1001](https://github.com/fabric8io/docker-maven-plugin/issues/1001))\n  - Fix network aliases management for docker-compose mode ([#1000](https://github.com/fabric8io/docker-maven-plugin/issues/1000))\n\n* **0.25.2** (2018-04-14)\n  - Fix for docker login issue with index.docker.io using a credential helper ([#946](https://github.com/fabric8io/docker-maven-plugin/issues/946))\n\n* **0.25.1** (2018-04-12)\n  - Fix regression which broke labels and env with space ([#988](https://github.com/fabric8io/docker-maven-plugin/issues/988))\n  - Fix and enhanced zero-config Dockerfile mode\n\n* **0.25.0** (2018-04-04)\n  - Fix possible NPE when logging to a file and the parent directory does not exist yet ([#911](https://github.com/fabric8io/docker-maven-plugin/issues/911)) ([#940](https://github.com/fabric8io/docker-maven-plugin/issues/940))\n  - Change content type to \"application/json\" when talking to the Docker daemon ([#945](https://github.com/fabric8io/docker-maven-plugin/issues/945))\n  - PostStart exec breakOnError now fails fast ([#970](https://github.com/fabric8io/docker-maven-plugin/issues/970))\n  - Use docker.skip.tag property on push and remove ([#954](https://github.com/fabric8io/docker-maven-plugin/issues/954)) ([#869](https://github.com/fabric8io/docker-maven-plugin/issues/869))\n  - Property placeholders are not interpolated when they are the only thing in the XML element value ([#960](https://github.com/fabric8io/docker-maven-plugin/issues/960))\n  - Fix deadlock waiting on docker log pattern to match ([#767](https://github.com/fabric8io/docker-maven-plugin/issues/767)) ([#981](https://github.com/fabric8io/docker-maven-plugin/issues/981)) ([#947](https://github.com/fabric8io/docker-maven-plugin/issues/947))\n  - Support multiline labels and empty labels ([#968](https://github.com/fabric8io/docker-maven-plugin/issues/968))\n  - Handle multi line credential helper responses ([#930](https://github.com/fabric8io/docker-maven-plugin/issues/930))\n  - Add support for merging external properties with XML configuration ([#938](https://github.com/fabric8io/docker-maven-plugin/issues/938)) ([#948](https://github.com/fabric8io/docker-maven-plugin/issues/948))\n  - Allow to specify different environment variables for run and build via properties ([#386](https://github.com/fabric8io/docker-maven-plugin/issues/386))\n  - Add simplified configuration which picks up a plain Dockerfile automatically from `src/main/docker` ([#957](https://github.com/fabric8io/docker-maven-plugin/issues/957))\n\n* **0.24.0** (2018-02-07)\n  - Respect system properties for ECR authentication ([#897](https://github.com/fabric8io/docker-maven-plugin/issues/897))\n  - Simplified auto pull handling and moved to `imagePullPolicy` instead.\n  - Initialize shutdown hook early to allow killing of containers when waiting for a condition ([#921](https://github.com/fabric8io/docker-maven-plugin/issues/921))\n  - Fix for including in assembly in archive mode when using a Dockerfile ([#916](https://github.com/fabric8io/docker-maven-plugin/issues/916))\n  - Fix for hanging wait on log ([#904](https://github.com/fabric8io/docker-maven-plugin/issues/904))\n  - Fix for credential helper which do not return a version ([#896](https://github.com/fabric8io/docker-maven-plugin/issues/896))\n  - Also remove tagged images when calling `docker:remove` ([#193](https://github.com/fabric8io/docker-maven-plugin/issues/193))\n  - Introduced a `removeMode` for selecting the images to remove\n  - Introduced a `breakOnError` for the `postStart` and `preStop` hooks in the\n    wait configuration ([#914](https://github.com/fabric8io/docker-maven-plugin/issues/914))\n\nPlease note that `autoPullMode` is deprecated now and the behaviour of the `autoPullMode == always` has been changed slightly so that now, it really always pulls the image from the registry. Also `removeAll` for `docker:remove` is deprecated in favor of `removeMode` (and the default mode has changed slightly). Please refer to the documentation for more information.\n\n* **0.23.0** (2017-11-04)\n  - Support relative paths when binding volumes in `docker-compose.yml` ([#846](https://github.com/fabric8io/docker-maven-plugin/issues/846))\n  - Allow  the session token for AWS authentication to be included in order to allow temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign requests ([#883](https://github.com/fabric8io/docker-maven-plugin/issues/883))\n  - Add support for credential helper to authenticate against a registry ([#821](https://github.com/fabric8io/docker-maven-plugin/issues/821))\n  - Fix registry auth config in plugin configuration ([#858](https://github.com/fabric8io/docker-maven-plugin/issues/858))\n  - Preserve leading whitespace in logs ([#875](https://github.com/fabric8io/docker-maven-plugin/issues/875))\n  - Maven property interpolation in Dockerfiles ([#877](https://github.com/fabric8io/docker-maven-plugin/issues/877))\n  - Allow parameters for the log prefix ([#890](https://github.com/fabric8io/docker-maven-plugin/issues/890))\n  - When removing a volume don't error if the volume does not exist ([#788](https://github.com/fabric8io/docker-maven-plugin/issues/788))\n  - Fix warning when COPY and/or ADD with parameters are used ([#884](https://github.com/fabric8io/docker-maven-plugin/issues/884))\n\n* **0.22.1** (2017-08-28)\n  - Allow Docker compose version \"2\", too ([#829](https://github.com/fabric8io/docker-maven-plugin/issues/829))\n  - Allow a registry to be set programmatically ([#853](https://github.com/fabric8io/docker-maven-plugin/issues/853))\n\n* **0.22.0** (2017-08-24)\n  - Fix NPE when detecting cert paths ([#764](https://github.com/fabric8io/docker-maven-plugin/issues/764))\n  - Fix `skipDockerMachine` ([#759](https://github.com/fabric8io/docker-maven-plugin/issues/759))\n  - Fix property config handler to work also with dockerFile and dockerFileDir ([#790](https://github.com/fabric8io/docker-maven-plugin/issues/790))\n  - Fix `dockerFile` option when pointing to another Dockerfile name ([#784](https://github.com/fabric8io/docker-maven-plugin/issues/784))\n  - Allow comma separated list of container names in dependsOn elements ([#810](https://github.com/fabric8io/docker-maven-plugin/issues/810))\n  - Trim whitespace and ignore empty elements in build configuration ports, runCmds, tags, volumes ([#816](https://github.com/fabric8io/docker-maven-plugin/issues/816))\n  - Trim whitespace and ignore empty elements in run configuration ports ([#816](https://github.com/fabric8io/docker-maven-plugin/issues/816))\n  - Fix \"useAllReactorProjects\" in assembly ([#812](https://github.com/fabric8io/docker-maven-plugin/issues/812))\n  - Add ECDSA support ([#824](https://github.com/fabric8io/docker-maven-plugin/issues/824))\n  - Fix test failures when build under Windows ([#834](https://github.com/fabric8io/docker-maven-plugin/issues/834))\n  - Update dependencies to latest versions where possible\n\n* **0.21.0** (2017-05-16)\n  - Add wait checker for checking the exit code of a container ([#498](https://github.com/fabric8io/docker-maven-plugin/issues/498))\n  - Check for exited container when doing wait checks ([#757](https://github.com/fabric8io/docker-maven-plugin/issues/757))\n  - New assembly configuration \"name\" for specifying the directory which holds the assembly files ([#634](https://github.com/fabric8io/docker-maven-plugin/issues/634))\n  - Add support for property replacement in external Dockerfiles ([#777](https://github.com/fabric8io/docker-maven-plugin/issues/777))\n\nPlease note that now filtering in an external Dockerfiles is switched on by default. This might interfere with Docker build args, so should switch filtering off with `<filter>false</filter>` in the `<build>` configuration if you have issues with this. See also the Documentation about [Filtering](https://dmp.fabric8.io/#build-filtering) for more Details.\n\n* **0.20.1** (2017-03-29)\n  - Tune log output for image names ([#737](https://github.com/fabric8io/docker-maven-plugin/issues/737))\n  - Allow image with multiple path segments ([#694](https://github.com/fabric8io/docker-maven-plugin/issues/694))\n  - Add support for PKCS#8 private keys in pem.key file. ([#730](https://github.com/fabric8io/docker-maven-plugin/issues/730))\n  - Improve resource management for certificates and keys. ([#730](https://github.com/fabric8io/docker-maven-plugin/issues/730))\n  - When using properties for configuration only build when `from` or `fromExt` is set ([#736](https://github.com/fabric8io/docker-maven-plugin/issues/736))\n  - Add new mojo \"docker:save\" for saving the image to a file ([#687](https://github.com/fabric8io/docker-maven-plugin/issues/687))\n  - Check whether a temporary tag could be removed and throw an error if not ([#725](https://github.com/fabric8io/docker-maven-plugin/issues/725))\n  - Allow multi line matches in log output ([#628](https://github.com/fabric8io/docker-maven-plugin/issues/628))\n  - Add a wait condition on a healthcheck when starting up containers ([#719](https://github.com/fabric8io/docker-maven-plugin/issues/719))\n  - Don't use authentication from config when no \"auth\" is set ([#731](https://github.com/fabric8io/docker-maven-plugin/issues/731))\n  \n* **0.20.0** (2017-02-17)\n  - Removed `build-nofork` and `source-nofork` in favor for a more direct solution which prevents forking of the lifecycle. Please refer the documentation, chapter \"Assembly\" for more information about this.\n\nThe experimental goals `build-nofork` and `source-nofork` have been removed again. Please use `build` and `source` directly when binding to execution phases.\n\n* **0.19.1** (2017-02-09)\n\n  - Fix handling of `run` commands from properties ([#684](https://github.com/fabric8io/docker-maven-plugin/issues/684))\n  - Fix empty `<link>` causing `NullPointerException` ([#693](https://github.com/fabric8io/docker-maven-plugin/issues/693))\n\n* **0.19.0** (2017-01-03)\n  - Better log message when waiting for URL ([#640](https://github.com/fabric8io/docker-maven-plugin/issues/640))\n  - Extended authentication for AWS ECR ([#663](https://github.com/fabric8io/docker-maven-plugin/issues/663))\n  - Add two new goals: \"volume-create\" and \"volume-remove\" for volume handling independent of images.\n  - Support for loading from an tar archive (option `<build><dockerArchive>`) ([#645](https://github.com/fabric8io/docker-maven-plugin/issues/645))\n  - Support when both `dockerFileDir` and `dockerFile` are set and `dockerFile` is a relative path ([#624](https://github.com/fabric8io/docker-maven-plugin/issues/624))\n  - Fix concurrency issue when writing into log files ([#652](https://github.com/fabric8io/docker-maven-plugin/issues/652))\n  - Support any Docker build options ([#666](https://github.com/fabric8io/docker-maven-plugin/issues/666))\n\n* **0.18.1** (2016-11-17)\n  - Renamed `basedir` and `exportBasedir` in an `<assembly>` configuration to `targetDir` and `exportTargetDir` since this better reflects the purpose, i.e. the target in the Docker image to which the assembly is copied. The old name is still recognized but deprecated.\n  - Fix issue with log statements which use a single argument form\n  - Fix bug in HTTP wait configuration when using an external property handler ([#613](https://github.com/fabric8io/docker-maven-plugin/issues/613))\n  - Fix NPE for \"docker:log\" when the container to log has already been stopped ([#612](https://github.com/fabric8io/docker-maven-plugin/issues/612))\n  - Allow a protocol (tcp/udp) for the specification of a port ([#610](https://github.com/fabric8io/docker-maven-plugin/issues/610))\n\nThe following variables in the assembly configuration has been renamed for consistencies sake:\n\n * `basedir` --> `targetDir`\n * `exportBasedir` --> `exportTargetDir`\n\nThe old variable names are still accepted but will be removed for release 1.0\n\n* **0.17.2** (2016-11-3)\n  - Fix issues with an empty Docker config file\n\n* **0.17.1** (2016-10-28)\n  - Add initial [Docker compose](https://dmp.fabric8.io/#docker-compose) support ([#384](https://github.com/fabric8io/docker-maven-plugin/issues/384))\n  - Made `docker:run` running in the foreground\n  - Add lifecycle fork to package for `docker:build` and `docker:source` for ease of use. Introduced `docker:build-nofork` and `docker:source-nofork`\n  - Removed lifecycle forks for all other Mojos ([#567](https://github.com/fabric8io/docker-maven-plugin/issues/567)) ([#599](https://github.com/fabric8io/docker-maven-plugin/issues/599))\n  - Add new option `tarLongFileMode` for the assembly configuration to avoid warning for too long files ([#591](https://github.com/fabric8io/docker-maven-plugin/issues/591))\n  - Add new option `tmpfs` for `<run>` to add mount pathes for temorary file systems ([#455](https://github.com/fabric8io/docker-maven-plugin/issues/455))\n  - Changed `docker.image` to `docker.filter` and `<image>` to `<filter>`.\n\nFor 0.17 the lifecycle handling of the plugins has changed slightly. All forks to the _initialize_ phase have been removed since they collide with certain setups. Instead a fork to the _package_ phase has been introduced for `docker:build` and `docker:source` to make it easier for them to be consumed on the commandline (because otherwise at least `package` has to be added as goal so that the assembly could be constructed from the artifacts built). If you have these goals bound to an `<execution>` please use `build-nofork` and `source-nofork` instead, otherwise the package phase will be called twice.\n\nAlso the treatment of the Maven property `docker.image` has changed. This was supposed to be used as a filter which caused a lot of confusion if people accidentally put their Docker image names into this property. Now the property has no special meaning anymore, and you can use `docker.filter` now for filtering out a specific images to build. For the same reason the top-level configuration element `<image>` has been renamed to `<filter>`.\n\n* **0.16.9** (2016-10-23)\n  - Removed (undocumented) property `docker.image.name` which could be used to be inserted as a `%a` specifier part in an image name.\n  - Fixed exposing of all property and port mappings ([#583](https://github.com/fabric8io/docker-maven-plugin/issues/583))\n  - Fix concurrency issue on log wait ([#596](https://github.com/fabric8io/docker-maven-plugin/issues/596))\n  - Add Dockerfile HEALTHCHECK support ([#594](https://github.com/fabric8io/docker-maven-plugin/issues/594))\n  - Fix writing empty property files ([#592](https://github.com/fabric8io/docker-maven-plugin/issues/592))\n\n* **0.16.8** (2016-10-14)\n  - Allow multiple network links per `<link>` element ([#558](https://github.com/fabric8io/docker-maven-plugin/issues/558))\n  - Fix startup of dependent containers when using links with specific container ids ([#586](https://github.com/fabric8io/docker-maven-plugin/issues/586))\n\n* **0.16.7** (2016-10-07)\n  - Even better logging\n\n* **0.16.6** (2016-10-07)\n  - Fix concurrency issues when doing a watch on logs ([#574](https://github.com/fabric8io/docker-maven-plugin/issues/574))\n  - Break push with dedicated registry if temporary image tag already exists ([#575](https://github.com/fabric8io/docker-maven-plugin/issues/575))\n  - Reduce log output for the non color case when pulling images ([#568](https://github.com/fabric8io/docker-maven-plugin/issues/568))\n  - Add possibility to change colors in log messages\n  - Don't print a progressbar when in batch mode (mvn -B) ([#564](https://github.com/fabric8io/docker-maven-plugin/issues/564))\n  - Add `exposedProperty` key to change the alias part of the exposed container properties ([#557](https://github.com/fabric8io/docker-maven-plugin/issues/557))\n\n* **0.16.5** (2016-09-27)\n  - Refactored Docker connection parameter detection\n  - Added a <fromExt> for extended definition of base images ([#572](https://github.com/fabric8io/docker-maven-plugin/issues/572))\n\n* **0.16.4** (2016-09-26)\n  - Fix issue with DOCKER_HOST coming from Docker Machine\n  - Don't pull a 'scratch' base image ([#565](https://github.com/fabric8io/docker-maven-plugin/issues/565))\n  - Fix handling when looking up non-existing containers ([#566](https://github.com/fabric8io/docker-maven-plugin/issues/566))\n\n* **0.16.3** (2016-09-22)\n  - Add 'allowAllHosts' to ping wait checker ([#559](https://github.com/fabric8io/docker-maven-plugin/issues/559))\n  - Allow 'stopAllContainers' also as Maven properties ([#536](https://github.com/fabric8io/docker-maven-plugin/issues/536))\n  - Use alias for stopping containers when naming strategy \"alias\" is used ([#536](https://github.com/fabric8io/docker-maven-plugin/issues/536))\n  - New option 'startParallel' for docker:start to speedup execution ([#531](https://github.com/fabric8io/docker-maven-plugin/issues/531))\n  - Tuned detection of docker host connection parameters to be more extensible\n\n* **0.16.2** (2016-09-15)\n  - Fixed naming of 'buildArgs' for `docker:build` (was `args` formerly)\n  - Experimental Support for 'Docker for Windows' ([#523](https://github.com/fabric8io/docker-maven-plugin/issues/523))\n  - Remove versions from custom lifecycle deps ([#539](https://github.com/fabric8io/docker-maven-plugin/issues/539))\n  - Fix extra new line in logoutput ([#538](https://github.com/fabric8io/docker-maven-plugin/issues/538))\n\n* **0.15.16** (2016-08-03)\n  - Run 'stopContainer' in a Future to short circuit extra waiting ([#518](https://github.com/fabric8io/docker-maven-plugin/issues/518))\n  - Don't pass `docker.buildArg` values that are empty ([#529](https://github.com/fabric8io/docker-maven-plugin/issues/529))\n  - Add new implicit generated properties `docker.container.<alias>.net.<name>.ip` when custom networks are used ([#533](https://github.com/fabric8io/docker-maven-plugin/issues/533))\n\n* **0.15.14** (2016-07-29)\n  - Pattern match fix for multiline log output. Related to ([#259](https://github.com/fabric8io/docker-maven-plugin/issues/259))\n\n* **0.15.13** (2016-07-29)\n  - Add <securityOpts> for running containers in special security contexts ([#524](https://github.com/fabric8io/docker-maven-plugin/issues/524))\n  - Add support for multiples network aliases ([#466](https://github.com/fabric8io/docker-maven-plugin/issues/466))\n\n* **0.15.12** (2016-07-25)\n  - API and documentation updates\n\n* **0.15.11** (2016-07-20)\n  - Invoke the `initialize` phase before docker goals ([#315](https://github.com/fabric8io/docker-maven-plugin/issues/315))\n  - Allow images to only be pulled once per build (useful for reactor projects) ([#504](https://github.com/fabric8io/docker-maven-plugin/issues/504))\n  - Allow retry of pushing a docker image in case of a 500 error ([#508](https://github.com/fabric8io/docker-maven-plugin/issues/508))\n  - Add \"ulimits\" to run-configuration ([#484](https://github.com/fabric8io/docker-maven-plugin/issues/484))\n\n* **0.15.10** (2016-07-19)\n  - Don't do redirect when waiting on an HTTP port ([#499](https://github.com/fabric8io/docker-maven-plugin/issues/499))\n  - Removed the container fetch limit of 100 and optimized getting containers by name and image ([#513](https://github.com/fabric8io/docker-maven-plugin/issues/513))\n\n* **0.15.9** (2016-06-28)\n  - Fixed issue when target directory does not exist yet ([#497](https://github.com/fabric8io/docker-maven-plugin/issues/497))\n\n* **0.15.8** (2016-06-27)\n  - Removed image configuration caching ([#495](https://github.com/fabric8io/docker-maven-plugin/issues/495))\n  - Fix for tcp wait when used with Docker for Mac ([#430](https://github.com/fabric8io/docker-maven-plugin/issues/430))\n  - Add warning when assembly is empty when watching a Docker image ([#490](https://github.com/fabric8io/docker-maven-plugin/issues/490))\n  - Add `docker.skip.build`, `docker.skip.run`, `docker.skip.push` properties and\n    renamed `docker.skipTags` to `docker.skip.tag` ([#483](https://github.com/fabric8io/docker-maven-plugin/issues/483))\n  - Reverted jansi back to version 1.11 because of [this issue](https://github.com/fusesource/jansi/issues/58)\n  - Add new assembly config options `permissions` for fine tuning permissions in the docker.tar ([#477](https://github.com/fabric8io/docker-maven-plugin/issues/477)). Deprecated `ignorePermissions`\n    in favor of a `<permissions>ignore</permissions>`\n  - Add auto creation of custom networks if the option `autoCreateCustomNetwork` is set ([#482](https://github.com/fabric8io/docker-maven-plugin/issues/482))\n  - Support for docker machine added ([#481](https://github.com/fabric8io/docker-maven-plugin/issues/481))\n\n* **0.15.7** (2016-06-09)\n  - Add support for '.maven-dockerinclude' for including certain files in plain Dockerfile build ([#471](https://github.com/fabric8io/docker-maven-plugin/issues/471))\n  - Add support for placeholders in image names.\n  - Expose container id as Maven property `docker.container.<alias>.id` ([#412](https://github.com/fabric8io/docker-maven-plugin/issues/412))\n  - Fix broken link in documentation ([#468](https://github.com/fabric8io/docker-maven-plugin/issues/468))\n\n* **0.15.4** (2016-06-03)\n  - Update dependencies: Apache HttpClient 4.5.2, JMockit 1.23, ...\n  - Fix read-only bindings ([#462](https://github.com/fabric8io/docker-maven-plugin/issues/462))\n  - Add 'shmSize' as option to the build config ([#463](https://github.com/fabric8io/docker-maven-plugin/issues/463))\n  - Fixed issue with `memory` and `\n\n* **0.15.3** (2016-05-27)\n  - Add duration information when pulling, building and pushing images ([#313](https://github.com/fabric8io/docker-maven-plugin/issues/313))\n  - Fixed logging to always use format strings ([#457](https://github.com/fabric8io/docker-maven-plugin/issues/457))\n  - Allow extended image names ([#459](https://github.com/fabric8io/docker-maven-plugin/issues/459))\n\n* **0.15.2** (2016-05-19)\n  - More robust response stream parsing ([#436](https://github.com/fabric8io/docker-maven-plugin/issues/436))\n  - Add `docker.dockerFileDir` and `docker.dockerFile` to the properties configuration provider. ([#438](https://github.com/fabric8io/docker-maven-plugin/issues/438))\n  - Fix splitting of bind volumes for Windows pathes ([#443](https://github.com/fabric8io/docker-maven-plugin/issues/443))\n  - Add new build config option `user` for switching the user at the end of the Dockerfile. `docker.user` can be used\n    for the properties configuration provider ([#441](https://github.com/fabric8io/docker-maven-plugin/issues/441))\n  - Include dot dirs when creating the build tar ([#446](https://github.com/fabric8io/docker-maven-plugin/issues/446))\n  - Fix property handler with wait config but empty tcp wait connection ([#451](https://github.com/fabric8io/docker-maven-plugin/issues/451))\n\n* **0.15.1** (2016-05-03)\n  - Fix push / pull progress bar ([#91](https://github.com/fabric8io/docker-maven-plugin/issues/91))\n  - Allow empty environment variable ([#434](https://github.com/fabric8io/docker-maven-plugin/issues/434))\n  - Async log request get now their own HTTP client ([#344](https://github.com/fabric8io/docker-maven-plugin/issues/344)) ([#259](https://github.com/fabric8io/docker-maven-plugin/issues/259))\n\n* **0.15.0** (2016-04-27)\n  - Be more conservative when no \"warnings\" are returned on create ([#407](https://github.com/fabric8io/docker-maven-plugin/issues/407))\n  - Fix parsing of timestamps with numeric timezone ([#410](https://github.com/fabric8io/docker-maven-plugin/issues/410))\n  - Validate image names to fit Docker conventions ([#423](https://github.com/fabric8io/docker-maven-plugin/issues/423)) ([#419](https://github.com/fabric8io/docker-maven-plugin/issues/419))\n  - Add support for builds args in external Dockerfiles ([#334](https://github.com/fabric8io/docker-maven-plugin/issues/334))\n  - Move `dockerFileDir` to topLevel `<build>` and introduced `dockerFile` directive\n   `build>assembly>dockerFileDir` is now deprecated and will be removed.\n  - Add new packaging \"docker\" (build + run), \"docker-build\" (build only) and\n    \"docker-tar\" (creating source)  ([#433](https://github.com/fabric8io/docker-maven-plugin/issues/433))\n  - Add `docker:run` as an alias to `docker:start`\n  - Expose certain container properties also as Maven properties. By default\n    the format is `docker.container.<alias>.ip` for the internal IP address of container with alias `<alias>`.\n    ([#198](https://github.com/fabric8io/docker-maven-plugin/issues/198))\n\n* **0.14.2**\n  - Introduce a mode `try` for `<cleanup>` so that an image gets removed if not being still used.\n    This is the default now, which should be close enough to `true` (except that it won't fail the build\n    when the image couldn't be removed) ([#401](https://github.com/fabric8io/docker-maven-plugin/issues/401))\n\n* **0.14.1**\n  - First (test) release performed with a fabric8 CD pipeline. No new features.\n\n* **0.14.0**\n  - Add support for Docker network and `host`, `bridge` and `container` network modes ([#335](https://github.com/fabric8io/docker-maven-plugin/issues/335))\n  - Add support for older Maven versions, minimum required version is now 3.0.5 ([#290](https://github.com/fabric8io/docker-maven-plugin/issues/290))\n  - Update to maven-assembly-plugin 2.6 which fixes issue with line endings on windows ([#127](https://github.com/fabric8io/docker-maven-plugin/issues/127))\n  - Disabled color output on Windows because ANSI emulation can't be enabled in Maven's sl4j logger which\n    caches system out/err\n  - Moved to [fabric8io](https://github.com/orgs/fabric8io/dashboard) as GitHub organization which implies\n    also changes in the maven coordinates (Maven group-id is now **io.fabric8**)\n  - Fix wait section in samples ([#385](https://github.com/fabric8io/docker-maven-plugin/issues/385))\n  - Add logging configuration to property handler\n  - Add support for a logging driver ([#379](https://github.com/fabric8io/docker-maven-plugin/issues/379))\n\nWith version `0.14.0` this plugin moved to the [fabric8](http://fabric8.io) community in order to provide\neven better services. This include a change in the Maven coordinates. I.e. the Maven group id is now **io.fabric8**\n(formerly: \"org.jolokia\"). Please adapt your pom files accordingly.\n\n* **0.13.9**\n  - Check also registry stored with an `https` prefix ([#367](https://github.com/fabric8io/docker-maven-plugin/issues/367))\n  - Don't stop containers not started by the project during parallel reactor builds ([#372](https://github.com/fabric8io/docker-maven-plugin/issues/372))\n\n* **0.13.8**\n  - Add option `nocache` to build configuration ([#348](https://github.com/fabric8io/docker-maven-plugin/issues/348))\n  - Add system property `docker.nocache` to disable build caching globally ([#349](https://github.com/fabric8io/docker-maven-plugin/issues/349))\n  - Add support for '.maven-dockerignore' for excluding certain files in plain Dockerfile build ([#362](https://github.com/fabric8io/docker-maven-plugin/issues/362))\n  - If naming strategy is \"alias\" stop only the container with the given alias with `docker:stop` ([#359](https://github.com/fabric8io/docker-maven-plugin/issues/359))\n  - Fix that containers without d-m-p label where still stopped\n  - Add support for OpenShift login (use `-DuseOpenShiftAuth` for enabling this) ([#350](https://github.com/fabric8io/docker-maven-plugin/issues/350))\n  - Add support for dedicated pull and push registry configuration respectively ([#351](https://github.com/fabric8io/docker-maven-plugin/issues/351))\n\n* **0.13.7**\n  - Fix default for \"cleanup\" in build configuration to `true` (as documented) ([#338](https://github.com/fabric8io/docker-maven-plugin/issues/338))\n  - Fix dynamic host property update in port mapping ([#323](https://github.com/fabric8io/docker-maven-plugin/issues/323))\n  - New goal 'docker:source' for attaching a Docker tar archive to the Maven project with an classifier \"docker-<alias>\" ([#311](https://github.com/fabric8io/docker-maven-plugin/issues/311))\n  - Be more careful with chowning the user when <user> is used in an assembly ([#336](https://github.com/fabric8io/docker-maven-plugin/issues/336))\n  - Move VOLUME to the end of the Dockerfile to allow initialization via RUN commands ([#341](https://github.com/fabric8io/docker-maven-plugin/issues/341))\n  - Allow multiple configurations with different Docker hosts again ([#320](https://github.com/fabric8io/docker-maven-plugin/issues/320))\n  - `docker:start` blocks now only when system property docker.follow is given ([#249](https://github.com/fabric8io/docker-maven-plugin/issues/249))\n  - `docker:stop` only stops containers started by this plugin by default ([#87](https://github.com/fabric8io/docker-maven-plugin/issues/87))\n  - Lookup `~/.docker/config.json` for registry credentials as fallback ([#147](https://github.com/fabric8io/docker-maven-plugin/issues/147))\n\n* **0.13.6**\n  - Don't use user from image when pulling base images ([#147](https://github.com/fabric8io/docker-maven-plugin/issues/147))\n  - Add a new assembly descriptor reference  `hawt-app` for using assemblies created by\n    [hawt-app](https://github.com/fabric8io/fabric8/tree/master/hawt-app-maven-plugin)\n\n* **0.13.5**\n  - Improvements for `docker:watch` ([#288](https://github.com/fabric8io/docker-maven-plugin/issues/288))\n  - Add parameter `kill` to `<watch>` configuration for waiting before\n    sending SIGKILL when stopping containers ([#293](https://github.com/fabric8io/docker-maven-plugin/issues/293))\n  - Add `file` for `<log>` to store the logout put in a file. Use\n    `docker.logStdout` to show logs nevertheless to stdout ([#287](https://github.com/fabric8io/docker-maven-plugin/issues/287))\n  - Support `watchMode == copy` for copying changed assembly files\n    into a running container ([#268](https://github.com/fabric8io/docker-maven-plugin/issues/268))\n  - Add a `target/classpath` file to the assembly as `classpath` for\n    `artifact-with-dependencies` predefined assembly descriptor ([#283](https://github.com/fabric8io/docker-maven-plugin/issues/283))\n  - Disable Apache HTTP Client retry in WaitUtil ([#297](https://github.com/fabric8io/docker-maven-plugin/issues/297))\n\n* **0.13.4**\n  - Support explicit exec arguments for `start.cmd` and\n    `start.entrypoint`. ([#253](https://github.com/fabric8io/docker-maven-plugin/issues/253))\n  - Fix processing of split chunked JSON responses\n    ([#259](https://github.com/fabric8io/docker-maven-plugin/issues/259))\n  - Fix for default registry handling. Again and\n    again. ([#261](https://github.com/fabric8io/docker-maven-plugin/issues/261))\n  - Allow `runCmds` to be compressed into a single command with the\n    build config option\n    `optimise`. ([#263](https://github.com/fabric8io/docker-maven-plugin/issues/263))\n  - Proper error message when default timeout is hit while waiting\n    ([#274](https://github.com/fabric8io/docker-maven-plugin/issues/274))\n  - Add proper error message when docker host URL is malformed\n    ([#277](https://github.com/fabric8io/docker-maven-plugin/issues/277))\n  - If no wait condition is given in wait continue immediately\n    ([#276](https://github.com/fabric8io/docker-maven-plugin/issues/276))\n  - Add logic to specify exec commands during postStart and preStop\n    ([#272](https://github.com/fabric8io/docker-maven-plugin/issues/272))\n  - Fixed docker:watch bug when watching on plain files\n\n* **0.13.3**\n  - Allow dangling images to be cleaned up after build\n    ([#20](https://github.com/fabric8io/docker-maven-plugin/issues/20))\n  - Adapt order of WORKDIR and RUN when building images\n    ([#222](https://github.com/fabric8io/docker-maven-plugin/issues/222))\n  - Allow 'build' and/or 'run' configuration to be skipped\n    ([#207](https://github.com/fabric8io/docker-maven-plugin/issues/207))\n  - Refactored to use 'inspect' instead of 'list' for checking the\n    existence of an image\n    ([#230](https://github.com/fabric8io/docker-maven-plugin/issues/230))\n  - Refactored ApacheHttpClientDelegate to avoid leaking connections\n    ([#232](https://github.com/fabric8io/docker-maven-plugin/issues/232))\n  - Allow empty `build` or `assembly` elements\n    ([#214](https://github.com/fabric8io/docker-maven-plugin/issues/214))\n    ([#236](https://github.com/fabric8io/docker-maven-plugin/issues/236))\n  - Add new configuration parameter 'maxConnections' to allow to\n    specify the number of parallel connections to the Docker\n    Host. Default: 100\n    ([#254](https://github.com/fabric8io/docker-maven-plugin/issues/254))\n  - Allow multiple containers of the same image to be linked\n    ([#182](https://github.com/fabric8io/docker-maven-plugin/issues/182))\n  - HTTP method and status code can be specified when waiting on an\n    HTTP URL\n    ([#258](https://github.com/fabric8io/docker-maven-plugin/issues/258))\n  - Introduced global `portPropertyFile` setting\n    ([#90](https://github.com/fabric8io/docker-maven-plugin/issues/90))\n  - Allow the container's host ip to be bound to a maven property and\n    exported\n\n* **0.13.2**\n  - \"run\" directives can be added to the Dockerfile\n    ([#191](https://github.com/fabric8io/docker-maven-plugin/issues/191))\n  - Support user information in wait URL\n    ([#211](https://github.com/fabric8io/docker-maven-plugin/issues/211))\n  - Stop started container in case of an error during startup\n    ([#217](https://github.com/fabric8io/docker-maven-plugin/issues/217))\n  - Allow linking to external containers\n    ([#195](https://github.com/fabric8io/docker-maven-plugin/issues/195))\n  - Allow volume mounting from external containers\n    ([#73](https://github.com/fabric8io/docker-maven-plugin/issues/73))\n\n* **0.13.1**\n  - Allow autoPull to be forced on docker:build and docker:start\n    ([#96](https://github.com/fabric8io/docker-maven-plugin/issues/96))\n  - Respect username when looking up credentials for a Docker registry\n    ([#174](https://github.com/fabric8io/docker-maven-plugin/issues/174))\n  - Add \"force=1\" to push for Fedora/CentOs images allowing to push to\n    docker hub\n\nNote that the default registry has been changed to `docker.io` as\ndocker hub doesn't use `registry.hub.docker.com` as the default\nregistry and refused to authenticate against this registry. For\nbackward compatibility reasons `registry.hub.docker.com`,\n`index.docker.io` and `docker.io` can be used as a server id in\n`~/.m2/settings.xml` for the default credentials for pushing without\nregistry to Docker hub.\n\n* **0.13.0**\n  - Add `docker:watch`\n    ([#187](https://github.com/fabric8io/docker-maven-plugin/issues/187))\n  - Allow `extraHosts` IPs to be resolved at runtime\n    ([#196](https://github.com/fabric8io/docker-maven-plugin/issues/196))\n  - Add `workDir` as configuration option to `<build>`\n    ([#204](https://github.com/fabric8io/docker-maven-plugin/issues/204))\n  - Fix problem with log output and wait\n    ([#200](https://github.com/fabric8io/docker-maven-plugin/issues/200))\n  - Don't verify SSL server certificates if `DOCKER_TLS_VERIFY` is not\n    set\n    ([#192](https://github.com/fabric8io/docker-maven-plugin/issues/192))\n  - For bind path on Windows machines\n    ([#188](https://github.com/fabric8io/docker-maven-plugin/issues/188))\n  - No 'from' required when using a Dockerfile\n    ([#201](https://github.com/fabric8io/docker-maven-plugin/issues/201))\n  - Support for LABEL for build and run.\n\nNote that since version 0.13.0 this plugin requires Docker API version v1.17 or later in order to support labels.\n\nThe watch feature has changed: Instead of using paramters like\n`docker.watch` or `docker.watch.interval` for `docker:start` a\ndedicated `docker:watch` has been introduced. Also the\n`<run><watch>...</watch></run>` configuration has been moved one level\nup so that `<watch>` and `<run>` are on the same level. Please refer\nto the [manual](manual.md#watching-for-image-changes) for an in depth\nexplanation of the much enhanced watch functionality.\n\n* **0.12.0**\n  - Allow CMD and ENTRYPOINT with shell and exec arguments\n    ([#130](https://github.com/fabric8io/docker-maven-plugin/issues/130))\n    ([#149](https://github.com/fabric8io/docker-maven-plugin/issues/149))\n  - Unix Socket support\n    ([#179](https://github.com/fabric8io/docker-maven-plugin/issues/179))\n  - Add a new parameter 'skipTags' for avoiding configured tagging of\n    images\n    ([#145](https://github.com/fabric8io/docker-maven-plugin/issues/145))\n  - Break build if log check or URL check runs into a timeout\n    ([#173](https://github.com/fabric8io/docker-maven-plugin/issues/173))\n\nPlease note that for consistencies sake `<command>` has been renamed\nto `<cmd>` which contains inner elements to match better the\nequivalent Dockerfile argument. The update should be trivial and easy\nto spot since a build will croak immediately.\n\nThe old format\n\n````xml\n<build>\n  <command>java -jar /server.jar</command>\n</build>\n````\n\nbecomes now\n\n````xml\n<build>\n  <cmd>\n    <exec>\n      <arg>java</arg>\n      <arg>-jar</arg>\n      <arg>/server.jar</arg>\n    </exec>\n  </cmd>\n</build>\n````\n\nor\n\n````xml\n<build>\n  <cmd>\n    <shell>java -jar /server.jar</shell>\n  </cmd>\n</build>\n````\n\ndepending on whether you prefer the `exec` or `shell` form.\n\n* **0.11.5**\n  - Fix problem with http:// URLs when a CERT path is set\n  - Fix warnings when parsing a pull response\n  - Add a new parameter 'docker.follow' which makes a `docker:start`\n    blocking until the CTRL-C is pressed\n    ([#176](https://github.com/fabric8io/docker-maven-plugin/issues/176))\n  - Add a `user` parameter to the assembly configuration so that the\n    added files are created for this user\n  - Fix problem when creating intermediate archive for collecting\n    assembly files introduced with #139. The container can be now set\n    with \"mode\" in the assembly configuration with the possible values\n    `dir`, `tar`, `tgz` and `zip`\n    ([#171](https://github.com/fabric8io/docker-maven-plugin/issues/171))\n  - Workaround Docker problem when using an implicit registry\n    `index.docker.io` when no registry is explicitly given.\n  - Fixed references to docker hub in documentation\n    ([#169](https://github.com/fabric8io/docker-maven-plugin/issues/169))\n  - Fixed registry authentication lookup\n    ([#146](https://github.com/fabric8io/docker-maven-plugin/issues/146))\n\n* **0.11.4**\n  - Fixed documentation for available properties\n  - Changed property `docker.assembly.exportBase` to\n    `docker.assembly.exportBaseDir`\n    ([#164](https://github.com/fabric8io/docker-maven-plugin/issues/164))\n  - Changed default behaviour of `exportBaseDir` (true if no base\n    image used with `from`, false otherwise)\n  - Fix log messages getting cut off in the build\n    ([#163](https://github.com/fabric8io/docker-maven-plugin/issues/163))\n  - Allow system properties to overwrite dynamic port mapping\n    ([#161](https://github.com/fabric8io/docker-maven-plugin/issues/161))\n  - Fix for empty authentication when pushing to registries\n    ([#102](https://github.com/fabric8io/docker-maven-plugin/issues/102))\n  - Added watch mode for images with `-Ddocker.watch`\n    ([#141](https://github.com/fabric8io/docker-maven-plugin/issues/141))\n  - Added support for inline assemblies (#157, #158)\n  - Add support for variable substitution is environment declarations\n    ([#137](https://github.com/fabric8io/docker-maven-plugin/issues/137))\n  - Use Tar archive as intermediate container when creating image ([#139](https://github.com/fabric8io/docker-maven-plugin/issues/139))\n  - Better error handling for Docker errors wrapped in JSON response\n    only\n    ([#167](https://github.com/fabric8io/docker-maven-plugin/issues/167))\n\n* **0.11.3**\n  - Add support for removeVolumes in `docker:stop` configuration\n    ([#120](https://github.com/fabric8io/docker-maven-plugin/issues/120))\n  - Add support for setting a custom maintainer in images\n    ([#117](https://github.com/fabric8io/docker-maven-plugin/issues/117))\n  - Allow containers to be named using\n    `<namingStrategy>alias</namingStrategy>` when started\n    ([#48](https://github.com/fabric8io/docker-maven-plugin/issues/48))\n  - Add new global property 'docker.verbose' for switching verbose\n    image build output\n    ([#36](https://github.com/fabric8io/docker-maven-plugin/issues/36))\n  - Add support for environment variables specified in a property file\n    ([#128](https://github.com/fabric8io/docker-maven-plugin/issues/128))\n  - Documentation improvements (#107, #121)\n  - Allow to use a dockerFileDir without any assembly\n\n* **0.11.2**\n  - Fix maven parse error when specifying restart policy\n    ([#99](https://github.com/fabric8io/docker-maven-plugin/issues/99))\n  - Allow host names to be used in port bindings\n    ([#101](https://github.com/fabric8io/docker-maven-plugin/issues/101))\n  - Add support for tagging at build and push time\n    ([#104](https://github.com/fabric8io/docker-maven-plugin/issues/104))\n  - Use correct output dir during multi-project builds\n    ([#97](https://github.com/fabric8io/docker-maven-plugin/issues/97))\n  - `descriptor` and `descriptorRef` in the assembly configuration are\n    now optional\n    ([#66](https://github.com/fabric8io/docker-maven-plugin/issues/66))\n  - Fix NPE when filtering enabled during assembly creation\n    ([#82](https://github.com/fabric8io/docker-maven-plugin/issues/82))\n  - Allow `${project.build.finalName}` to be overridden when using a\n    pre-packaged assembly descriptor for artifacts\n    ([#111](https://github.com/fabric8io/docker-maven-plugin/issues/111))\n\n* **0.11.1**\n  - Add support for binding UDP ports\n    ([#83](https://github.com/fabric8io/docker-maven-plugin/issues/83))\n  - \"Entrypoint\" supports now arguments\n    ([#84](https://github.com/fabric8io/docker-maven-plugin/issues/84))\n  - Fix basedir for multi module projects\n    ([#89](https://github.com/fabric8io/docker-maven-plugin/issues/89))\n  - Pull base images before building when \"autoPull\" is switched on\n    (#76, #77, #88)\n  - Fix for stopping containers without tag\n    ([#86](https://github.com/fabric8io/docker-maven-plugin/issues/86))\n\n* **0.11.0**\n  - Add support for binding/exporting containers during startup\n    ([#55](https://github.com/fabric8io/docker-maven-plugin/issues/55))\n  - Provide better control of the build assembly configuration. In\n    addition, the plugin will now search for assembly descriptors in\n    `src/main/docker`. This default can be overridden via the global\n    configuration option `sourceDirectory`.\n  - An external `Dockerfile` can now be specified to build an image.\n  - When \"creating\" containers they get now all host configuration\n    instead of during \"start\". This is the default behaviour since\n    v1.15 while the older variant where the host configuration is fed\n    into the \"start\" call is deprecated and will go away.\n  - Allow selecting the API version with the configuration\n    \"apiVersion\".  Default and minimum API version is now \"v1.15\"\n  - A registry can be specified as system property `docker.registry`\n    or environment variable `DOCKER_REGISTRY`\n    ([#26](https://github.com/fabric8io/docker-maven-plugin/issues/26))\n  - Add new wait parameter `shutdown` which allows to specify the\n    amount of time to wait between stopping a container and removing\n    it ([#54](https://github.com/fabric8io/docker-maven-plugin/issues/54))\n\nPlease note, that the syntax for binding volumes from another\ncontainer has changed slightly in 0.10.6.  See\n\"[Volume binding](manual.md#volume-binding)\" for details but in short:\n\n````xml\n<run>\n  <volumes>\n    <from>data</from>\n    <from>fabric8/demo</from>\n  </volumes>\n....\n</run>\n````\n\nbecomes\n\n````xml\n<run>\n  <volumes>\n    <from>\n      <image>data</image>\n      <image>fabric8/demo</image>\n    </from>\n  </volumes>\n....\n</run>\n````\n\nThe syntax for specifying the build assembly configuration has also\nchanged. See \"[Build Assembly] (manual.md#build-assembly)\" for details\nbut in short:\n\n````xml\n<build>\n  ...\n  <exportDir>/export</exportDir>\n  <assemblyDescriptor>src/main/docker/assembly.xml</assemblyDescriptor>\n</build>\n````\n\nbecomes\n\n````xml\n<build>\n  ...\n  <assembly>\n    <basedir>/export</basedir>\n    <descriptor>assembly.xml</descriptor>\n  </assembly>\n</build>\n````\n\n* **0.10.5**\n  - Add hooks for external configurations\n  - Add property based configuration for images\n    ([#42](https://github.com/fabric8io/docker-maven-plugin/issues/42))\n  - Add new goal `docker:logs` for showing logs of configured\n    containers\n    ([#49](https://github.com/fabric8io/docker-maven-plugin/issues/49))\n  - Support for showing logs during `docker:start`\n    ([#8](https://github.com/fabric8io/docker-maven-plugin/issues/8))\n  - Use `COPY` instead of `ADD` when putting a Maven assembly into the\n    container\n    ([#53](https://github.com/fabric8io/docker-maven-plugin/issues/53))\n  - If `exportDir` is `/` then do not actually export (since it\n    doesn't make much sense) (see #62)\n\n* **0.10.4**\n  - Restructured and updated documentation\n  - Fixed push issue when using a private registry\n    ([#40](https://github.com/fabric8io/docker-maven-plugin/issues/40))\n  - Add support for binding to an arbitrary host IP\n    ([#39](https://github.com/fabric8io/docker-maven-plugin/issues/39))\n\n* **0.10.3**\n  - Added \"remove\" goal for cleaning up images\n  - Allow \"stop\" also as standalone goal for stopping all managed\n    builds\n\n* **0.10.2**\n  - Support for SSL Authentication with Docker 1.3. Plugin will\n    respect `DOCKER_CERT_PATH` with fallback to `~/.docker/`.  The\n    plugin configuration `certPath` can be used, too and has the\n    highest priority.\n  - Getting rid of UniRest, using\n    [Apache HttpComponents](http://hc.apache.org/) exclusively for\n    contacting the Docker host.\n  - Support for linking of containers (see the configuration in the\n    [shootout-docker-maven](https://github.com/fabric8io/shootout-docker-maven/blob/master/pom.xml)\n    POM) Images can be specified in any order, the plugin takes care\n    of the right startup order when running containers.\n  - Support for waiting on a container's log output before continuing\n\n## 0.9.x Series\n\nOriginal configuration syntax (as described in the old\n[README](readme-0.9.x.md))\n\n* **0.9.12**\n  - Fixed push issue when using a private registry\n    ([#40](https://github.com/fabric8io/docker-maven-plugin/issues/40))\n\n* **0.9.11**\n  - Support for SSL Authentication with Docker 1.3. Plugin will\n    respect `DOCKER_CERT_PATH` with fallback to `~/.docker/`.  The\n    plugin configuration `certPath` can be used, too and has the\n    highest priority.\n"
  },
  {
    "path": "doc/ci-docs.sh",
    "content": "echo ===========================================\necho Deploying docker-maven-plugin documentation\necho ===========================================\n\nmvn -Pdoc-html && \\\nmvn -Pdoc-pdf && \\\ngit clone -b gh-pages git@github.com:fabric8io/docker-maven-plugin.git gh-pages && \\\ncp -rv target/generated-docs/* gh-pages/ && \\\ncd gh-pages && \\\nmv index.pdf docker-maven-plugin.pdf && \\\ngit add --ignore-errors * && \\\ngit commit -m \"generated documentation\" && \\\ngit push origin gh-pages && \\\ncd .. && \\\nrm -rf gh-pages target\n"
  },
  {
    "path": "doc/examples.md",
    "content": "## Examples\n\nThis plugin comes with some commented examples in the `samples/` directory:\n\n### Jolokia Demo\n\n[data-jolokia](https://github.com/fabric8io/docker-maven-plugin/tree/master/samples/data-jolokia)\nis a setup for testing the [Jolokia](http://www.jolokia.org) HTTP-JMX\nbridge in a tomcat. It uses a Docker data container which is linked\ninto the Tomcat container and contains the WAR files to deploy. There\nare two flavor of tests\n\n* One with two image where a (almost naked) data container with the\n  war file is created and then mounted into the server image during\n  startup before the integration test.\n\n* When using the profile `-Pmerge` then a single image with Tomcat and\n  the dependent war files is created. During startup of a container\n  from the created image, a deploy script will link over the war files\n  into Tomat so that they are automatically deployed.\n  \nFor running the tests call\n\n```bash\n# Use two (\"data\" and \"server\") connected containers\nmvn clean install\n# Use a single image with tomcat and data:\nmvn -Pmerge clean install\n# Use a property based configuration:\nmvn -Pprops clean install\n```\n\nThe sever used is by default Tomcat 7. This server can easily be\nchanged with the system properties `server.name` and\n`server.version`. The following variants are available:\n\n* For `server.name=tomcat` the `server.version` can be 3.3, 4.0, 5.5, 6.0, 7.0\n  or 8.0\n* For `server.name=jetty` the `server.version` can be 4, 5, 6, 7, 8 or 9\n\nExample:\n\n```bash\nmvn -Dserver.name=jetty -Dserver.version=9 clean install\n```\n\nIn addition to running the integration test with building images, starting containers, \nrunning tests and stopping containers one can also only start the containers:\n\n```bash\nmvn docker:start\n```\n\nIn order to get the dynamically exposed port, use `docker ps`. You can connect to the \nJolokia agent inside the container then with an URL like `http://localhost:http://localhost:49171/jolokia` to \nthe Agent.\n\nFor stopping the server simply call\n\n```bash\nmvn docker:stop\n```\n\n### Cargo Demo\n\n[cargo-jolokia](https://github.com/fabric8io/docker-maven-plugin/tree/master/samples/cargo-jolokia)\nwill use Docker to start a Tomcat 7 server with dynamic port mapping,\nwhich is used for remote deployment via\n[Cargo](https://codehaus-cargo.github.io/cargo/Maven2+plugin.html) and running the\nintegration tests.\n\n### docker-maven-plugin Shootout\n\nIn order to help in the decision, which plugin to use, there is a\nsample project\n[rhuss/shootout-docker-maven](https://github.com/rhuss/shootout-docker-maven),\nwhich has more complex sample project involving two images:\n\n* Vanilla PostgreSQL 9 Image\n* HTTP Request Logging Service\n  - MicroService with embedded Tomcat\n  - DB Schema is created during startup via [Flyway](http://flywaydb.org/)\n* PostgreSQL container is connected via a Docker 'link'\n* Simple integration test which exercises the service\n\nThe different plugins can be enabled with different Maven profiles,\nthe one for this plugin is called `fabric8io` (and the others `wouterd`,\n`alexec` and `spotify`).\n\nFor more information please look over there.\n"
  },
  {
    "path": "doc/howto-release.md",
    "content": "\n# Release instructions\n\n## Preparation\n\n* Increase version numbers in the poms below samples/ (they are not automatically updated)\n\n```\ncd samples\nmvn versions:set -DgenerateBackupPoms=false -DnewVersion=0.15.4\n```\n\n* Run \"update_issue_links.sh\" in \"doc/\"\n* Check into Git, push, create PR and apply\n\n## Building and deploying\n\n* Run the build over the fabric8 CD Pipeline \n\nor in the _classic_ way:\n\n```\nmvn -Dmaven.repo.local=/tmp/clean-repo -DdevelopmentVersion=0.23-SNAPSHOT -DreleaseVersion=0.23.0 -Dtag=v0.23.0 -Prelease release:prepare\nmvn -Dmaven.repo.local=/tmp/clean-repo -DdevelopmentVersion=0.23-SNAPSHOT -DreleaseVersion=0.23.0 -Dtag=v0.23.0 -Prelease release:perform\n```\n\nand then push to Maven central manually via https://oss.sonatype.org/\n\n## Update from upstream\n\n```\ngit co master\ngit pull upstream master\ngit rebase upstream/master\n```\n\n## After the build\n\n* Set sample version back to the snapshot version\n\n```\nmvn versions:set -DgenerateBackupPoms=false -DnewVersion=0.15-SNAPSHOT\n```\n\n* Check-In and create PR\n\n## Update documentation\n\n* Update branch `dmp.fabric8.io` with the exact version of the plugin build and push it to upstream (https://github.com/fabric8io/docker-maven-plugin)\n\n```\ngit fetch -u upstream --tags\ngit co dmp.fabric8.io\ngit merge v0.19.0\ngit push -u upstream dmp.fabric8.io\n```\n\n* The docs will be then build by `circleci.com`\n"
  },
  {
    "path": "doc/integration-tests.md",
    "content": "\n# Integration Testing\n\nThis document currently only holds some ideas of how and what to integration test this plugin\n\n### Registry Handling\n\n* Specify registry via environment variable, global configuration, as image configuration or with name\n* Test that `autoPull` works for `docker:start`\n* Check that push works with the registry. Also check, that no temporary docker images names (which needs to\n  be created before pushing) are left over."
  },
  {
    "path": "doc/intro.md",
    "content": "## Introduction \n\nIt focuses on two major aspects:\n\n* **Building** and **pushing** Docker images which contain build artifacts\n* **Starting** and **stopping** Docker containers for integration\n  testing and development \n\nDocker *images* are the central entity which can be configured. \nContainers, on the other hand, are more or less volatile. They are\ncreated and destroyed on the fly from the configured images and are\ncompletely managed internally.\n\n### Building docker images\n\nOne purpose of this plugin is to create docker images holding the\nactual application. This is done with the `docker:build` goal.  It\nis easy to include build artifacts and their dependencies into an image. \nTherefore, this plugin uses the\n[assembly descriptor format](http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html)\nfrom the\n[maven-assembly-plugin](http://maven.apache.org/plugins/maven-assembly-plugin/)\nto specify the content which will be added from a sub-directory in the image \n(`/maven` by default). Images that are built with this plugin can be pushed \nto public or private Docker registries with `docker:push`.\n\n### Running containers\n\nWith this plugin it is possible to run completely isolated integration\ntests so you don't need to take care of shared resources. Ports can be\nmapped dynamically and made available as Maven properties to your\nintegration test code. \n\nMultiple containers can be managed at once, which can be linked\ntogether or share data via volumes. Containers are created and started\nwith the `docker:start` goal and stopped and destroyed with the\n`docker:stop` goal. For integration tests, both goals are typically\nbound to the `pre-integration-test` and `post-integration-test` phase,\nrespectively. It is recommended to use the \n[`maven-failsafe-plugin`](http://maven.apache.org/surefire/maven-failsafe-plugin/) \nfor integration testing in order to stop the docker container even when\nthe tests fail.\n\nFor proper isolation, container exposed ports can be dynamically and\nflexibly mapped to localhost ports. It is easy to specify a Maven\nproperty which will be filled in with a dynamically assigned port\nafter a container has been started. This can then be used as\na parameter for integration tests to connect to the application.\n\n### Configuration\n\nThe plugin configuration contains a *general part* and a list\nof *image-specific* configurations, one for each image. \n\nThe general part contains global configuration like the Docker URL or\nthe path to the SSL certificates for communication with the Docker Host.\n\nThen, each specific image configuration has three parts:\n\n* A general image part containing the image's name and alias.\n* A `<build>` configuration specifying how images are built.\n* A `<run>` configuration describing how containers should be created and started.\n\nThe `<build>` and `<run>` parts are optional and can be omitted.\n\nLet's look at a plugin configuration example:\n\n````xml\n<configuration>\n  <images>\n    <image>\n      <alias>service</alias>\n      <name>fabric8/docker-demo:${project.version}</name>\n\n      <build>\n         <from>java:8</from>\n         <assembly>\n           <descriptor>docker-assembly.xml</descriptor>\n         </assembly>\n         <ports>\n           <port>8080</port>\n         </ports>\n         <cmd>\n            <shell>java -jar /maven/service.jar</shell>\n         </cmd>\n      </build>\n\n      <run>\n         <ports>\n           <port>tomcat.port:8080</port>\n         </ports>\n         <wait>\n           <http>\n              <url>http://localhost:${tomcat.port}/access</url>\n           </http>\n           <time>10000</time>\n         </wait>\n         <links>\n           <link>database:db</link>\n         </links>\n       </run>\n    </image>\n\n    <image>\n      <alias>database</alias>\n      <name>postgres:9</name>\n      <run>\n        <wait>\n          <log>database system is ready to accept connections</log>\n          <time>20000</time>\n        </wait>\n      </run>\n    </image>\n  </images>\n</configuration>\n````\n\nHere, two images are specified. One is the official PostgreSQL 9 image from\nDocker Hub, which internally is referenced as \"*database*\" (`<alias>`). It\nonly has a `<run>` section which declares that the startup should wait\nuntil the given text pattern is matched in the log output. Next is a\n\"*service*\" image, which is specified in the `<build>` section. It\ncreates an image which has artifacts and dependencies in the\n`/maven` directory (and which are specified with an assembly\ndescriptor). Additionally it specifies the startup command for the\ncontainer, which in this example fires up a microservice from a jar\nfile copied over via the assembly descriptor. It also exposes\nport 8080. In the `<run>` section this port is dynamically mapped to a\ndynamically chosen port, and then assigned to the\nMaven property `${tomcat.port}`. This property could be used, for example,\nby an integration test to access this microservice. An important part is\nthe `<links>` section which indicates that the image with the alias of\n\"*database*\" is linked into the \"*service*\" container, which can access\nthe internal ports in the usual Docker way (via environment variables\nprefixed with `DB_`).\n\nImages can be specified in any order and the plugin will take care of the\nproper startup order (and will bail out if it detects circular\ndependencies). \n\n### Other highlights\n\nSome other highlights in random order (and not complete):\n\n* Auto pulling of images (with a progress indicator)\n* Waiting for a container to startup based on time, the reachability\n  of an URL, or a pattern in the log output\n* Support for SSL authentication (since Docker 1.3)\n* Specification of encrypted registry passwords for push and pull in\n  `~/.m2/settings.xml` (i.e., outside the `pom.xml`)\n* Color output ;-)\n\n### Why another Maven Plugin ?\n\nIf you search on GitHub you will find a whole cosmos of Maven Docker\nplugins (As of November 2014: 12 (!) plugins which 4 actively maintained).\nOn the one hand, variety is a good thing, but on the other hand for\nusers it is hard to decide which one to choose. So, you might wonder\nwhy you should choose this one.\n\nThere s a dedicated [shootout project](https://github.com/fabric8io/shootout-docker-maven)\nwhich compares the four most active plugins. It contains a simple demo\nproject with a database and a microservice image, along with an integration\ntest. Each plugin is configured to create images and run the\nintegration test (if possible). Although it might be a bit biased, it can \nbe useful for figuring out which plugin suits you best.\n\nThe high-level design goals and initial motivation for this plugin are:\n\n* A flexible, **dynamic port mapping** from container to host\n  ports so that truly isolated builds could be made. This should\n  work on indirect setups with VMs like\n  [boot2docker](https://github.com/boot2docker/boot2docker) or\n  [docker-machine](https://docs.docker.com/machine/) for\n  running on OS X/Windows.\n\n* It should be possible to **pull images** on the fly to get\n  self-contained and repeatable builds with the only requirement to\n  have Docker installed.\n\n* The configuration of the plugin should be **simple**, since developers\n  don't want to be forced to dive into specific Docker details only to\n  start a container. So, only a handful options should be exposed,\n  which needs not necessarily map directly to docker config setup.\n\n* There should be as **few dependencies** as possible for this plugin. So it\n  does *not* use the Java Docker API\n  [docker-java](https://github.com/docker-java/docker-java) which is\n  external to docker and has a different lifecycle than Docker's\n  [remote API](http://docs.docker.io/en/latest/reference/api/docker_remote_api/).\n  Since this plugin needs only a small subset of the whole API,\n  it is OK to do the REST calls directly. That way the plugin has\n  to deal only with Docker peculiarities and not docker-java's as well.\n  As a side-effect, it has fewer transitive dependencies.\n  FYI: There are other Docker Java/Groovy client libraries out, which\n  might be suitable for plugins like this:\n  [fabric/fabric-docker-api](https://github.com/fabric8io/fabric8/tree/master/fabric/fabric-docker-api),\n  [spotify/docker-client](https://github.com/spotify/docker-client)\n  or\n  [gesellix-docker/docker-client](https://github.com/gesellix-docker/docker-client).\n  Can you see the pattern ;-) ?\n  \nSo, final words: Enjoy this plugin, and please use the\n[issue tracker](https://github.com/fabric8io/docker-maven-plugin/issues)\nfor anything that hurts, or when you have a wish list. \n\n"
  },
  {
    "path": "doc/migration-0.9.x.md",
    "content": "## Migration Guide\n\nThis recipes gives you some hint and help for migrating from the old\n(0.9.x) configuration syntax to the new one (0.10.x and later).\n\nIf there are any issue when doing the migration or you do need some\nhelp, please raise an\n[issue](https://github.com/fabric8io/docker-maven-plugin/issues) with your\noriginal configuration and you will get some help for the migration.\n\nThe biggest change was support for multiple images. The whole story\nabout the change can be found in this\n[blog post](https://ro14nd.de/Docker-Maven-Plugin-Rewrite/). In the\nold version there was a single configurtion which resulted in one or\ntwo images, depending on the `mergeData` property.\n\nIn general now all build aspects are collected now below a `<build>`\nsection and all runtime aspects in a `<run>` section for each image. \n\nTypically it should be clear what configuration from the original,\nflat configuration list is a runtime or build aspect, here are some\nhints, depending on whether data merging is on or off (property\n`mergeData`) \n\n* `image` becomes the name of an image with a `<run>` configuration\n  (non-merging) or the base image within a `<from>`\n  element in the `<build>` section (merging). \n* `dataImage` is used for the name of the data image (non-merging)\n  with only a `<build>` section or the name of the single image with\n  both `<run>` and `<build>` section when merging is used.\n\nThe [Examples](#examples) below show sample migrations for these two\ndifferent situations and make this transformation clearer.\n\n### Wait configuration\n\nWhen waiting for certain conditions during startup, in the old format\nthere where dedicated configuration params for each possible\nconditions. Now they are collected within a subelement `<wait>` which\nis part of a `<run>` configuration.\n\nFor example:\n\n```xml\n<waitHttp>http://localhost:${port}/jolokia</waitHttp>\n<wait>10000</wait>\n```\n\nbecomes \n\n```\n<wait>\n  <http>\n    <url>http://localhost:${port}/jolokia</url>\n  </http>\n  <time>10000</time>\n</wait>\n```\n\nIn addition the new syntax support also waiting on a log outpbut\n(`<log>`). \n\n### Lifecycle binding\n\nThe old version of the plugin combined some task: E.g. if you used\n`docker:start` or `docker:pull` a `docker:build` was done\nimplicitely. In order to make stuff more explicite and easier to\nunderstand this is not the case anymore. So, when you bind to a\nlifecycle phase you have to add all steps explicitely:\n\n```xml\n<executions>\n  <execution>\n    <id>start</id>\n    <phase>pre-integration-test</phase>\n    <goals>\n      <goal>build</goal>\n      <goal>start</goal>\n    </goals>\n  </execution>\n  ...\n</executions>\n``` \n\n### Examples\n\nThe migration is different depending on whether you use `mergeData`\nor not. \n\n## Non merged images\n\nWhen `mergeData` was false, two images where created: One holding the\ndata and one for the server which is connected to the data container\nduring startup. So, the following old configuration \n\n```xml\n<configuration>\n  <mergeData>false</mergeData>\n  <image>consol/tomcat-7.0</image>\n  <dataImage>jolokia/data</dataImage>\n  <assemblyDescriptor>src/main/docker-assembly.xml</assemblyDescriptor>\n  <env>\n    <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n  </env>\n  <ports>\n    <port>jolokia.port:8080</port>\n  </ports>\n  <waitHttp>http://localhost:${jolokia.port}/jolokia</waitHttp>\n  <wait>10000</wait>\n</configuration>\n```\nbecomes in the new syntax a configuration for two images\n\n```xml\n<configuration>\n  <images>\n    <image>\n      <alias>server</alias>\n      <name>consol/tomcat-7.0</name>\n      <run>\n        <volumes>\n          <from>data</from>\n        </volumes>\n        <env>\n          <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n        </env>\n        <ports>\n          <port>jolokia.port:8080</port>\n        </ports>\n        <wait>\n          <http>\n            <url>http://localhost:${jolokia.port}/jolokia</url>\n          </http>\n          <time>10000</time>\n        </wait>\n      </run>\n    </image>\n    <image>\n      <alias>data</alias>\n      <name>jolokia/data</name>\n      <build>\n        <assemblyDescriptor>src/main/docker-assembly.xml</assemblyDescriptor>\n      </build>\n    </image>\n  </images>\n</configuration>\n```\n\nPlease note, that one image only has a `<run>` configuration, the\ndata image has a `<build>` section only. They both are linked together\nduring startup of the `server` container via `<volumes>` (where the\nsymbolic name of the data container can be used). \n\n## Merged images\n\nWhen  `mergeData` was true, only a single image was created. So\nthe original configuration which looks like\n\n```xml\n<configuration>\n  <mergeData>true</mergeData>\n  <image>consol/tomcat-7.0</image>\n  <dataImage>jolokia/data</dataImage>\n  <assemblyDescriptor>src/main/docker-assembly.xml</assemblyDescriptor>\n  <env>\n    <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n  </env>\n  <ports>\n    <port>jolokia.port:8080</port>\n  </ports>\n  <waitHttp>http://localhost:${jolokia.port}/jolokia</waitHttp>\n  <wait>10000</wait>\n</configuration>\n```\n\ncan be directly translated to a single image configuration\n\n```xml\n<configuration>\n  <images>\n    <image>\n      <name>jolokia/data</name>\n      <build>\n        <from>consol/tomcat-7.0</from>\n        <assemblyDescriptor>src/main/docker-assembly.xml</assemblyDescriptor>\n      </build>\n      <run>\n        <env>\n          <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n        </env>\n        <ports>\n          <port>jolokia.port:8080</port>\n        </ports>\n        <wait>\n          <http>\n            <url>http://localhost:${jolokia.port}/jolokia</url>\n          </http>\n          <time>10000</time>\n        </wait>\n      </run>\n    </image>\n  </images>\n</configuration>\n```\n\n### Misc renamings\n\nSome properties where renamed for consistencies sake:\n\n* `url` to `dockerHost` and the corresponding system property\n  `docker.url` to `docker.host`\n* `color` to `useColor` \n"
  },
  {
    "path": "doc/readme-0.9.x.md",
    "content": "# docker-maven-plugin 0.9.x\n\n[![endorse](http://api.coderwall.com/fabric8io/endorsecount.png)](http://coderwall.com/fabric8io)\n[![Build Status](https://secure.travis-ci.org/fabric8io/docker-maven-plugin.png)](http://travis-ci.org/fabric8io/docker-maven-plugin)\n[![Flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/73919/Jolokia-JMX-on-Capsaicin)\n\nThis is a Maven plugin for managing Docker images and containers from within Maven builds. \n\n> **This document describes version 0.9.x of this plugin. Starting\n> with version 0.10.1 a new configuration syntax was introduced which\n> as documented in the [README](../README.md). See the\n> [CHANGELOG](changelog.md) for more details about the differences and\n> this [blog post](http://ro14nd.de/Docker-Maven-Plugin-Rewrite/) for\n> the motivation behind this restructuring. The 0.9.x version is\n> deprecated and won't be updated in the futuer. For a migration to\n> the new syntax please refer to this [guide](migration-0.9.x.md)**\n\nWith this plugin it is possible to run completely isolated integration tests so you don't need to take care of shared resources. Ports can be mapped dynamically and made available as Maven properties. \n\nBuild artifacts and dependencies can be accessed from within \nrunning containers, so that a file based deployment is easily possible and there is no need to use dedicated deployment support from plugins like [Cargo](https://codehaus-cargo.github.io/cargo/Maven2+plugin.html).\n \nThis plugin's **highlights** are:\n\n* Configurable port mapping\n* Assigning dynamically selected host ports to Maven variables\n* Pulling of images (with progress indicator) if not yet downloaded\n* Optional waiting on a successful HTTP ping to the container\n* On-the-fly creation of Docker data images and containers with Maven artifacts and dependencies linked or merged into the containers under test.\n* Pushing data images to a registry\n* Setting of environment variables when creating the container\n* Support for SSL authentication (since Docker 1.3)\n* Color output ;-)\n\nThis plugin is available from Maven central and can be connected to pre- and post-integration phase as seen below.\nPlease refer also to the examples provided in the `samples/` directory.\n\n````xml\n<plugin>\n  <groupId>org.jolokia</groupId>\n  <artifactId>docker-maven-plugin</artifactId>\n  <version>0.9.11</version>\n\n  <configuration>\n     <!-- For possible options, see below -->\n  </configuration>\n\n  <!-- Connect start/stop to pre- and\n       post-integration-test phase, respectively -->\n  <executions>\n    <execution>\n       <id>start</id>\n       <phase>pre-integration-test</phase>\n       <goals>\n         <goal>start</goal>\n       </goals>\n    </execution>\n    <execution>\n       <id>stop</id>\n       <phase>post-integration-test</phase>\n       <goals>\n         <goal>stop</goal>\n      </goals>\n    </execution>\n  </executions>\n</plugin>\n````\n\n## Maven Goals\n\n### `docker:start`\n\nCreates and starts a specified docker container with the additional possibility to link artifacts and dependencies to this \n  container, or, if `mergeData` is set to `true`, create a new image based on the given image and the assembly artifacts specified. \n\n#### Configuration\n\n| Parameter    | Descriptions                                            | Property       | Default                 |\n| ------------ | ------------------------------------------------------- | -------------- | ----------------------- |\n| **url**      | URL to the docker daemon                                | `docker.url`   | `http://localhost:2375` |\n| **image**    | Name of the docker image (e.g. `jolokia/tomcat:7.0.52`) | `docker.image` | none, required          |\n| **ports**    | List of ports to be mapped statically or dynamically.   |                |                         |\n| **env**      | Additional environment variables used when creating a container |        |                         | \n| **autoPull** | Set to `true` if an yet unloaded image should be automatically pulled | `docker.autoPull` | `true`      |\n| **command**  | Command to execute in the docker container              |`docker.command`|                         |\n| **assemblyDescriptor**  | Path to the data container assembly descriptor. See below for an explanation and example.              |                |                         |\n| **assemblyDescriptorRef** | Predefined assemblies which can be directly used. For possible values, see below. | | |\n| **mergeData** | If set to `true` create a new image based on the configured image and containing the assembly as described with `assemblyDescriptor` or `assemblyDescriptorRef` | `docker.mergeData` | `false` |\n| **dataBaseImage** | Base for the data image (used only when `mergeData` is false) | `docker.baseImage` | `busybox:latest` |\n| **dataImage** | Name to use for the created data image | `docker.dataImage` | `<group>/<artefact>:<version>` |\n| **dataExportDir** | Name of the volume which gets exported | `docker.dataExportDir` | `/maven` |\n| **authConfig** | Authentication configuration when autopulling images. See below for details. | | |\n| **portPropertyFile** | Path to a file where dynamically mapped ports are written to |   |                         |\n| **wait**     | Ramp up time in milliseconds                            | `docker.wait`  |                         |\n| **waitHttp** | Wait until this URL is reachable with an HTTP HEAD request. Dynamic port variables can be given, too | `docker.waitHttp` | |\n| **color**    | Set to `true` for colored output                        | `docker.color` | `true` if TTY connected  |\n| **skip**     | If set to `true` skip the execution of this goal        | `docker.skip`  |                          |\n\n### `docker:stop`\n\nStops and removes a docker container. \n\n#### Configuration\n\n| Parameter  | Descriptions                     | Property       | Default                 |\n| ---------- | -------------------------------- | -------------- | ----------------------- |\n| **url**    | URL to the docker daemon         | `docker.url`   | `http://localhost:4243` |\n| **image** | Which image to stop. All containers for this named image are stopped | `docker.image` |  |\n| **keepContainer** | Set to `true` for not automatically removing the container after stopping it. | `docker.keepContainer` | |\n| **keepRunning** | Set to `true` for not stopping the container even when this goals runs. | `docker.keepRunning` | `false` |\n| **keepData**  | Keep the data container and image after the build if set to `true` | `docker.keepData` |  `false`                       |\n| **color**  | Set to `true` for colored output | `docker.color` | `true` if TTY connected |\n| **skip**     | If set to `true` skip the execution of this goal        | `docker.skip`  |                          |\n\n### `docker:push`\n\nPush a data image to the registry. The data image is the same created during the `start` goal. See below for more information about how the data image is created. The registry to push is by \ndefault `registry.hub.docker.io` but can be specified as part of the `dataImage` name the Docker way. E.g. `docker.test.org:5000/data:1.5` will push the repository `data` with tag `1.5` to \nthe registry `docker.test.org` at port `5000`. Security information (i.e. user and password) can be specified in multiple ways as described in an extra section. \n\n#### Configuration\n\n| Parameter    | Descriptions                                            | Property       | Default                 |\n| ------------ | ------------------------------------------------------- | -------------- | ----------------------- |\n| **url**      | URL to the docker daemon                                | `docker.url`   | `http://localhost:2375` |\n| **image**    | Name of the docker base image (e.g. `consol/tomcat:7.0.52`) | `docker.image` | none         |\n| **autoPull** | Set to `true` if an yet unloaded image should be automatically pulled | `docker.autoPull` | `true`      |\n| **assemblyDescriptor**  | Path to the data container assembly descriptor. See below for an explanation and example               |                |                         |\n| **assemblyDescriptorRef** | Predefined assemblies which can be directly used. Possible values are given below | | |\n| **mergeData** | If set to `true` create a new image based on the configured image and containing the assembly as described with `assemblyDescriptor` or `assemblyDescriptorRef` | `docker.mergeData` | `false` |\n| **dataBaseImage** | Base for the data image (used only when `mergeData` is false) | `docker.baseImage` | `busybox:latest` |\n| **dataImage** | Name to use for the created data image | `docker.dataImage` | `<group>/<artefact>:<version>` |\n| **dataExportDir** | Name of the volume which gets exported | `docker.dataExportDir` | `/maven` |\n| **keepData**  | Keep the data image after the build if set to `true` | `docker.keepData` |  `true`                       |\n| **authConfig** | Authentication configuration when pushing images. See below for details. | | |\n| **color**    | Set to `true` for colored output                        | `docker.color` | `true` if TTY connected  |\n| **skip**     | If set to `true` skip the execution of this goal        | `docker.skip`  |                          |\n\n### `docker:build`\n\nBuild a data image without pushing. It works essentially the same as `docker:push` but does not push to a registry\nand does not delete the image afterwards. \n\n#### Configuration\n\n| Parameter    | Descriptions                                            | Property       | Default                 |\n| ------------ | ------------------------------------------------------- | -------------- | ----------------------- |\n| **url**      | URL to the docker daemon                                | `docker.url`   | `http://localhost:2375` |\n| **image**    | Name of the docker base image (e.g. `consol/tomcat:7.0.52`) | `docker.image` | none         |\n| **autoPull** | Set to `true` if an yet unloaded base image should be automatically pulled | `docker.autoPull` | `true`      |\n| **assemblyDescriptor**  | Path to the data container assembly descriptor. See below for an explanation and example               |                |                         |\n| **assemblyDescriptorRef** | Predefined assemblies which can be directly used. Possible values are given below | | |\n| **mergeData** | If set to `true` create a new image based on the configured image and containing the assembly as described with `assemblyDescriptor` or `assemblyDescriptorRef` | `docker.mergeData` | `false` |\n| **dataBaseImage** | Base for the data image (used only when `mergeData` is false) | `docker.baseImage` | `busybox:latest` |\n| **dataImage** | Name to use for the created data image | `docker.dataImage` | `<group>/<artefact>:<version>` |\n| **dataExportDir** | Name of the volume which gets exported | `docker.dataExportDir` | `/maven` |\n| **ports**    | List of ports to be exposed                             |                |  | \n| **env**      | List of environment variables to use for building       |                |  | \n| **color**    | Set to `true` for colored output                        | `docker.color` | `true` if TTY connected  |\n| **skip**     | If set to `true` skip the execution of this goal        | `docker.skip`  |                          |\n\n## Dynamic Port mapping\n\nFor the `start` goal, container port mapping may be configured using a `ports` declaration.\n\n```xml\n<ports>\n  <port>18080:8080</port>\n  <port>host.port:80</port>\n<ports>\n```\n\nA `port` stanza may take one of two forms:\n* A tuple consisting of two numeric values separated by a `:`. This form will result in an explicit mapping between the docker host and the corresponding port inside the container. In the above example, port 18080 would be exposed on the docker host and mapped to port 8080 in the running container.\n* A tuple consisting of a string and a numeric value separated by a `:`. In this form, the string portion of the tuple will correspond to a Maven property. If the property is undefined when the `start` task executes, a port will be dynamically selected by Docker in the range 49000 ... 49900 and assigned to the property which may then be used later in the same POM file. If the property exists and has a numeric value, that value will be used as the exposed port on the docker host as in the previous form. In the above example, the docker service will elect a new port and assign the value to the property `host.port` which may then later be used in a property expression similar to `<value>${host.port}</value>`. This can be used to pin a port from the outside when doing some initial testing similar to:\n\n    mvn -Dhost.port=10080 docker:start\n\nAnother useful configuration option is `portPropertyFile` with which a file can be specified to which the real port\nmapping is written after all dynamic ports has been resolved. The keys of this property file are the variable names,\nthe values are the dynamically assigned host ports. This property file might be useful together with other Maven\nplugins which already resolved their Maven variables earlier in the lifecycle than this plugin so that the port variables\nmight not be available to them.\n\n## Setting environment variables\n\nWhen creating a container one or more environment variables can be set via configuration with the `env` parameter\n \n```xml\n<env>\n  <JAVA_HOME>/opt/jdk8</JAVA_HOME>\n  <CATALINA_OPTS>-Djava.security.egd=file:/dev/./urandom</CATALINA_OPTS>\n</env>\n```\n\nIf you put this configuration into profiles you can easily create various test variants with a single image (e.g. by \nswitching the JDK or whatever).\n\n## Getting your assembly into the container\n\nWith using the `assemblyDescriptor` or `assemblyDescriptorRef` option it is possible to bring local files, artifacts and dependencies into the running Docker container. This works as follows:\n\n* `assemblyDescriptor` points to a file describing the data to assemble. It has the same format as for creating assemblies with the [maven-assembly-plugin](http://maven.apache.org/plugins/maven-assembly-plugin/) , with some restrictions (see below).\n* Alternatively `assemblyDescriptorRef` can be used with the name of a predefined assembly descriptor. See below for possible values.\n* This plugin will create the assembly and create a Docker image on the fly which exports the assembly below a directory `/maven`. Typically this will be an extra image, but if the configuration parameter `mergeData` is set then the image which was configured for the `start` goal is used as a base image so that the data and e.g. application server are contained in the same image. This is useful for distributing a complete image where artifacts and the server are baked together.\n* From this image a (data) container is created and the 'real' container is started with a `volumesFrom` option pointing to this data container (if `mergeData` is not used).\n* That way, the container started has access to all the data created from the directory `/maven/` within the container.\n* The container command can check for the existence of this directory and deploy everything within this directory.\n\nLet's have a look at an example. In this case, we are deploying a war-dependency into a Tomcat container. The assembly descriptor `src/main/docker-assembly.xml` option may look like\n\n````xml\n<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 \n                        http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <dependencySets>\n    <dependencySet>\n      <includes>\n        <include>org.jolokia:jolokia-war</include>\n      </includes>\n      <outputDirectory>.</outputDirectory>\n      <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n    </dependencySet>\n</assembly>\n````\n\nThen you will end up with a data container which contains with a file `/maven/jolokia.war` which is mirrored into the main container.\n\nThe plugin configuration could look like\n\n````xml\n<plugin>\n    <groupId>org.jolokia</groupId>\n    <artifactId>docker-maven-plugin</artifactId>\n    ....\n    <configuration>\n      <image>jolokia/tomcat-7.0</image>\n      <assemblyDescriptor>src/main/docker-assembly.xml</assemblyDescriptor>\n      ...\n    </configuration>\n</plugin>\n````\n\nThe image `jolokia/tomcat-7.0` is a [trusted build](https://github.com/fabric8io/jolokia-it/tree/master/docker/tomcat/7.0) available from the central docker registry which uses a command `deploy-and-run.sh` that looks like this:\n\n````bash\n#!/bin/sh\n\nDIR=${DEPLOY_DIR:-/maven}\necho \"Checking *.war in $DIR\"\nif [ -d $DIR ]; then\n  for i in $DIR/*.war; do\n     file=$(basename $i)\n     echo \"Linking $i --> /opt/tomcat/webapps/$file\"\n     ln -s $i /opt/tomcat/webapps/$file\n  done\nfi\n/opt/tomcat/bin/catalina.sh run\n````\n\nBefore starting tomcat, this script will link every .war file it finds in `/maven` to `/opt/tomcat/webapps` which effectively will deploy them. \n\nAlternatively, the parameter `mergeData` could have been set to `true` in the plugin configuration. In this case no separate data image is created but an image which is based on the specified image (`jolokia/tomcat-7.0` in this example) and the assembly are directly available from `/maven`. This has the advantage that only a single image needs to be pushed containing  both, the created artifact and application server.\n\nIt is really that easy to deploy your artifacts. And it's fast (less than 10s for starting, deploying, testing (1 test) and stopping the container on my 4years old MBP using boot2docker).\n\n### Assembly Descriptor\n\nThe assembly descriptor has the same [format](http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html) as the the maven-assembly-plugin with the following exceptions:\n\n* `<formats>` are ignored, the assembly will always use a directory when preparing the data container (i.e. the format is fixed to `dir`)\n* The `<id>` is ignored since only a single assembly descriptor is used (no need to distinguish multiple descriptors)\n\nThis `docker-maven-plugin` comes with some predefined assembly descriptors which can be used with `assemblyDescritproRef`:\n\n* **artifact-with-dependencies** will copy your project's artifact and all its dependencies\n* **artifact** will copy only the project's artifact but no dependencies.\n* **project** will copy over the whole Maven project but with out `target/` directory.\n* **rootWar** will copy the artifact as `ROOT.war` to the exposed directory. I.e. Tomcat will then deploy the war under the root context.\n\n## Cleanup\n\nVarious configuration parameters of this plugin are available for cleaning up after a build:\n\n* `keepRunning` specifies that the container should not be stopped after the build. Obviously, the container and any data image created will be left alone as well. This option is especially useful when given as command line option `-Ddocker.keepRunning` for doing some debugging or developing integration tests.\n\n* `keepContainer` tells the plugin to not remove the container created from the image after the build (the container is stopped, though). If a merged container was created via the option `mergeData` then this container will remain as well as the on-the-fly created image this container belongs to. This is useful for post-mortem analysis of the container by e.g. looking at the logs. This option can be switched on with `-Ddocker.keepContainer`. If a separate data container is used, this data container and its image will stay as well.\n\n* `keepData` finally can be used to keep only the data container, but the other container should be be removed. This option has only an effect if `keepContainer` is `false`. That way, the created artifacts can be kept even after the build.\n\n## Authentication\n\nWhen pulling (via the `autoPull` mode of `docker:start` and `docker:push`) or pushing image, it might be necessary to authenticate against a Docker registry.  \n\nThere are three different ways for providing credentials:\n\n* Using a `<authConfig>` section in the plugin configuration with `<username>` and `<password>` elements.\n* Providing system properties `docker.username` and `docker.password` from the outside\n* Using a `<server>` configuration in the the `~/.m2/settings.xml` settings\n\nUsing the username and password directly in the `pom.xml` is not recommended since this is widely visible. This is easiest and transparent way, though. Using an `<authConfig>` is straight forward:\n\n````xml\n<plugin>\n  <configuration>\n     <image>consol/tomcat-7.0</image>\n     ...\n     <authConfig>\n         <username>jolokia</username>\n         <password>s!cr!t</password>\n     </authConfig>\n  </configuration>\n</plugin>\n````\n\nThe system property provided credentials are a good compromise when using CI servers like Jenkins. You simply provide the credentials from the outside:\n\n\tmvn -Ddocker.username=jolokia -Ddocker.password=s!cr!t docker:push\n\nThe most secure and also the most *mavenish* way is to add a server to the Maven settings file `~/.m2/settings.xml`: \n\n````xml\n<servers>\n  <server>\n    <id>registry.hub.docker.io</id>\n    <username>jolokia</username>\n    <password>s!cr!t</password>\n  </server>\n  ....\n</servers>\n````\n\nThe server id must specify the registry to push to/pull from, which by default is central index `registry.hub.docker.io`. Here you should add you docker.io account for your repositories.\n\n### Password encryption\n\nRegardless which mode you choose you can encrypt password as described in the [Maven documentation](http://maven.apache.org/guides/mini/guide-encryption.html). Assuming that you have setup a *master password* in `~/.m2/security-settings.xml` you can create easily encrypted passwords:\n\n````bash\n\t$ mvn --encrypt-password\n\tPassword:\n\t{QJ6wvuEfacMHklqsmrtrn1/ClOLqLm8hB7yUL23KOKo=}\n````\n\nThis password then can be used in `authConfig`, `docker.password` and/or the `<server>` setting configuration. However, putting an encrypted password into `authConfig` in the `pom.xml` doesn't make much sense, since this password is encrypted with an individual master password.\n\n## SSL with keys and certificates\n\nThe plugin can communicate with the Docker Host via SSL, too. This is the default now for Docker 1.3 (and Boot2Docker). \nSSL is switched on if the port used is `2376` which is the default, IANA registered SSL port of the Docker host \n(and plain HTTP for `2375`). The directory holding `ca.pem`, `key.pem` and `cert.pem` can be configured with the\nconfiguration parameter `certPath`. Alternatively, the environment variable `DOCKER_CERT_PATH` is evaluated and finally \n`~/.docker` is used as the last fallback.\n\n## Examples\n\nThis plugin comes with some commented examples in the `samples/` directory:\n\n* [data-jolokia-demo](https://github.com/fabric8io/docker-maven-plugin/tree/master/samples/data-jolokia-demo) is a setup for testing the [Jolokia](http://www.jolokia.org) HTTP-JMX bridge in a tomcat. It uses a Docker data container which is linked into the Tomcat container and contains the WAR files to deply\n* [cargo-jolokia-demo](https://github.com/fabric8io/docker-maven-plugin/tree/master/samples/cargo-jolokia-demo) is the same as above except that Jolokia gets deployed via [Cargo](https://codehaus-cargo.github.io/cargo/Maven2+plugin.html)\n\nFor a complete example please refer to `samples/data-jolokia-demo/pom.xml`.\n\nIn order to prove, that self contained builds are not a fiction, you might convince yourself by trying out this (on a UN*X like system):\n\n````bash\n# Move away your local maven repository for a moment\ncd ~/.m2/\nmv repository repository.bak\n\n# Fetch docker-maven-plugin\ncd /tmp/\ngit clone https://github.com/fabric8io/docker-maven-plugin.git\ncd docker-maven-plugin/\n\n# Install plugin\n# (This is only needed until the plugin makes it to maven central)\nmvn install\n\n# Goto the sample\ncd samples/data-jolokia-demo\n\n# Run the integration test\nmvn verify\n\n# Use a 'merged' data image\nmvn -Pmerge-data verify\n\n# Push the data image\nmvn docker:push\n \n# Please note, that first it will take some time to fetch the image\n# from docker.io. The next time running it will be much faster. \n\n# Restore back you .m2 repo\ncd ~/.m2\nmv repository /tmp/\nmv repository.bak repository\n```` \n\n## Misc\n\n* [Script](https://gist.github.com/deinspanjer/9215467) for setting up NAT forwarding rules when using [boot2docker](https://github.com/boot2docker/boot2docker)\non OS X\n\n* It is recommended to use the `maven-failsafe-plugin` for integration testing in order to\nstop the docker container even when the tests are failing.\n\n## Why another docker-maven-plugin ? \n\nSpring feelings in 2014 seems to be quite fertile for the Java crowd's\nDocker awareness\n;-). [Not only I](https://github.com/bibryam/docker-maven-plugin/issues/1)\ncounted ~~5~~ 10 [maven-docker-plugins](https://github.com/search?q=docker-maven-plugin)\non GitHub as of ~~April~~ July 2014, tendency increasing. It seems, that all\nof them have a slightly different focus, but all of them can do the\nmost important tasks: Starting and stopping containers. \n\nSo you might wonder, why I started this plugin if there were already\nquite some out here ?\n\nThe reason is quite simple: I didn't knew them when I started and if\nyou look at the commit history you will see that they all started\ntheir life roughly at the same time (March 2014).\n\nI expect there will be some settling soon and even some merging of\nefforts which I would highly appreciate and support.\n\nFor what it's worth, here are some of my motivations for this plugin\nand what I want to achieve:\n\n* I needed a flexible, **dynamic port mapping** from container to host\n  ports so that truly isolated build can be achieved. This should\n  work on indirect setups with VMs like\n  [boot2docker](https://github.com/boot2docker/boot2docker) for\n  running on OS X.\n  \n* It should be possible to **pull images** on the fly to get\n  self-contained and repeatable builds with the only requirement to\n  have docker installed. \n  \n* The configuration of the plugin should be **simple** since usually\n  developers don't want to dive into specific Docker details only to\n  start a container. So, only a handful options should be exposed\n  which needs not necessarily map directly to docker config setup.\n  \n* The plugin should play nicely with\n  [Cargo](https://codehaus-cargo.github.io/cargo/Maven2+plugin.html) so that deployments into\n  containers can be easy. \n  \n* I want as **less dependencies** as possible for this plugin. So I\n  decided to *not* use the\n  Java Docker API [docker-java](https://github.com/docker-java/docker-java) which is\n  external to docker and has a different lifecycle than Docker's\n  [remote API](http://docs.docker.io/en/latest/reference/api/docker_remote_api/). \n  That is probably the biggest difference to the other\n  docker-maven-plugins since AFAIK they all rely on this API. Since\n  for this plugin I really need only a small subset of the whole API,\n  I think it is ok to do the REST calls directly. That way I only have\n  to deal with Docker peculiarities and not also with docker-java's\n  one. As a side effect this plugin has less transitive dependencies.\n  FYI: There is now yet another Docker Java client library out, which\n  might be used for plugins like this, too:\n  [fabric-docker-api](https://github.com/fabric8io/fabric8/tree/master/fabric/fabric-docker-api). (Just\n  in case somebody wants to write yet another plugin ;-)\n\nIn the meantime, enjoy this plugin, and please use the\n[issue tracker](https://github.com/fabric8io/docker-maven-plugin/issues) \nfor anything what hurts.\n\n"
  },
  {
    "path": "doc/update_issue_links.sh",
    "content": "#!/bin/sh\nperl -i -p -e 's|\\(\\s*#(\\d+)\\s*\\)|([#$1](https://github.com/fabric8io/docker-maven-plugin/issues/$1))|g' changelog.md\n"
  },
  {
    "path": "it/README.md",
    "content": "## docker-maven-plugin examples\n\nThis directory holds various examples for the usage of the docker-maven\nplugin. It can be used as starting point for your own projects.\n\nThe examples are\n\n* **jolokia-integration-test** : The same integration test for [Jolokia](http://www.jolokia.org)\n  in various way for defining it\n* **net** : Usage of various `net` modes (`bridge`, `none`, `host`, `container` and custom networks)\n\n## Backlog\n\n* **base-layer** : Dependencies are put in a separate base layer\n* **multiple-docker-hosts** : Usage of multiple docker hosts\n"
  },
  {
    "path": "it/docker-compose/pom.xml",
    "content": "<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the contaner running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-docker-compose</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n  <url>http://www.jolokia.org</url>\n\n  <properties>\n    <server.version>7</server.version>\n    <server.name>tomcat</server.name>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.jolokia</groupId>\n      <artifactId>jolokia-war</artifactId>\n      <version>1.3.5</version>\n      <type>war</type>\n    </dependency>\n  </dependencies>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>install</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>install</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <images>\n            <image>\n            <alias>jolokia-war</alias>\n              <!-- Data image containing jolokia.war -->\n              <name>${project.groupId}/${project.artifactId}:latest</name>\n              <!-- <build> the image-->\n              <build>\n                <assembly>\n                  <inline>\n                    <id>jolokia</id>\n                    <dependencySets>\n                      <dependencySet>\n                        <includes>\n                          <include>org.jolokia:jolokia-war</include>\n                        </includes>\n                        <outputDirectory>.</outputDirectory>\n                        <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n                      </dependencySet>\n                    </dependencySets>\n                  </inline>\n                </assembly>\n              </build>\n              <!-- The <run> part is taken from compose -->\n              <external>\n                <type>compose</type>\n              </external>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/docker-compose/src/main/docker/Dockerfile",
    "content": "FROM busybox:latest\nVOLUME [\"/maven\"]\nCOPY j4p.war /maven/\n"
  },
  {
    "path": "it/docker-compose/src/main/docker/docker-compose.yml",
    "content": "version: \"2\"\nservices:\n  jolokia-war:\n    labels:\n      \"dmp.type\": \"example\"\n      \"dmp.value\": \"100$$\"\n  tomcat:\n    image: \"fabric8/tomcat-7:latest\"\n    ports:\n      - \"8080:8080\"\n    environment:\n      CATALINA_OPTS: \"-Xmx32m\"\n      AB_OFF: \"1\"\n    ulimits:\n      memlock:\n        hard: 2048\n        soft: 1024\n    volumes_from:\n      - jolokia-war"
  },
  {
    "path": "it/dockerfile/README.md",
    "content": "## d-m-p sample using a Dockerfile\n\nThis example shows how to use docker-maven-plugin together with a Dockerfile. \nIt is a simple `HelloWorld` servlet running on top of Jetty at the root context.\n \nThe [Dockerfile](src/main/docker/Dockerfile) is located is `src/main/docker`. \nPlease note how the assembly is added using the directory `maven` which will be created on the fly by this plugin when an `<assembly>` is specified. \n \nTo build and start a Jetty container with a mapped port at 8080 on the Docker host use\n\n```\nmvn package docker:build docker:run\n```"
  },
  {
    "path": "it/dockerfile/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n  <!--\n  Simple sample program including Java code.\n\n  This helloworld exactly has been taken over mostly from https://github.com/arun-gupta/docker-java-sample.git\n-->\n\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dockerfile</artifactId>\n  <version>0.29-SNAPSHOT</version>\n  <packaging>war</packaging>\n  <name>dmp-sample-dockerfile</name>\n\n  <properties>\n    <file>welcome.txt</file>\n    <base>jetty</base>\n    <project.artifactId>${project.artifactId}</project.artifactId>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>3.8.1</version>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>javax.servlet</groupId>\n      <artifactId>javax.servlet-api</artifactId>\n      <version>3.1.0</version>\n    </dependency>\n    <dependency>\n      <groupId>commons-io</groupId>\n      <artifactId>commons-io</artifactId>\n      <version>2.5</version>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>install</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>install</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <images>\n            <image>\n              <name>fabric8:dmp-sample-dockerfile</name>\n              <alias>dockerfile</alias>\n              <build>\n                <!-- filter>@</filter-->\n                <contextDir>${project.basedir}/src/main/docker</contextDir>\n                <assembly>\n                  <descriptorRef>rootWar</descriptorRef>\n                </assembly>\n              </build>\n              <run>\n                <ports>\n                  <port>8080:8080</port>\n                </ports>\n              </run>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "it/dockerfile/src/main/docker/Dockerfile",
    "content": "# Sample Dockerfile for use with the Docker file mode\nFROM ${base}\n\nENV SAMPLE_BUILD_MODE=dockerfile\nLABEL PROJECT_NAME=hello-world \\\n      PROJECT=${project.artifactId}\n\n# Arbitrary files can be added\nADD ${file} /\n\n# In maven/ the files as specified in the <assembly> section is stored\n# and need to be added manually\nCOPY maven/ /var/lib/jetty/webapps/\n\nEXPOSE 8080\n"
  },
  {
    "path": "it/dockerfile/src/main/docker/welcome.txt",
    "content": "Hello World !!!"
  },
  {
    "path": "it/dockerfile/src/main/java/io/fabric8/dmp/samples/dockerfile/HelloWorldServlet.java",
    "content": "package io.fabric8.dmp.samples.dockerfile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.FileUtils;\n\n/**\n * @author roland\n * @since 18.04.17\n */\npublic class HelloWorldServlet extends HttpServlet {\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n        String txt = FileUtils.readFileToString(new File(\"/welcome.txt\"), Charset.defaultCharset());\n        resp.getWriter().append(txt).flush();\n        resp.setHeader(\"Content-Type\", \"plain/text\");\n    }\n}\n"
  },
  {
    "path": "it/dockerfile/src/main/webapp/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-app version=\"3.0\" xmlns=\"http://java.sun.com/xml/ns/javaee\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\">\n\n  <servlet>\n    <display-name>HelloWold</display-name>\n    <servlet-name>hello-world</servlet-name>\n    <servlet-class>io.fabric8.dmp.samples.dockerfile.HelloWorldServlet</servlet-class>\n  </servlet>\n\n  <servlet-mapping>\n    <servlet-name>hello-world</servlet-name>\n    <url-pattern>/</url-pattern>\n  </servlet-mapping>\n\n</web-app>"
  },
  {
    "path": "it/dockerignore/.maven-dockerignore",
    "content": "target/**\n"
  },
  {
    "path": "it/dockerignore/Dockerfile",
    "content": "FROM busybox\n"
  },
  {
    "path": "it/dockerignore/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for showing a vanilla Dockerfile\n  usage from the top-level directory\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-dockerignore</artifactId>\n  <version>0.29-SNAPSHOT</version>\n  <packaging>docker-build</packaging>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <extensions>true</extensions>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>install</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <images>\n            <image>\n              <alias>simple</alias>\n              <name>dmp-sample/dockerignore</name>\n              <build>\n                <dockerFileDir>${project.basedir}</dockerFileDir>\n                <compression>gzip</compression>\n              </build>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "it/healthcheck/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the health check feature\n\n  Call it with 'mvn install'.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-healthcheck</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <images>\n            <image>\n              <alias>healthybox1</alias>\n              <name>busybox1</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n                <cmd>\n                  <shell>sleep 2</shell>\n                </cmd>\n              </build>\n              <run>\n                <wait>\n                  <healthy>true</healthy>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>healthybox2</alias>\n              <name>busybox2</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <timeout>3s</timeout>\n                  <retries>3</retries>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>healthybox3</alias>\n              <name>busybox3</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <retries>3</retries>\n                  <cmd>\n                    <shell>curl -f http://localhost/ || exit 1</shell>\n                  </cmd>\n                </healthCheck>\n              </build>\n              <run>\n                <wait>\n                  <healthy>true</healthy>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>healthybox4</alias>\n              <name>busybox4</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <mode>cmd</mode>\n                  <interval>5m</interval>\n                  <cmd>\n                    <exec>\n                      <args>curl</args>\n                      <args>-f</args>\n                      <args>http://localhost/</args>\n                      <args>||</args>\n                      <args>exit 1</args>\n                    </exec>\n                  </cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>healthybox5</alias>\n              <name>busybox5</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <timeout>3s</timeout>\n                  <retries>3</retries>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>unhealthybox6</alias>\n              <name>busybox5</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <mode>none</mode>\n                </healthCheck>\n              </build>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/helloworld/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n  <!--\n  Simple sample program including Java code.\n\n  This helloworld exactly has been taken over mostly from https://github.com/arun-gupta/docker-java-sample.git\n-->\n\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <artifactId>dmp-sample-helloworld</artifactId>\n  <version>0.29-SNAPSHOT</version>\n  <packaging>jar</packaging>\n  <name>dmp-sample-helloworld</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>3.8.1</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.6.1</version>\n        <configuration>\n          <source>1.8</source>\n          <target>1.8</target>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.codehaus.mojo</groupId>\n        <artifactId>exec-maven-plugin</artifactId>\n        <version>1.5.0</version>\n        <configuration>\n          <mainClass>io.fabric8.dmp.sample.helloworld.App</mainClass>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-jar-plugin</artifactId>\n        <version>3.0.2</version>\n        <configuration>\n          <archive>\n            <manifest>\n              <mainClass>io.fabric8.dmp.sample.helloworld.App</mainClass>\n            </manifest>\n          </archive>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <images>\n            <image>\n              <name>hello/sub/project/java:${project.version}</name>\n              <alias>hello-world</alias>\n              <build>\n                <from>openjdk:latest</from>\n                <assembly>\n                  <descriptorRef>artifact</descriptorRef>\n                </assembly>\n                <cmd>java -jar maven/${project.name}-${project.version}.jar</cmd>\n              </build>\n              <run>\n                <wait>\n                  <log>Hello World!</log>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>docker:build</id>\n            <phase>install</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>docker:start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>docker:stop</id>\n            <phase>install</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "it/helloworld/src/main/java/io/fabric8/dmp/sample/helloworld/App.java",
    "content": "package io.fabric8.dmp.sample.helloworld;\n\n/**\n * Hello world!\n */\npublic class App {\n    public static void main(String[] args) {\n        System.out.println(\"Hello World!\");\n    }\n}\n"
  },
  {
    "path": "it/helloworld/src/test/java/io/fabric8/dmp/sample/helloworld/AppTest.java",
    "content": "package io.fabric8.dmp.sample.helloworld;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest\n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "it/log/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the custom network mode\n\n  Call it with 'mvn install'.\n  It will automatically create the custom network \"test-network\" and create two automatically named containers that can\n  talk to each other via their netAlias names.\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <groupId>io.fabric8</groupId>\n  <artifactId>dmp-sample-log</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <startParallel>false</startParallel>\n          <images>\n            <image>\n              <alias>jetty1</alias>\n              <name>jetty</name>\n              <run>\n                <wait>\n                  <log>.*Server:main: Started @\\d+ms.*</log>\n                  <time>60000</time>\n                </wait>\n              <log><enabled>true</enabled></log>\n              </run>\n            </image>\n            <image>\n              <alias>jetty2</alias>\n              <name>jetty</name>\n              <run>\n                <wait>\n                  <log>.*Server:main: Started @\\d+ms.*</log>\n                  <time>60000</time>\n                </wait>\n              <log><enabled>true</enabled></log>\n              </run>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/net/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the various network modes\n\n  Call it with 'mvn install' and one of the following profiles:\n\n  * \"container\" : Container connecting to another container's network\n  * \"bridge\"    : Bridge mode\n  * \"host\"      : Host mode\n  * \"custom\"    : Custom network 'test-network' (must be created with 'docker network create test-network' in advance)\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-net</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n  <profiles>\n    <profile>\n      <!-- \"box1\" connects to the network of \"box2\" which is bridged -->\n      <id>container</id>\n      <activation><activeByDefault>true</activeByDefault></activation>\n      <properties>\n        <box1.net>container:box2</box1.net>\n        <box2.net>bridge</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in \"bridge\" mode -->\n      <id>bridge</id>\n      <properties>\n        <box1.net>bridge</box1.net>\n        <box2.net>bridge</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in \"host\" mode -->\n      <id>host</id>\n      <properties>\n        <box1.net>host</box1.net>\n        <box2.net>host</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images with no networking -->\n      <id>none</id>\n      <properties>\n        <box1.net>none</box1.net>\n        <box2.net>none</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in a custom network 'test-network' which needs to be created beforehand -->\n      <id>custom</id>\n      <properties>\n        <box1.net>test-network</box1.net>\n        <box2.net>test-network</box2.net>\n      </properties>\n    </profile>\n\n  </profiles>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <images>\n            <image>\n              <alias>box1</alias>\n              <name>busybox</name>\n              <run>\n                <namingStrategy>alias</namingStrategy>\n                <net>${box1.net}</net>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>ip address | grep \"inet \"; echo \"finish\"; tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <log>\n                  <prefix>1</prefix> <color>cyan</color>\n                </log>\n                <wait>\n                  <log>finish</log>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>box2</alias>\n              <name>busybox</name>\n              <run>\n                <net>${box2.net}</net>\n                <namingStrategy>alias</namingStrategy>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>ip address | grep \"inet \"; echo \"finish\"; tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <log>\n                  <prefix>2</prefix> <color>blue</color>\n                </log>\n                <wait>\n                  <log>finish</log>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the contaner running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>dmp-sample-parent</artifactId>\n  <version>0.29-SNAPSHOT</version>\n  <packaging>pom</packaging>\n\n  <url>http://www.jolokia.org</url>\n\n  <!-- Should this reference the outer directory as a parent pom instead?  -->\n  <properties>\n    <docker.maven.plugin.version>${project.version}</docker.maven.plugin.version>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n  </properties>\n\n  <build>\n    <pluginManagement>\n      <plugins>\n        <plugin>\n          <groupId>io.fabric8</groupId>\n          <artifactId>docker-maven-plugin</artifactId>\n          <version>${docker.maven.plugin.version}</version>\n        </plugin>\n      </plugins>\n    </pluginManagement>\n  </build>\n\n  <modules>\n    <module>net</module>\n    <module>volume</module>\n    <module>properties</module>\n    <module>dockerignore</module>\n    <module>docker-compose</module>\n    <module>smallest</module>\n    <module>zero-config</module>\n    <module>healthcheck</module>\n    <module>helloworld</module>\n    <module>dockerfile</module>\n    <module>log</module>\n    <module>run-java</module>\n  </modules>\n</project>\n"
  },
  {
    "path": "it/properties/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for showing a vanilla Dockerfile\n  usage from the top-level directory\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-properties</artifactId>\n  <version>0.29-SNAPSHOT</version>\n  <packaging>docker-build</packaging>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>install</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n        <extensions>true</extensions>\n        <configuration>\n          <images>\n            <image>\n              <external>\n                <type>properties</type>\n                <prefix>postgres.docker</prefix>\n              </external>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n  <properties>\n    <postgres.docker.name>postgres:9.5.2</postgres.docker.name>\n    <postgres.docker.log.prefix>postgres</postgres.docker.log.prefix>\n    <postgres.docker.ports.1>${itest.postgres.port}:5432</postgres.docker.ports.1>\n    <postgres.docker.env.POSTGRES_DB>localhost</postgres.docker.env.POSTGRES_DB>\n    <postgres.docker.envRun.POSTGRES_USER>superuser</postgres.docker.envRun.POSTGRES_USER>\n    <postgres.docker.envRun.POSTGRES_PASSWORD>superuser-password</postgres.docker.envRun.POSTGRES_PASSWORD>\n    <postgres.docker.wait.time>10000</postgres.docker.wait.time>\n    <postgres.docker.wait.log>PostgreSQL init process complete</postgres.docker.wait.log>\n  </properties>\n</project>\n"
  },
  {
    "path": "it/run-java/Dockerfile",
    "content": "FROM openjdk:jre\n\nADD target/${project.build.finalName}.jar /opt/hello-world.jar\nADD target/docker-extra/run-java/run-java.sh /opt\n\n# See https://github.com/fabric8io-images/run-java-sh/ for more information\n# about run-java.sh\nCMD JAVA_MAIN_CLASS=HelloWorld sh /opt/run-java.sh\n\n"
  },
  {
    "path": "it/run-java/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>dmp-sample-run-java</artifactId>\n  <packaging>jar</packaging>\n  <version>0.29-SNAPSHOT</version>\n\n  <build>\n\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>docker:build</id>\n            <phase>install</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n        </executions>  \n        <dependencies>\n          <dependency>\n            <groupId>io.fabric8</groupId>\n            <artifactId>run-java-sh</artifactId>\n            <version>1.2.2</version>\n          </dependency>\n        </dependencies>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/run-java/src/main/java/HelloWorld.java",
    "content": "public class HelloWorld {\n\n    public static void main(String[] args) {\n        System.out.println(\"Hello world !\");\n    }\n}\n"
  },
  {
    "path": "it/smallest/Dockerfile",
    "content": "FROM busybox\nCMD [\"echo\", \"Hello\", \"world!\"]\n"
  },
  {
    "path": "it/smallest/pom.xml",
    "content": "<project>\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <groupId>fabric8io</groupId>\n  <artifactId>dmp-sample-smallest</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "it/test.xml",
    "content": "<c xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://fabric8.io/docker-maven-plugin/test2\"\n         xsi:schemaLocation=\"http://fabric8.io/docker-maven-plugin/test2 file:///tmp/test2.xsd\">\n  <watchMode>build</watchMode>\n  <pushRegistry>blas</pushRegistry>\n  <pushRegistry>blub</pushRegistry>\n  <images>\n    <image>\n      <build>\n\n      </build>\n      <name>Test</name>\n      <alias>blub</alias>\n      <run>\n      </run>\n      <alias></alias>\n    </image>\n  </images>\n</c>"
  },
  {
    "path": "it/volume/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the volume creation feature\n\n\n  Call it with 'mvn docker:create-volume'.\n  or\n  Call it with 'mvn docker:verify'\n  It will create the volume \"newVolume\"\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-volume</artifactId>\n  <version>0.29-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <startParallel>true</startParallel>\n          <volumes>\n            <volume>\n              <name>newVolume</name>\n            </volume>\n          </volumes>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>volume-create</goal>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n              <goal>volume-remove</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/zero-config/Dockerfile",
    "content": "FROM openjdk:jre\n\n#RUN VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)\nARG jar_file=target/zero-config-${project.version}.jar\n\nADD $jar_file /tmp/zero-config.jar\nCMD java -cp /tmp/zero-config.jar HelloWorld\n"
  },
  {
    "path": "it/zero-config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.29-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>zero-config</artifactId>\n  <packaging>jar</packaging>\n  <version>0.29-SNAPSHOT</version>\n\n  <properties>\n    <jar_file>${project.build.directory}/${project.build.finalName}.jar</jar_file>\n  </properties>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>build</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>build</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "it/zero-config/src/main/java/HelloWorld.java",
    "content": "public class HelloWorld {\n\n    public static void main(String[] args) {\n        System.out.println(\"Hello world !\");\n    }\n}\n"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  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,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -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\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -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  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nif [ \"$MVNW_VERBOSE\" = true ]; then\n  echo $MAVEN_PROJECTBASEDIR\nfi\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <prerequisites>\n    <maven>3.0.3</maven>\n  </prerequisites>\n\n  <groupId>io.fabric8</groupId>\n  <artifactId>docker-maven-plugin</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>maven-plugin</packaging>\n\n  <name>docker-maven-plugin</name>\n  <description>Docker Maven Plugin</description>\n\n  <url>http://github.com/fabric8io/docker-maven-plugin</url>\n\n  <developers>\n    <developer>\n      <name>Roland Huss</name>\n      <id>roland</id>\n      <email>rhuss@redhat.com</email>\n      <roles>\n        <role>Developer</role>\n      </roles>\n    </developer>\n    <developer>\n      <name>Jae Gangemi</name>\n      <id>jgangemi</id>\n      <roles>\n        <role>Developer</role>\n      </roles>\n    </developer>\n  </developers>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <maven.version>3.3.9</maven.version>\n    <jmockit.version>1.43</jmockit.version>\n    <surefire.version>3.0.0-M2</surefire.version>\n    <jib-core.version>0.12.0</jib-core.version>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-plugin-api</artifactId>\n      <scope>provided</scope>\n      <version>${maven.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-core</artifactId>\n      <version>${maven.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-model</artifactId>\n      <version>${maven.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-artifact</artifactId>\n      <version>${maven.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.sonatype.plexus</groupId>\n      <artifactId>plexus-sec-dispatcher</artifactId>\n      <scope>provided</scope>\n      <version>1.3</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.codehaus.plexus</groupId>\n      <artifactId>plexus-utils</artifactId>\n      <scope>provided</scope>\n      <version>3.0.24</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.codehaus.plexus</groupId>\n      <artifactId>plexus-component-annotations</artifactId>\n      <scope>provided</scope>\n      <version>1.6</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.maven.plugin-tools</groupId>\n      <artifactId>maven-plugin-annotations</artifactId>\n      <version>3.5</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <!-- =============================================================================== -->\n\n    <dependency>\n      <groupId>org.apache.httpcomponents</groupId>\n      <artifactId>httpclient</artifactId>\n      <version>4.5.5</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.github.jnr</groupId>\n      <artifactId>jnr-unixsocket</artifactId>\n      <version>0.25</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-lang3</artifactId>\n      <version>3.6</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-text</artifactId>\n      <version>1.1</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.google.code.gson</groupId>\n      <artifactId>gson</artifactId>\n      <version>2.8.5</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.fusesource.jansi</groupId>\n      <artifactId>jansi</artifactId>\n      <version>1.16</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.bouncycastle</groupId>\n      <artifactId>bcpkix-jdk15on</artifactId>\n      <version>1.65</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.maven.plugins</groupId>\n      <artifactId>maven-assembly-plugin</artifactId>\n      <version>3.1.0</version>\n      <exclusions>\n        <exclusion>\n          <groupId>org.apache.maven</groupId>\n          <artifactId>maven-project</artifactId>\n        </exclusion>\n        <!-- Doesn't work with guava which contains newer versions of utilities -->\n        <exclusion>\n          <groupId>com.google.collections</groupId>\n          <artifactId>google-collections</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>com.google.cloud.tools</groupId>\n      <artifactId>jib-core</artifactId>\n      <version>${jib-core.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.yaml</groupId>\n      <artifactId>snakeyaml</artifactId>\n      <version>1.26</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.google.guava</groupId>\n      <artifactId>guava</artifactId>\n      <version>27.0.1-jre</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.codehaus.plexus</groupId>\n      <artifactId>plexus-interpolation</artifactId>\n      <version>1.24</version>\n    </dependency>\n\n\n    <!-- =============================================================================== -->\n\n    <dependency>\n      <groupId>org.hamcrest</groupId>\n      <artifactId>hamcrest-all</artifactId>\n      <version>1.3</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.jmockit</groupId>\n      <artifactId>jmockit</artifactId>\n      <version>${jmockit.version}</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.12</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.skyscreamer</groupId>\n      <artifactId>jsonassert</artifactId>\n      <version>1.5.0</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>pl.pragmatists</groupId>\n      <artifactId>JUnitParams</artifactId>\n      <version>1.1.0</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.assertj</groupId>\n      <artifactId>assertj-core</artifactId>\n      <version>2.6.0</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>com.github.stefanbirkner</groupId>\n      <artifactId>system-rules</artifactId>\n      <version>1.19.0</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n\n      <plugin>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.6.2</version>\n        <configuration>\n          <source>1.8</source>\n          <target>1.8</target>\n          <compilerArgument>-Xlint:deprecation</compilerArgument>\n          <showWarnings>true</showWarnings>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-plugin-plugin</artifactId>\n        <version>3.5</version>\n        <configuration>\n          <goalPrefix>docker</goalPrefix>\n          <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>\n        </configuration>\n        <executions>\n          <execution>\n            <id>mojo-descriptor</id>\n            <phase>process-classes</phase>\n            <goals>\n              <goal>descriptor</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>help-goal</id>\n            <goals>\n              <goal>helpmojo</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <artifactId>maven-resources-plugin</artifactId>\n        <version>3.0.2</version>\n        <executions>\n          <execution>\n            <id>copy-plexus-resources</id>\n            <phase>process-resources</phase>\n            <goals>\n              <goal>copy-resources</goal>\n            </goals>\n            <configuration>\n              <outputDirectory>${basedir}/target/filtered-resources/META-INF/plexus</outputDirectory>\n              <resources>\n                <resource>\n                  <directory>src/main/resources/META-INF/plexus</directory>\n                  <filtering>true</filtering>\n                </resource>\n              </resources>\n            </configuration>\n          </execution>\n          <execution>\n            <id>copy-asciidoc</id>\n            <phase>process-resources</phase>\n            <goals>\n              <goal>copy-resources</goal>\n            </goals>\n            <configuration>\n              <outputDirectory>${project.build.outputDirectory}/META-INF/doc/dmp</outputDirectory>\n              <resources>\n                <resource>\n                  <directory>src/main/asciidoc</directory>\n                  <filtering>false</filtering>\n                </resource>\n              </resources>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>org.codehaus.plexus</groupId>\n        <artifactId>plexus-component-metadata</artifactId>\n        <version>1.7.1</version>\n        <configuration>\n          <staticMetadataDirectory>${basedir}/target/filtered-resources/META-INF/plexus</staticMetadataDirectory>\n        </configuration>\n        <executions>\n          <execution>\n            <goals>\n              <goal>generate-metadata</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n\n    <pluginManagement>\n      <plugins>\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-release-plugin</artifactId>\n          <version>2.5.3</version>\n        </plugin>\n\n        <plugin>\n          <artifactId>maven-jar-plugin</artifactId>\n          <version>3.0.2</version>\n          <configuration>\n            <archive>\n              <addMavenDescriptor>true</addMavenDescriptor>\n            </archive>\n          </configuration>\n        </plugin>\n\n        <plugin>\n          <artifactId>maven-deploy-plugin</artifactId>\n          <version>2.8.2</version>\n        </plugin>\n\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-source-plugin</artifactId>\n          <version>3.0.0</version>\n        </plugin>\n\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-javadoc-plugin</artifactId>\n          <version>2.10.4</version>\n        </plugin>\n\n        <plugin>\n          <groupId>org.asciidoctor</groupId>\n          <artifactId>asciidoctor-maven-plugin</artifactId>\n          <version>1.5.5</version>\n          <dependencies>\n            <dependency>\n              <groupId>org.asciidoctor</groupId>\n              <artifactId>asciidoctorj</artifactId>\n              <version>1.5.6</version>\n            </dependency>\n          </dependencies>\n          <configuration>\n            <sourceDirectory>src/main/asciidoc</sourceDirectory>\n            <attributes>\n              <icons>font</icons>\n              <pagenums />\n              <version>${project.version}</version>\n              <toc />\n                <idprefix />\n              <idseparator>-</idseparator>\n            </attributes>\n          </configuration>\n        </plugin>\n\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-surefire-plugin</artifactId>\n          <version>${surefire.version}</version>\n          <configuration>\n            <argLine>\n              -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar\n            </argLine>\n          </configuration>\n        </plugin>\n\n      </plugins>\n\n    </pluginManagement>\n\n    <extensions>\n      <extension>\n        <groupId>org.apache.maven.wagon</groupId>\n        <artifactId>wagon-ssh-external</artifactId>\n        <version>3.1.0</version>\n      </extension>\n    </extensions>\n  </build>\n\n  <distributionManagement>\n    <snapshotRepository>\n      <id>sonatype-nexus-snapshots</id>\n      <name>Sonatype Nexus Snapshots</name>\n      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n    </snapshotRepository>\n    <repository>\n      <id>sonatype-nexus-staging</id>\n      <name>Nexus Release Repository</name>\n      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n    </repository>\n  </distributionManagement>\n\n  <issueManagement>\n    <system>GitHub</system>\n    <url>https://github.com/fabric8io/docker-maven-plugin/issues/</url>\n  </issueManagement>\n\n  <scm>\n    <connection>scm:git:git://github.com/fabric8io/docker-maven-plugin.git</connection>\n    <developerConnection>scm:git:ssh://git@github.com/fabric8io/docker-maven-plugin.git</developerConnection>\n    <tag>HEAD</tag>\n    <url>git://github.com/fabric8io/docker-maven-plugin.git</url>\n  </scm>\n\n  <licenses>\n    <license>\n      <name>Apache 2</name>\n      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n      <distribution>repo</distribution>\n      <comments>A business-friendly OSS license</comments>\n    </license>\n  </licenses>\n\n  <profiles>\n    <!-- \"release\" profiles used for deploying with fabric8 -->\n    <profile>\n      <id>release</id>\n      <build>\n        <plugins>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-deploy-plugin</artifactId>\n            <configuration>\n              <skip>true</skip>\n            </configuration>\n          </plugin>\n\n          <plugin>\n            <groupId>org.sonatype.plugins</groupId>\n            <artifactId>nexus-staging-maven-plugin</artifactId>\n            <version>1.6.8</version>\n            <executions>\n              <execution>\n                <id>default-deploy</id>\n                <phase>deploy</phase>\n                <goals>\n                  <goal>deploy</goal>\n                </goals>\n              </execution>\n            </executions>\n            <configuration>\n              <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n              <serverId>oss-sonatype-staging</serverId>\n            </configuration>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-gpg-plugin</artifactId>\n            <version>1.6</version>\n            <configuration>\n              <skip>false</skip>\n            </configuration>\n            <executions>\n              <execution>\n                <id>sign-artifacts</id>\n                <phase>verify</phase>\n                <goals>\n                  <goal>sign</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-source-plugin</artifactId>\n            <configuration>\n              <skipSource>false</skipSource>\n            </configuration>\n            <executions>\n              <execution>\n                <id>attach-sources</id>\n                <goals>\n                  <goal>jar</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-javadoc-plugin</artifactId>\n            <configuration>\n              <skip>false</skip>\n              <additionalparam>-Xdoclint:none</additionalparam>\n            </configuration>\n            <executions>\n              <execution>\n                <id>attach-javadocs</id>\n                <goals>\n                  <goal>jar</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <!-- Enable JaCoCo Test -->\n    <profile>\n      <id>jacoco</id>\n\n      <properties>\n        <jacoco.version>0.7.9</jacoco.version>\n      </properties>\n\n      <dependencies>\n        <!-- JaCoCo runtime must be in classpath for offline mode -->\n        <dependency>\n          <groupId>org.jacoco</groupId>\n          <artifactId>org.jacoco.agent</artifactId>\n          <classifier>runtime</classifier>\n          <version>${jacoco.version}</version>\n          <scope>test</scope>\n        </dependency>\n      </dependencies>\n\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.jacoco</groupId>\n            <artifactId>jacoco-maven-plugin</artifactId>\n            <version>${jacoco.version}</version>\n            <executions>\n              <execution>\n                <id>instrument</id>\n                <phase>process-classes</phase>\n                <goals>\n                  <goal>instrument</goal>\n                </goals>\n              </execution>\n              <execution>\n                <id>restore</id>\n                <phase>test</phase>\n                <goals>\n                  <goal>restore-instrumented-classes</goal>\n                  <goal>report</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-surefire-plugin</artifactId>\n\t    <version>${surefire.version}</version>\n            <configuration>\n              <systemPropertyVariables>\n                <!-- JaCoCo runtime must know where to dump coverage: -->\n                <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>\n              </systemPropertyVariables>\n            </configuration>\n          </plugin>\n\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>doc-html</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.asciidoctor</groupId>\n            <artifactId>asciidoctor-maven-plugin</artifactId>\n            <configuration>\n              <backend>html</backend>\n              <sourceHighlighter>coderay</sourceHighlighter>\n              <attributes>\n                <toc>left</toc>\n              </attributes>\n            </configuration>\n          </plugin>\n        </plugins>\n        <defaultGoal>generate-resources asciidoctor:process-asciidoc</defaultGoal>\n      </build>\n    </profile>\n    <profile>\n      <id>doc-pdf</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.asciidoctor</groupId>\n            <artifactId>asciidoctor-maven-plugin</artifactId>\n            <configuration>\n              <backend>pdf</backend>\n              <sourceHighlighter>rouge</sourceHighlighter>\n            </configuration>\n            <dependencies>\n              <dependency>\n                <groupId>org.asciidoctor</groupId>\n                <artifactId>asciidoctorj-pdf</artifactId>\n                <version>1.5.0-alpha.16</version>\n              </dependency>\n            </dependencies>\n          </plugin>\n        </plugins>\n        <defaultGoal>generate-resources asciidoctor:process-asciidoc</defaultGoal>\n      </build>\n    </profile>\n  </profiles>\n</project>\n"
  },
  {
    "path": "release.groovy",
    "content": "#!/usr/bin/groovy\ndef repo(){\n return 'fabric8io/docker-maven-plugin'\n}\n\ndef stage(){\n  return stageProject{\n    project = repo()\n    useGitTagForNextVersion = true\n  }\n}\n\ndef approveRelease(project){\n  def releaseVersion = project[1]\n  approve{\n    room = null\n    version = releaseVersion\n    console = null\n    environment = 'fabric8'\n  }\n}\n\ndef release(project){\n  releaseProject{\n    stagedProject = project\n    useGitTagForNextVersion = true\n    helmPush = false\n    groupId = 'io.fabric8'\n    githubOrganisation = 'fabric8io'\n    artifactIdToWatchInCentral = 'docker-maven-plugin'\n    artifactExtensionToWatchInCentral = 'jar'\n  }\n}\n\ndef mergePullRequest(prId){\n  mergeAndWaitForPullRequest{\n    project = repo()\n    pullRequestId = prId\n  }\n\n}\nreturn this\n"
  },
  {
    "path": "release.sh",
    "content": "#!/bin/bash\n\n# ======================================\n# Release script for docker-maven-plugin\n# ======================================\n\n# Exit if any error occurs\n# Fail on a single failed command in a pipeline (if supported)\nset -o pipefail\n\n# Save global script args, use \"build\" as default\nif [ -z \"$1\" ]; then\n    ARGS=(\"\")\nelse\n    ARGS=(\"$@\")\nfi\n\n# Fail on error and undefined vars (please don't use global vars, but evaluation of functions for return values)\nset -eu\n\nusage() {\n    cat - <<EOT\nRelease docker-maven-plugin\n\n\n-n  --dry-run                 Dry run, which performs the whole build but does no tagging, artefact\n                              upload or pushing Docker images\n    --release-version <ver>   Version to release (e.g. \"1.2.1\"). One version arg is mandatory\n    --snapshot-release        Snapshot release which can be created on a daily basis.\n    --settings <file>         Path to a custom settings.xml to use for the release.\n                              This file must contain all the credentials to be used for Sonatype.\n                              By default ~/.m2/settings.xml is used.\n    --local-maven-repo <dir>  Local dir for holding the local Maven repo cache. If not given, then a new\n                              temporary directory will be used (and removed after the release)\n    --no-git-push             Don't push the release tag (and symbolic major.minor tag) at the end\n    --git-remote              Name of the git remote to push to. If not given, its trying to be pushed\n                              to the git remote to which the currently checked out branch is attached to.\n                              Works only when on a branch, not when checked out directly.\n    --log <log-file>          Write full log to <log-file>, only print progress to screen\nEOT\n}\n\n\n# Dir where this script is located\nbasedir() {\n    # Default is current directory\n    local script=${BASH_SOURCE[0]}\n\n    # Resolve symbolic links\n    if [ -L $script ]; then\n        if readlink -f $script >/dev/null 2>&1; then\n            script=$(readlink -f $script)\n        elif readlink $script >/dev/null 2>&1; then\n            script=$(readlink $script)\n        elif realpath $script >/dev/null 2>&1; then\n            script=$(realpath $script)\n        else\n            echo \"ERROR: Cannot resolve symbolic link $script\"\n            exit 1\n        fi\n    fi\n\n    local dir=$(dirname \"$script\")\n    local full_dir=$(cd \"${dir}\" && pwd)\n    echo ${full_dir}\n}\n\n# Checks if a flag is present in the arguments.\nhasflag() {\n    filters=\"$@\"\n    for var in \"${ARGS[@]}\"; do\n        for filter in $filters; do\n          if [ \"$var\" = \"$filter\" ]; then\n              echo 'true'\n              return\n          fi\n        done\n    done\n}\n\n# Read the value of an option.\nreadopt() {\n    filters=\"$@\"\n    next=false\n    for var in \"${ARGS[@]}\"; do\n        if $next; then\n            echo $var\n            break;\n        fi\n        for filter in $filters; do\n            if [[ \"$var\" = ${filter}* ]]; then\n                local value=\"${var//${filter}=/}\"\n                if [ \"$value\" != \"$var\" ]; then\n                    echo $value\n                    return\n                fi\n                next=true\n            fi\n        done\n    done\n}\n\ncheck_error() {\n    local msg=\"$*\"\n    if [ \"${msg//ERROR/}\" != \"${msg}\" ]; then\n        echo \"===============================================================\"\n        echo $msg\n        exit 1\n    fi\n}\n\nget_release_version() {\n    if [ $(hasflag --snapshot-release) ]; then\n        echo $(calc_timestamp_version)\n        return\n    fi\n\n    local release_version=$(readopt --release-version)\n    if [ -z \"${release_version}\" ]; then\n        echo \"ERROR: Please specify --release-version\"\n        return\n    fi\n    echo $release_version\n}\n\ncalc_timestamp_version() {\n    # ./mvnw -N help:evaluate -Dexpression=\"project.version\"\n    local pom_version=$(./mvnw -N help:evaluate -Dexpression=\"project.version\" | grep  '^[0-9]' | sed -e 's/\\([0-9]*\\.[0-9]*\\).*/\\1/')\n    if [ -z \"${pom_version}\" ]; then\n        echo \"ERROR: Cannot extract version from pom.xml\"\n        exit 1\n    fi\n    local patch_level=$(git tag | grep ^$pom_version | grep -v '-' | grep '[0-9]*\\.[0-9]*\\.' | sed -e s/${pom_version}.// | sort -n -r | head -1)\n    echo \"${pom_version}.$((patch_level+1))-$(date '+%Y%m%d')\"\n}\n\ncheck_git_clean() {\n    echo \"==== Checking for clean Git Repo\"\n    set +e\n    git diff-index --quiet HEAD --\n    local git_uncommitted=$?\n    set -e\n    if [ $git_uncommitted != 0 ]; then\n       echo \"Untracked or changed files exist. Please run release on a clean repo\"\n       git status\n       exit 1\n    fi\n}\n\nupdate_pom_versions() {\n    local version=\"$1\"\n    local maven_opts=\"$2\"\n\n    echo \"==== Updating pom.xml versions to $version\"\n    ./mvnw ${maven_opts} versions:set -DnewVersion=$version -DprocessAllModules=true -DgenerateBackupPoms=false\n}\n\nextract_maven_opts() {\n    local maven_opts=\"-Dmaven.repo.local=$1 --batch-mode\"\n\n    local settings_xml=$(readopt --settings-xml --settings)\n    if [ -n \"${settings_xml}\" ]; then\n        maven_opts=\"$maven_opts -s $settings_xml\"\n    fi\n\n    echo $maven_opts\n}\n\nmvn_clean_install() {\n    local maven_opts=\"$1\"\n\n    echo \"==== Running 'mvn clean install'\"\n    ./mvnw ${maven_opts} clean install -DskipTests\n}\n\nbuild_and_stage_artefacts() {\n    local maven_opts=\"$1\"\n\n    if [ $(hasflag --snapshot-release) ]; then\n        echo \"==== Building locally (--no-maven-release)\"\n        ./mvnw ${maven_opts} clean install -Pflash\n    else\n        echo \"==== Building and staging Maven artefacts to Sonatype\"\n        ./mvnw ${maven_opts} -Prelease clean deploy -DstagingDescription=\"Staging Syndesis for $(readopt --release-version)\"\n    fi\n}\n\ndrop_staging_repo() {\n    local maven_opts=\"$1\"\n\n    if [ $(hasflag --snapshot-release) ]; then\n        return\n    fi\n\n    echo \"==== Dropping Sonatype staging repo\"\n    ./mvnw ${maven_opts} nexus-staging:drop -Prelease -DstagingDescription=\"Dropping repo\"\n}\n\nrelease_staging_repo() {\n    local maven_opts=\"$1\"\n\n    if [ $(hasflag --snapshot-release) ]; then\n        return\n    fi\n\n    echo \"==== Releasing Sonatype staging repo\"\n    ./mvnw ${maven_opts} -Prelease nexus-staging:release -DstagingDescription=\"Releasing $(readopt --release-version)\"\n}\n\ngit_commit_files() {\n    local version=$1\n\n    echo \"==== Committing files to local git\"\n    git_commit pom.xml \"Update pom.xmls to $version\"\n}\n\ngit_tag_release() {\n    local release_version=${1}\n\n    echo \"==== Tagging version $release_version\"\n    git tag -f \"$release_version\"\n}\n\ngit_push() {\n    local release_version=${1:-}\n\n    if [ ! $(hasflag --no-git-push) ] && [ ! $(hasflag --dry-run -n) ]; then\n        local remote=$(readopt --git-remote)\n        if [ -z \"${remote}\" ]; then\n            # Push to the remote attached to the local checkout branch\n            remote=$(git for-each-ref --format='%(upstream:short)' $(git symbolic-ref -q HEAD) | sed -e 's/\\([^\\/]*\\)\\/.*/\\1/')\n            if [ -z \"${remote}\" ]; then\n              echo \"ERROR: Cannot find remote repository to git push to\"\n              exit 1\n            fi\n        fi\n\n        echo \"==== Pushing to GitHub\"\n        if [ -n \"$release_version\" ]; then\n            echo \"* Pushing $release_version\"\n            if [ $(hasflag --snapshot-release) ]; then\n                # Force push to allow multiple releases per day\n                git push -f -u $remote $release_version\n            else\n                git push -u $remote $release_version\n            fi\n        fi\n    fi\n}\n\n# ===================================================================================\n\nif [ $(hasflag --help -h) ]; then\n   usage\n   exit 0\nfi\n\ncd $(basedir)\nrelease_version=$(get_release_version)\ncheck_error \"$release_version\"\n\n# Write to logfile if requested\nif [ $(readopt --log) ]; then\n    logfile=$(readopt --log)\n    touch $logfile\n    tail -f $logfile > >(grep ^====) &\n    tail_pid=$!\n    trap \"kill $tail_pid\" EXIT\n    exec >>$logfile 2>&1\n    sleep 1\nfi\n\n# Verify that there are no modified file in git repo\ncheck_git_clean\n\n# Temporary local repository to guarantee a clean build\nlocal_maven_repo=$(readopt --local-maven-repo)\nif [ -z \"$local_maven_repo\" ]; then\n    local_maven_repo=$(mktemp -d 2>/dev/null || mktemp -d -t 'maven_repo')\n    trap \"echo 'Removing temp maven repo $local_maven_repo' && rm -rf $local_maven_repo\" \"EXIT\"\nfi\n\n# Calculate common maven options\nmaven_opts=\"$(extract_maven_opts $local_maven_repo)\"\ncheck_error $maven_opts\n\n# Set pom.xml version to the given release_version\nupdate_pom_versions \"$release_version\" \"$maven_opts\"\n\n# Make a clean install\nmvn_clean_install \"$maven_opts\"\n\n# Build and stage artefacts to Sonatype\nbuild_and_stage_artefacts \"$maven_opts\"\n\n# For a test run, we are done\nif [ $(hasflag --dry-run -n) ]; then\n    drop_staging_repo \"$maven_opts\"\n\n    echo \"==== Dry run finished, nothing has been committed\"\n    echo \"==== Use 'git reset --hard' to cleanup\"\n    exit 0\nfi\n\n# ========================================================================\n# Commit, tag, release, push\n# --------------------------\n\n# Git Commit all changed files\ngit_commit_files \"$release_version\"\n\n# Tag the release version\ngit_tag_release \"$release_version\"\n\n# Push everything (if configured)\ngit_push \"$release_version\"\n\n# Release staging repo\nrelease_staging_repo \"$maven_opts\"\n"
  },
  {
    "path": "samples/README.md",
    "content": "## docker-maven-plugin examples\n\nThis directory holds various examples for the usage of the docker-maven\nplugin. It can be used as starting point for your own projects.\n\nThe examples are\n\n* **jolokia-integration-test** : The same integration test for [Jolokia](http://www.jolokia.org)\n  in various way for defining it\n* **net** : Usage of various `net` modes (`bridge`, `none`, `host`, `container` and custom networks)\n\n## Backlog\n\n* **base-layer** : Dependencies are put in a separate base layer\n* **multiple-docker-hosts** : Usage of multiple docker hosts\n"
  },
  {
    "path": "samples/cargo-jolokia/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the container running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <groupId>io.fabric8</groupId>\n  <artifactId>dmp-sample-cargo-jolokia</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <url>http://www.jolokia.org</url>\n\n  <properties>\n    <tomcat>7.0</tomcat>\n    <image>consol/tomcat-${tomcat}</image>\n    <jolokia.version>1.2.1</jolokia.version>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n  </properties>\n\n  <dependencies>\n    <!-- Dependencies with their version which are referenced from the\n       src/main/docker-assembly.xml descriptor. These are the WAR files which gets\n       stored in the data docker container which then is linked into tomcat container for\n       deployment -->\n    <!-- Jolokia Agent -->\n    <dependency>\n      <groupId>org.jolokia</groupId>\n        <artifactId>jolokia-war</artifactId>\n      <version>${jolokia.version}</version>\n      <type>war</type>\n    </dependency>\n\n    <!-- ======================================================================================  -->\n    <!-- Used for the integration tests -->\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.11</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>com.jayway.restassured</groupId>\n      <artifactId>rest-assured</artifactId>\n      <version>2.3.1</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n        <machine>\n          <autoCreate>true</autoCreate>\n          <createOptions>\n            <virtualbox-memory>10000</virtualbox-memory>\n            <virtualbox-no-share></virtualbox-no-share>\n          </createOptions>\n        </machine>\n          <images>\n            <image>\n              <name>${image}</name>\n              <run>\n                <ports>\n                  <!-- Port mappings: Container internal port (which must be exposed) will be\n                 dynamically mapped and this (random) port will be assigned to the maven variable\n                 ${tomcat.port}. Multiple port mapping can be specified here-->\n                  <port>jolokia.port:8080</port>\n                </ports>\n                <wait>\n                  <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                  <http>\n                    <url>http://${docker.host.address}:${jolokia.port}/jolokia</url>\n                  </http>\n                  <!-- ... but at max 10 seconds -->\n                  <time>10000</time>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n\n\n      <plugin>\n        <groupId>org.codehaus.cargo</groupId>\n        <artifactId>cargo-maven2-plugin</artifactId>\n        <configuration>\n\n          <!-- Use Tomcat 7 as server -->\n          <container>\n            <containerId>tomcat7x</containerId>\n            <type>remote</type>\n          </container>\n\n          <!-- Server specific configuration -->\n          <configuration>\n            <type>runtime</type>\n            <properties>\n              <cargo.hostname>localhost</cargo.hostname>\n\n              <!-- This is the port chosen by Docker -->\n              <cargo.servlet.port>${jolokia.port}</cargo.servlet.port>\n\n              <!-- User as configured in the Docker image -->\n              <cargo.remote.username>admin</cargo.remote.username>\n              <cargo.remote.password>admin</cargo.remote.password>\n            </properties>\n          </configuration>\n\n          <deployables>\n            <!-- Deploy a Jolokia agent -->\n            <deployable>\n              <groupId>org.jolokia</groupId>\n              <artifactId>jolokia-war</artifactId>\n              <type>war</type>\n              <properties>\n                <context>/jolokia</context>\n              </properties>\n            </deployable>\n          </deployables>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start-server</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>deploy</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n\n\n      <!-- fails-safe-plugin should be used instead of surefire so that the container gets stopped even\n           when the tests fail -->\n      <plugin>\n        <artifactId>maven-failsafe-plugin</artifactId>\n        <version>2.17</version>\n        <configuration>\n          <systemPropertyVariables>\n            <!-- Needs to be repeated here (the following two lines strangely doesn't work when the next line is omitted although)\n                 Maven, you little sneaky beast ... -->\n            <jolokia.port>${jolokia.port}</jolokia.port>\n\n            <!-- Map maven variables to system properties which in turn can be used in the test classes -->\n            <jolokia.url>http://localhost:${jolokia.port}/jolokia</jolokia.url>\n            <jolokia.version>${jolokia.version}</jolokia.version>\n          </systemPropertyVariables>\n        </configuration>\n      </plugin>\n\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/cargo-jolokia/src/test/java/io/fabric8/maven/docker/sample/jolokia/VersionIT.java",
    "content": "package io.fabric8.maven.docker.sample.jolokia;\n\nimport com.jayway.restassured.RestAssured;\nimport com.jayway.restassured.parsing.Parser;\nimport com.jayway.restassured.path.json.JsonPath;\nimport org.junit.Test;\n\nimport static com.jayway.restassured.RestAssured.*;\nimport static com.jayway.restassured.path.json.JsonPath.with;\nimport static org.hamcrest.Matchers.*;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 15.05.14\n */\npublic class VersionIT {\n\n    @Test\n    public void testVersion() {\n        String versionExpected = System.getProperty(\"jolokia.version\");\n        String jolokiaUrl = System.getProperty(\"jolokia.url\");\n\n        RestAssured.baseURI = jolokiaUrl;\n        RestAssured.defaultParser = Parser.JSON;\n        System.out.println(\"Checking URL: \" + jolokiaUrl);\n\n        // Need to do it that way since Jolokia doesnt return application/json as mimetype by default\n        JsonPath json = with(get(\"/version\").asString());\n        json.prettyPrint();\n        assertEquals(versionExpected, json.get(\"value.agent\"));\n\n        // Alternatively, set the mime type before, then Rest-assured's fluent API can be used\n        given()\n                .param(\"mimeType\", \"application/json\")\n                .get(\"/version\")\n        .then().assertThat()\n                .header(\"content-type\", containsString(\"application/json\"))\n                .body(\"value.agent\", equalTo(versionExpected))\n                .body(\"timestamp\", lessThanOrEqualTo((int) (System.currentTimeMillis() / 1000)))\n                .body(\"status\", equalTo(200))\n                .body(\"value.protocol\", equalTo(\"7.1\"))\n                .body(\"value.config\",notNullValue());\n\n    }\n\n}\n"
  },
  {
    "path": "samples/custom-net/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the custom network mode\n\n  Call it with 'mvn install'.\n  It will automatically create the custom network \"test-network\" and create two automatically named containers that can\n  talk to each other via their netAlias names.\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-custom-net</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <autoCreateCustomNetworks>true</autoCreateCustomNetworks>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <startParallel>true</startParallel>\n          <images>\n            <image>\n              <alias>box1</alias>\n              <name>busybox</name>\n              <run>\n                <network>\n                  <name>test-network</name>\n                  <alias>box1</alias>\n                  <alias>box1-alternative</alias>\n                </network>\n                <namingStrategy>none</namingStrategy>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <log>\n                  <prefix>1</prefix> <color>cyan</color>\n                </log>\n              </run>\n            </image>\n            <image>\n              <alias>box2</alias>\n              <name>busybox</name>\n              <run>\n                <network>\n                  <name>test-network</name>\n                  <alias>box2</alias>\n                </network>\n                <dependsOn>\n                  <container>box1</container>\n                </dependsOn>\n                <namingStrategy>none</namingStrategy>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>nslookup box1-alternative; tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <wait>\n                  <log>box1.test-network</log>\n                </wait>\n                <log>\n                  <prefix>2</prefix> <color>blue</color>\n                </log>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/data-jolokia/pom.xml",
    "content": "<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the contaner running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-data-jolokia</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <!-- add custom lifecycle -->\n  <packaging>docker</packaging>\n\n  <url>http://www.jolokia.org</url>\n\n  <properties>\n    <server.version>8</server.version>\n    <server.name>tomcat</server.name>\n    <image>docker.io/fabric8/${server.name}-${server.version}:latest</image>\n    <jolokia.version>1.3.2</jolokia.version>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n  </properties>\n\n  <dependencies>\n    <!-- Dependencies with their version which are referenced from the\n       src/main/docker-assembly.xml descriptor. These are the WAR files which gets\n       stored in the data docker container which then is linked into tomcat container for\n       deployment -->\n    <!-- Jolokia Agent -->\n    <dependency>\n      <groupId>org.jolokia</groupId>\n      <artifactId>jolokia-war</artifactId>\n      <version>${jolokia.version}</version>\n      <type>war</type>\n    </dependency>\n    <!-- Jolokia Integration Test Setup (registers some Test MBeans) -->\n    <dependency>\n      <groupId>org.jolokia</groupId>\n      <artifactId>jolokia-it-war</artifactId>\n      <version>${jolokia.version}</version>\n      <type>war</type>\n    </dependency>\n\n    <!-- ======================================================================================  -->\n    <!-- Used for the integration tests -->\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.11</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.jayway.restassured</groupId>\n      <artifactId>rest-assured</artifactId>\n      <version>2.3.1</version>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <extensions>true</extensions> <!-- enables using 'docker' packaging above -->\n        <configuration>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <imagePullPolicy>IfNotPresent</imagePullPolicy>\n          <watchPostGoal>org.apache.maven.plugins:maven-help-plugin:help</watchPostGoal>\n          <images>\n            <!-- Docker Image to use -->\n            <image>\n              <!-- App-Server Image -->\n              <alias>server</alias>\n              <name>${image}</name>\n              <run>\n                <namingStrategy>none</namingStrategy>\n                <volumes>\n                  <from>\n                    <image>data</image>\n                  </from>\n                </volumes>\n                <env>\n                  <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n                  <!-- Base starts jolokia by default. With this, it can be disabled -->\n                  <JOLOKIA_OFF>1</JOLOKIA_OFF>\n                  <EMPTY_PROP></EMPTY_PROP>\n                </env>\n                <envPropertyFile>${project.basedir}/src/main/docker/environment.properties</envPropertyFile>\n                <ports>\n                  <!-- Port mappings: Container internal port (which must be exposed) will be\n                 dynamically mapped and this (random) port will be assigned to the maven variable\n                 ${jolokia.port}. Multiple port mappings can be specified here-->\n                  <port>${jolokia.host}:${jolokia.port}:8080</port>\n                </ports>\n                <wait>\n                  <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                  <log>Server startup</log>\n                  <http>\n                    <url>http://${jolokia.host}:${jolokia.port}/jolokia</url>\n                    <method>GET</method>\n                    <status>200</status>\n                  </http>\n                  <exec>\n                    <postStart>ls -l /maven</postStart>\n                  </exec>\n                  <time>10000</time>\n                  <shutdown>500</shutdown>\n                </wait>\n                <log>\n                  <enabled>true</enabled>\n                  <color>red</color>\n                </log>\n                <ulimits>\n                  <ulimit>\n                    <name>memlock</name>\n                    <hard>2048</hard>\n                    <soft>1024</soft>\n                  </ulimit>\n                </ulimits>\n                <tmpfs>\n                  <mount>/var/lib/temp:size=10m</mount>\n                </tmpfs>\n              </run>\n              <watch>\n                <mode>none</mode>\n              </watch>\n            </image>\n            <image>\n              <alias>data</alias>\n              <!-- Artifact Image-->\n              <name>fabric8/${project.artifactId}:latest</name>\n              <build>\n                <tags>\n                  <tag>${project.version}</tag>\n                </tags>\n                <!-- The assembly descriptor prepares the \"/maven\" directory in the temporary data\n                   container which will be made available to the specified container. The startup script in the\n                   container (fabric8/tomcat-8) should check for files in this directory and use them\n                   for deploying them. I.e. this image has a script 'deploy-and-run.sh' which exactly\n                   does this. -->\n                <assembly>\n                  <name>app</name>\n                  <targetDir>/maven</targetDir>\n                  <mode>dir</mode>\n                  <user>www-data:www-data:www-data</user>\n                  <descriptor>assembly.xml</descriptor>\n                </assembly>\n              </build>\n              <watch>\n                <interval>5000</interval>\n                <mode>both</mode>\n              </watch>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n\n      <!-- fails-safe-plugin should be used instead of surefire so that the container gets stopped even\n           when the tests fail -->\n      <plugin>\n        <artifactId>maven-failsafe-plugin</artifactId>\n        <version>2.17</version>\n        <configuration>\n          <systemPropertyVariables>\n            <!-- Needs to be repeated here (the following two lines strangely doesn't work when the next line is omitted although)\n                 Maven, you little sneaky beast ... -->\n            <jolokia.port>${jolokia.port}</jolokia.port>\n\n            <!-- Map maven variables to system properties which in turn can be used in the test classes -->\n            <jolokia.url>http://${docker.host.address}:${jolokia.port}/jolokia</jolokia.url>\n            <jolokia.version>${jolokia.version}</jolokia.version>\n          </systemPropertyVariables>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.2</version>\n        <configuration>\n          <source>1.7</source>\n          <target>1.7</target>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-help-plugin</artifactId>\n        <version>2.2</version>\n      </plugin>\n    </plugins>\n  </build>\n\n  <repositories>\n    <repository>\n      <id>snapshots-repo</id>\n      <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n      <releases>\n        <enabled>false</enabled>\n      </releases>\n      <snapshots>\n        <enabled>true</enabled>\n      </snapshots>\n    </repository>\n  </repositories>\n\n  <profiles>\n    <profile>\n      <id>merge</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration combine.self=\"override\">\n              <watchPostGoal>org.apache.maven.plugins:maven-help-plugin:help</watchPostGoal>\n              <images>\n                <image>\n                  <!-- Artifact Image-->\n                  <name>jolokia/${project.artifactId}:${project.version}</name>\n                  <alias>jolokia</alias>\n                  <build>\n                    <from>${image}</from>\n                    <labels>\n                      <dmp.version>${project.version}</dmp.version>\n                      <dmp.name>${project.artifactId}</dmp.name>\n                    </labels>\n                    <assembly>\n                      <descriptor>assembly.xml</descriptor>\n                    </assembly>\n                  </build>\n                  <run>\n                    <env>\n                      <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n                      <JOLOKIA_OFF>1</JOLOKIA_OFF>\n                    </env>\n                    <ports>\n                      <!-- Port mappings: Container internal port (which must be exposed) will be\n                         dynamically mapped and this (random) port will be assigned to the maven variable\n                         ${tomcat.port}. Multiple port mapping can be specified here-->\n                      <port>jolokia.port:8080</port>\n                    </ports>\n                    <labels>\n                      <dmp.type>example</dmp.type>\n                    </labels>\n                    <wait>\n                      <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                      <http>\n                        <url>http://${docker.host.address}:${jolokia.port}/jolokia</url>\n                      </http>\n                      <time>10000</time>\n                    </wait>\n                    <log>\n                      <enabled>true</enabled>\n                      <prefix>TC</prefix>\n                      <color>yellow</color>\n                    </log>\n                  </run>\n                  <watch>\n                    <mode>copy</mode>\n                    <postExec>ls -l /maven</postExec>\n                  </watch>\n                </image>\n\n              </images>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>inline</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration combine.self=\"override\">\n              <images>\n                <image>\n                  <!-- Artifact Image-->\n                  <name>jolokia/${project.artifactId}:${project.version}</name>\n                  <alias>jolokia</alias>\n                  <build>\n                    <from>${image}</from>\n                    <assembly>\n                      <mode>dir</mode>\n                      <inline xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                              xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n                              xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n                        <id>jolokia-it</id>\n\n                        <dependencySets>\n                          <dependencySet>\n                            <includes>\n                              <include>org.jolokia:jolokia-war</include>\n                            </includes>\n                            <outputDirectory>.</outputDirectory>\n                            <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n                          </dependencySet>\n                          <dependencySet>\n                            <includes>\n                              <include>org.jolokia:jolokia-it-war</include>\n                            </includes>\n                            <outputDirectory>.</outputDirectory>\n                            <outputFileNameMapping>jolokia-it.war</outputFileNameMapping>\n                          </dependencySet>\n                        </dependencySets>\n                      </inline>\n                    </assembly>\n                  </build>\n                  <run>\n                    <ports>\n                      <port>jolokia.port:8080</port>\n                    </ports>\n                    <wait>\n                      <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                      <http>\n                        <url>http://${docker.host.address}:${jolokia.port}/jolokia</url>\n                      </http>\n                      <time>10000</time>\n                    </wait>\n                  </run>\n                </image>\n              </images>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n\n    <profile>\n      <id>dockerfile</id>\n      <properties>\n        <dockerfile>demo/Dockerfile</dockerfile>\n      </properties>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration combine.self=\"override\">\n              <autoPull>true</autoPull>\n              <images>\n                <image>\n                  <!-- Artifact Image-->\n                  <name>jolokia/${project.artifactId}-dockerfile:${project.version}</name>\n                  <alias>jolokia</alias>\n                  <build>\n                    <dockerFile>${dockerfile}</dockerFile>\n                    <args>\n                      <deploymentDir>maven</deploymentDir>\n                    </args>\n                  </build>\n                  <run>\n                    <ports>\n                      <port>jolokia.port:8080</port>\n                    </ports>\n                    <wait>\n                      <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                      <http>\n                        <url>http://${docker.host.address}:${jolokia.port}/jolokia</url>\n                      </http>\n                      <time>10000</time>\n                    </wait>\n                  </run>\n                </image>\n              </images>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>props</id>\n      <properties>\n        <docker.name>jolokia/${project.artifactId}:${project.version}</docker.name>\n        <docker.alias>service</docker.alias>\n        <docker.from>${image}</docker.from>\n        <docker.assembly.descriptor>assembly.xml</docker.assembly.descriptor>\n        <docker.env.CATALINA_OPTS>-Xmx32m</docker.env.CATALINA_OPTS>\n        <docker.env.JOLOKIA_OPTS>1</docker.env.JOLOKIA_OPTS>\n        <docker.ports.0>jolokia.port:8080</docker.ports.0>\n        <docker.wait.url>http://${docker.host.address}:${jolokia.port}/jolokia</docker.wait.url>\n        <docker.wait.time>10000</docker.wait.time>\n      </properties>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration combine.self=\"override\">\n              <images>\n                <image>\n                  <external>\n                    <type>props</type>\n                    <prefix>docker</prefix>\n                  </external>\n                </image>\n              </images>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>wildfly</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration combine.self=\"override\">\n              <images>\n                <image>\n                  <!-- Artifact Image-->\n                  <name>jolokia/${project.artifactId}-wildfly:${project.version}</name>\n                  <build>\n                    <from>jboss/wildfly:8.2.0.Final</from>\n                    <assembly>\n                      <user>jboss:jboss:jboss</user>\n                      <targetDir>/opt/jboss/wildfly/standalone/deployments</targetDir>\n                      <descriptor>assembly.xml</descriptor>\n                    </assembly>\n                  </build>\n                  <run>\n                    <ports>\n                      <port>jolokia.port:8080</port>\n                    </ports>\n                    <wait>\n                      <!-- The plugin waits until this URL is reachable via HTTP ... -->\n                      <wait>\n                        <url>http://${docker.host.address}:${jolokia.port}/jolokia</url>\n                      </wait>\n                      <time>10000</time>\n                    </wait>\n                  </run>\n                </image>\n              </images>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>tomcat</id>\n      <properties>\n        <server.name>tomcat</server.name>\n        <server.version>8</server.version>\n      </properties>\n      <activation>\n        <activeByDefault>true</activeByDefault>\n      </activation>\n    </profile>\n\n    <profile>\n      <id>jetty</id>\n      <properties>\n        <server.name>jetty</server.name>\n        <server.version>8</server.version>\n      </properties>\n    </profile>\n\n    <profile>\n      <id>properties</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>com.soebes.maven.plugins</groupId>\n            <artifactId>echo-maven-plugin</artifactId>\n            <version>0.2</version>\n            <executions>\n              <execution>\n                <phase>integration-test</phase>\n                <goals>\n                  <goal>echo</goal>\n                </goals>\n              </execution>\n            </executions>\n            <configuration>\n              <echos>\n                <echo>============= Props  =============</echo>\n                <echo>IP : ${docker.container.server.ip}</echo>\n              </echos>\n            </configuration>\n          </plugin>\n\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>machine</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>io.fabric8</groupId>\n            <artifactId>docker-maven-plugin</artifactId>\n            <configuration>\n              <machine>\n                <name>maven</name>\n                <autoCreate>true</autoCreate>\n                <createOptions>\n                  <driver>virtualbox</driver>\n                </createOptions>\n              </machine>\n            </configuration>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n  </profiles>\n</project>\n"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/assembly.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>jolokia-it</id>\n  <dependencySets>\n    <dependencySet>\n      <includes>\n        <include>org.jolokia:jolokia-war</include>\n      </includes>\n      <outputDirectory>.</outputDirectory>\n      <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n      <useProjectArtifact>false</useProjectArtifact>\n    </dependencySet>\n    <dependencySet>\n      <includes>\n        <include>org.jolokia:jolokia-it-war</include>\n      </includes>\n      <outputDirectory>.</outputDirectory>\n      <outputFileNameMapping>jolokia-it.war</outputFileNameMapping>\n      <useProjectArtifact>false</useProjectArtifact>\n    </dependencySet>\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.basedir}/src/main/docker</directory>\n      <outputDirectory>test/</outputDirectory>\n      <filtered>true</filtered>\n      <includes>\n        <include>hello.sh</include>\n      </includes>\n      <fileMode>755</fileMode>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/demo/.maven-dockerignore",
    "content": "# Ignore patterns\nplease-ignore/**"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/demo/Dockerfile",
    "content": "FROM consol/tomcat-7.0:latest\nARG deploymentDir\nADD jolokia.war /${deploymentDir}/\nADD jolokia-it.war /${deploymentDir}/\n"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/demo/Dockerfile.test",
    "content": "FROM consol/tomcat-8.0:latest\nARG deploymentDir\nADD jolokia.war /maven/\nADD jolokia-it.war /maven/\n"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/demo/please-ignore/me.md",
    "content": "Yep, please ignore !"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/environment.properties",
    "content": "EXAMPLE_OF_AN_EXTERNAL_VARIABLE=Yes, it is"
  },
  {
    "path": "samples/data-jolokia/src/main/docker/hello.sh",
    "content": "#!/bin/sh\n\necho \"Hello world\"\n"
  },
  {
    "path": "samples/data-jolokia/src/test/java/io/fabric8/maven/docker/sample/jolokia/VersionIT.java",
    "content": "package io.fabric8.maven.docker.sample.jolokia;\n\nimport com.jayway.restassured.RestAssured;\nimport com.jayway.restassured.parsing.Parser;\nimport com.jayway.restassured.path.json.JsonPath;\nimport org.junit.Test;\n\nimport static com.jayway.restassured.RestAssured.*;\nimport static com.jayway.restassured.path.json.JsonPath.with;\nimport static org.hamcrest.Matchers.*;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 15.05.14\n */\npublic class VersionIT {\n\n    @Test\n    public void testVersion() {\n        String versionExpected = System.getProperty(\"jolokia.version\");\n        String jolokiaUrl = System.getProperty(\"jolokia.url\");\n\n        RestAssured.baseURI = jolokiaUrl;\n        RestAssured.defaultParser = Parser.JSON;\n        System.out.println(\"Checking URL: \" + jolokiaUrl);\n\n        // Need to do it that way since Jolokia doesnt return application/json as mimetype by default\n        JsonPath json = with(get(\"/version\").asString());\n        json.prettyPrint();\n        assertEquals(versionExpected, json.get(\"value.agent\"));\n\n        // Alternatively, set the mime type before, then Rest-assured's fluent API can be used\n        given()\n                .param(\"mimeType\", \"application/json\")\n                .get(\"/version\")\n        .then().assertThat()\n                .header(\"content-type\", containsString(\"application/json\"))\n                .body(\"value.agent\", equalTo(versionExpected))\n                .body(\"timestamp\", lessThanOrEqualTo((int) (System.currentTimeMillis() / 1000)))\n                .body(\"status\", equalTo(200))\n                .body(\"value.protocol\", equalTo(\"7.2\"))\n                .body(\"value.config\",notNullValue());\n\n    }\n\n}\n"
  },
  {
    "path": "samples/docker-compose/pom.xml",
    "content": "<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the contaner running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-docker-compose</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <url>http://www.jolokia.org</url>\n\n  <properties>\n    <server.version>7</server.version>\n    <server.name>tomcat</server.name>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.jolokia</groupId>\n      <artifactId>jolokia-war</artifactId>\n      <version>1.3.5</version>\n      <type>war</type>\n    </dependency>\n  </dependencies>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <images>\n            <image>\n            <alias>jolokia-war</alias>\n              <!-- Data image containing jolokia.war -->\n              <name>${project.groupId}/${project.artifactId}:latest</name>\n              <!-- <build> the image-->\n              <build>\n                <assembly>\n                  <inline>\n                    <id>jolokia</id>\n                    <dependencySets>\n                      <dependencySet>\n                        <includes>\n                          <include>org.jolokia:jolokia-war</include>\n                        </includes>\n                        <outputDirectory>.</outputDirectory>\n                        <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n                      </dependencySet>\n                    </dependencySets>\n                  </inline>\n                </assembly>\n              </build>\n              <!-- The <run> part is taken from compose -->\n              <external>\n                <type>compose</type>\n              </external>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/docker-compose/src/main/docker/Dockerfile",
    "content": "FROM busybox:latest\nVOLUME [\"/maven\"]\nCOPY j4p.war /maven/\n"
  },
  {
    "path": "samples/docker-compose/src/main/docker/docker-compose.yml",
    "content": "version: \"2\"\nservices:\n  jolokia-war:\n    labels:\n      \"dmp.type\": \"example\"\n      \"dmp.value\": \"100$$\"\n  tomcat:\n    image: \"fabric8/tomcat-7:latest\"\n    ports:\n      - \"8080:8080\"\n    environment:\n      CATALINA_OPTS: \"-Xmx32m\"\n      AB_OFF: \"1\"\n    ulimits:\n      memlock:\n        hard: 2048\n        soft: 1024\n    volumes_from:\n      - jolokia-war"
  },
  {
    "path": "samples/dockerfile/README.md",
    "content": "## d-m-p sample using a Dockerfile\n\nThis example shows how to use docker-maven-plugin together with a Dockerfile. \nIt is a simple `HelloWorld` servlet running on top of Jetty at the root context.\n \nThe [Dockerfile](src/main/docker/Dockerfile) is located is `src/main/docker`. \nPlease note how the assembly is added using the directory `maven` which will be created on the fly by this plugin when an `<assembly>` is specified. \n \nTo build and start a Jetty container with a mapped port at 8080 on the Docker host use\n\n```\nmvn package docker:build docker:run\n```"
  },
  {
    "path": "samples/dockerfile/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n  <!--\n  Simple sample program including Java code.\n\n  This helloworld exactly has been taken over mostly from https://github.com/arun-gupta/docker-java-sample.git\n-->\n\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dockerfile</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>war</packaging>\n  <name>dmp-sample-dockerfile</name>\n\n  <properties>\n    <file>welcome.txt</file>\n    <base>jetty</base>\n    <project.artifactId>${project.artifactId}</project.artifactId>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>3.8.1</version>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>javax.servlet</groupId>\n      <artifactId>javax.servlet-api</artifactId>\n      <version>3.1.0</version>\n    </dependency>\n    <dependency>\n      <groupId>commons-io</groupId>\n      <artifactId>commons-io</artifactId>\n      <version>2.5</version>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <images>\n            <image>\n              <name>fabric8:dmp-sample-dockerfile</name>\n              <alias>dockerfile</alias>\n              <build>\n                <!-- filter>@</filter-->\n                <contextDir>${project.basedir}/src/main/docker</contextDir>\n                <assembly>\n                  <descriptorRef>rootWar</descriptorRef>\n                </assembly>\n              </build>\n              <run>\n                <ports>\n                  <port>8080:8080</port>\n                </ports>\n              </run>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/dockerfile/src/main/docker/Dockerfile",
    "content": "# Sample Dockerfile for use with the Docker file mode\nFROM ${base}\n\nENV SAMPLE_BUILD_MODE=dockerfile\nLABEL PROJECT_NAME=hello-world \\\n      PROJECT=${project.artifactId}\n\n# Arbitrary files can be added\nADD ${file} /\n\n# In maven/ the files as specified in the <assembly> section is stored\n# and need to be added manually\nCOPY maven/ /var/lib/jetty/webapps/\n\nEXPOSE 8080\n"
  },
  {
    "path": "samples/dockerfile/src/main/docker/welcome.txt",
    "content": "Hello World !!!"
  },
  {
    "path": "samples/dockerfile/src/main/java/io/fabric8/dmp/samples/dockerfile/HelloWorldServlet.java",
    "content": "package io.fabric8.dmp.samples.dockerfile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.FileUtils;\n\n/**\n * @author roland\n * @since 18.04.17\n */\npublic class HelloWorldServlet extends HttpServlet {\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n        String txt = FileUtils.readFileToString(new File(\"/welcome.txt\"), Charset.defaultCharset());\n        resp.getWriter().append(txt).flush();\n        resp.setHeader(\"Content-Type\", \"plain/text\");\n    }\n}\n"
  },
  {
    "path": "samples/dockerfile/src/main/webapp/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-app version=\"3.0\" xmlns=\"http://java.sun.com/xml/ns/javaee\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\">\n\n  <servlet>\n    <display-name>HelloWold</display-name>\n    <servlet-name>hello-world</servlet-name>\n    <servlet-class>io.fabric8.dmp.samples.dockerfile.HelloWorldServlet</servlet-class>\n  </servlet>\n\n  <servlet-mapping>\n    <servlet-name>hello-world</servlet-name>\n    <url-pattern>/</url-pattern>\n  </servlet-mapping>\n\n</web-app>"
  },
  {
    "path": "samples/dockerignore/.maven-dockerignore",
    "content": "target/**\n"
  },
  {
    "path": "samples/dockerignore/Dockerfile",
    "content": "FROM busybox\n"
  },
  {
    "path": "samples/dockerignore/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for showing a vanilla Dockerfile\n  usage from the top-level directory\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-dockerignore</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>docker-build</packaging>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <extensions>true</extensions>\n        <configuration>\n          <images>\n            <image>\n              <alias>simple</alias>\n              <name>dmp-sample/dockerignore</name>\n              <build>\n                <dockerFileDir>${project.basedir}</dockerFileDir>\n                <compression>gzip</compression>\n              </build>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/healthcheck/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the health check feature\n\n  Call it with 'mvn install'.\n\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-healthcheck</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <images>\n            <image>\n              <alias>healthybox1</alias>\n              <name>busybox1</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n                <cmd>\n                  <shell>sleep 2</shell>\n                </cmd>\n              </build>\n              <run>\n                <wait>\n                  <healthy>true</healthy>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>healthybox2</alias>\n              <name>busybox2</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <timeout>3s</timeout>\n                  <retries>3</retries>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>healthybox3</alias>\n              <name>busybox3</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <retries>3</retries>\n                  <cmd>\n                    <shell>curl -f http://localhost/ || exit 1</shell>\n                  </cmd>\n                </healthCheck>\n              </build>\n              <run>\n                <wait>\n                  <healthy>true</healthy>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>healthybox4</alias>\n              <name>busybox4</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <mode>cmd</mode>\n                  <interval>5m</interval>\n                  <cmd>\n                    <exec>\n                      <args>curl</args>\n                      <args>-f</args>\n                      <args>http://localhost/</args>\n                      <args>||</args>\n                      <args>exit 1</args>\n                    </exec>\n                  </cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>healthybox5</alias>\n              <name>busybox5</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <interval>5m</interval>\n                  <timeout>3s</timeout>\n                  <startPeriod>30m</startPeriod>\n                  <retries>3</retries>\n                  <cmd>curl -f http://localhost/ || exit 1</cmd>\n                </healthCheck>\n              </build>\n            </image>\n            <image>\n              <alias>unhealthybox6</alias>\n              <name>busybox5</name>\n              <build>\n                <from>busybox</from>\n                <healthCheck>\n                  <mode>none</mode>\n                </healthCheck>\n              </build>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/helloworld/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n  <!--\n  Simple sample program including Java code.\n\n  This helloworld exactly has been taken over mostly from https://github.com/arun-gupta/docker-java-sample.git\n-->\n\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <artifactId>dmp-sample-helloworld</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>jar</packaging>\n  <name>dmp-sample-helloworld</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>3.8.1</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.6.1</version>\n        <configuration>\n          <source>1.8</source>\n          <target>1.8</target>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.codehaus.mojo</groupId>\n        <artifactId>exec-maven-plugin</artifactId>\n        <version>1.5.0</version>\n        <configuration>\n          <mainClass>io.fabric8.dmp.sample.helloworld.App</mainClass>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-jar-plugin</artifactId>\n        <version>3.0.2</version>\n        <configuration>\n          <archive>\n            <manifest>\n              <mainClass>io.fabric8.dmp.sample.helloworld.App</mainClass>\n            </manifest>\n          </archive>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <images>\n            <image>\n              <name>hello/sub/project/java:${project.version}</name>\n              <alias>hello-world</alias>\n              <build>\n                <from>openjdk:latest</from>\n                <assembly>\n                  <descriptorRef>artifact</descriptorRef>\n                </assembly>\n                <cmd>java -jar maven/${project.name}-${project.version}.jar</cmd>\n              </build>\n              <run>\n                <wait>\n                  <log>Hello World!</log>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>docker:start</id>\n            <phase>install</phase>\n            <goals>\n              <goal>run</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/helloworld/src/main/java/io/fabric8/dmp/sample/helloworld/App.java",
    "content": "package io.fabric8.dmp.sample.helloworld;\n\n/**\n * Hello world!\n */\npublic class App {\n    public static void main(String[] args) {\n        System.out.println(\"Hello World!\");\n    }\n}\n"
  },
  {
    "path": "samples/helloworld/src/test/java/io/fabric8/dmp/sample/helloworld/AppTest.java",
    "content": "package io.fabric8.dmp.sample.helloworld;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest\n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "samples/log/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the custom network mode\n\n  Call it with 'mvn install'.\n  It will automatically create the custom network \"test-network\" and create two automatically named containers that can\n  talk to each other via their netAlias names.\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <groupId>io.fabric8</groupId>\n  <artifactId>dmp-sample-log</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <startParallel>false</startParallel>\n          <images>\n            <image>\n              <alias>jetty1</alias>\n              <name>jetty</name>\n              <run>\n                <wait>\n                  <log>.*Server:main: Started @\\d+ms.*</log>\n                  <time>60000</time>\n                </wait>\n              <log><enabled>true</enabled></log>\n              </run>\n            </image>\n            <image>\n              <alias>jetty2</alias>\n              <name>jetty</name>\n              <run>\n                <wait>\n                  <log>.*Server:main: Started @\\d+ms.*</log>\n                  <time>60000</time>\n                </wait>\n              <log><enabled>true</enabled></log>\n              </run>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/multi-wait/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>io.fabric8</groupId>\n  <artifactId>dmp-sample-multi-wait</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <!-- Sample exhibiting connection error described in https://github.com/fabric8io/docker-maven-plugin/issues/574 -->\n <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <logDate>none</logDate>\n          <images>\n            <image>\n              <alias>db</alias>\n              <name>postgres:9.6.0</name>\n              <run>\n                <env>\n                  <POSTGRES_PASSWORD>password</POSTGRES_PASSWORD>\n                </env>\n                <wait>\n                  <log>database system is ready to accept connections</log>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>db2</alias>\n              <name>postgres:9.6.0</name>\n              <run>\n                <env>\n                  <POSTGRES_PASSWORD>password</POSTGRES_PASSWORD>\n                </env>\n                <wait>\n                  <log>database system is ready to accept connections</log>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>web</alias>\n              <name>jboss/wildfly:9.0.2.Final</name>\n              <run>\n                <links>\n                  <link>db,db2</link>\n                </links>\n                <ports>\n                  <port>web.port:8080</port>\n                </ports>\n                <wait>\n                  <log>WildFly Full .* \\(WildFly Core .*\\) started</log>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-failsafe-plugin</artifactId>\n        <executions>\n          <execution>\n            <goals>\n              <goal>integration-test</goal>\n              <goal>verify</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/net/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the various network modes\n\n  Call it with 'mvn install' and one of the following profiles:\n\n  * \"container\" : Container connecting to another container's network\n  * \"bridge\"    : Bridge mode\n  * \"host\"      : Host mode\n  * \"custom\"    : Custom network 'test-network' (must be created with 'docker network create test-network' in advance)\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-net</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <profiles>\n    <profile>\n      <!-- \"box1\" connects to the network of \"box2\" which is bridged -->\n      <id>container</id>\n      <activation><activeByDefault>true</activeByDefault></activation>\n      <properties>\n        <box1.net>container:box2</box1.net>\n        <box2.net>bridge</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in \"bridge\" mode -->\n      <id>bridge</id>\n      <properties>\n        <box1.net>bridge</box1.net>\n        <box2.net>bridge</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in \"host\" mode -->\n      <id>host</id>\n      <properties>\n        <box1.net>host</box1.net>\n        <box2.net>host</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images with no networking -->\n      <id>none</id>\n      <properties>\n        <box1.net>none</box1.net>\n        <box2.net>none</box2.net>\n      </properties>\n    </profile>\n\n    <profile>\n      <!-- Both images in a custom network 'test-network' which needs to be created beforehand -->\n      <id>custom</id>\n      <properties>\n        <box1.net>test-network</box1.net>\n        <box2.net>test-network</box2.net>\n      </properties>\n    </profile>\n\n  </profiles>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <watchInterval>500</watchInterval>\n          <logDate>default</logDate>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <images>\n            <image>\n              <alias>box1</alias>\n              <name>busybox</name>\n              <run>\n                <namingStrategy>alias</namingStrategy>\n                <net>${box1.net}</net>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>ip address | grep \"inet \"; echo \"finish\"; tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <log>\n                  <prefix>1</prefix> <color>cyan</color>\n                </log>\n                <wait>\n                  <log>finish</log>\n                </wait>\n              </run>\n            </image>\n            <image>\n              <alias>box2</alias>\n              <name>busybox</name>\n              <run>\n                <net>${box2.net}</net>\n                <namingStrategy>alias</namingStrategy>\n                <cmd>\n                  <exec>\n                    <args>sh</args>\n                    <args>-c</args>\n                    <args>ip address | grep \"inet \"; echo \"finish\"; tail -f /dev/null</args>\n                  </exec>\n                </cmd>\n                <log>\n                  <prefix>2</prefix> <color>blue</color>\n                </log>\n                <wait>\n                  <log>finish</log>\n                </wait>\n              </run>\n            </image>\n          </images>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Integration test demo which makes some small tests for Jolokia\n\n  Call it with: 'mvn verify'\n\n  The test does the following:\n\n  * Creates a Docker data container with 'jolokia.war' and 'jolokia-it.war' as described in\n    assembly descriptor src/main/docker-assembly.xml\n  * Starts (and optionally pull) the jolokia/tomcat-7.0 container with the data container linked to it\n  * Waits until Tomcat is up (i.e. until it is reachable via an HTTP request)\n  * Runs an integration test via maven-failsafe-plugin, using rest-assured for accessing the deployed app.\n  * Prints out some version information about the contaner running (in order prove that's not a fake ;-)\n  * Stops and removes the containers.\n\n  -->\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>dmp-sample-parent</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>pom</packaging>\n\n  <url>http://www.jolokia.org</url>\n\n  <!-- Should this reference the outer directory as a parent pom instead?  -->\n  <properties>\n    <docker.maven.plugin.version>${project.version}</docker.maven.plugin.version>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n  </properties>\n\n  <build>\n    <pluginManagement>\n      <plugins>\n        <plugin>\n          <groupId>io.fabric8</groupId>\n          <artifactId>docker-maven-plugin</artifactId>\n          <version>${docker.maven.plugin.version}</version>\n        </plugin>\n      </plugins>\n    </pluginManagement>\n  </build>\n\n  <modules>\n    <module>net</module>\n    <module>custom-net</module>\n    <module>volume</module>\n    <module>properties</module>\n    <module>dockerignore</module>\n    <module>docker-compose</module>\n    <module>data-jolokia</module>\n    <module>smallest</module>\n    <module>zero-config</module>\n    <module>healthcheck</module>\n    <module>multi-wait</module>\n    <module>helloworld</module>\n    <module>dockerfile</module>\n    <module>cargo-jolokia</module>\n    <module>log</module>\n    <module>run-java</module>\n    <module>spring-boot-with-jib</module>\n  </modules>\n</project>\n"
  },
  {
    "path": "samples/properties/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for showing a vanilla Dockerfile\n  usage from the top-level directory\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-properties</artifactId>\n  <version>0.34-SNAPSHOT</version>\n  <packaging>docker-build</packaging>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <extensions>true</extensions>\n        <configuration>\n          <images>\n            <image>\n              <external>\n                <type>properties</type>\n                <prefix>postgres.docker</prefix>\n              </external>\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n  <properties>\n    <postgres.docker.name>postgres:9.5.2</postgres.docker.name>\n    <postgres.docker.log.prefix>postgres</postgres.docker.log.prefix>\n    <postgres.docker.ports.1>${itest.postgres.port}:5432</postgres.docker.ports.1>\n    <postgres.docker.env.POSTGRES_DB>localhost</postgres.docker.env.POSTGRES_DB>\n    <postgres.docker.envRun.POSTGRES_USER>superuser</postgres.docker.envRun.POSTGRES_USER>\n    <postgres.docker.envRun.POSTGRES_PASSWORD>superuser-password</postgres.docker.envRun.POSTGRES_PASSWORD>\n    <postgres.docker.wait.time>10000</postgres.docker.wait.time>\n    <postgres.docker.wait.log>PostgreSQL init process complete</postgres.docker.wait.log>\n  </properties>\n</project>\n"
  },
  {
    "path": "samples/run-java/Dockerfile",
    "content": "FROM openjdk:jre\n\nADD target/${project.build.finalName}.jar /opt/hello-world.jar\nADD target/docker-extra/run-java/run-java.sh /opt\n\n# See https://github.com/fabric8io-images/run-java-sh/ for more information\n# about run-java.sh\nCMD JAVA_MAIN_CLASS=HelloWorld sh /opt/run-java.sh\n\n"
  },
  {
    "path": "samples/run-java/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>dmp-sample-run-java</artifactId>\n  <packaging>jar</packaging>\n  <version>0.34-SNAPSHOT</version>\n\n  <build>\n\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <dependencies>\n          <dependency>\n            <groupId>io.fabric8</groupId>\n            <artifactId>run-java-sh</artifactId>\n            <version>1.2.2</version>\n          </dependency>\n        </dependencies>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/run-java/src/main/java/HelloWorld.java",
    "content": "public class HelloWorld {\n\n    public static void main(String[] args) {\n        System.out.println(\"Hello world !\");\n    }\n}\n"
  },
  {
    "path": "samples/smallest/Dockerfile",
    "content": "FROM busybox\nCMD [\"echo\", \"Hello\", \"world!\"]\n"
  },
  {
    "path": "samples/smallest/pom.xml",
    "content": "<project>\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <groupId>fabric8io</groupId>\n  <artifactId>dmp-sample-smallest</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "samples/spring-boot-with-jib/README.md",
    "content": "# Spring Boot Sample with JIB Build Mode\n\nThis is also a Spring Boot application to demonstrate how Docker Maven Plugin handles build workflows by \nintegrating with [JIB](https://github.com/GoogleContainerTools/jib) which makes Docker Maven Plugin independent of docker\ndaemon.\n\n### How to Build?\nYou can compile project as usual by issuing a simple `mvn clean install` command.\n\n## Standard Configuration\nYou can build images in JIB mode by just setting `docker.build.jib=true`. Plugin would switch to JIB Build and build your image tarball into project's output directory. You can either copy this tarball and load into some docker daemon or push your image to some registry. I will be proceeding with building image and pushing to docker hub. So I would modify `pom.xml` properties to set `docker.user` to my dockerhub username:\n```.xml\n <docker.user>exampleuser</docker.user>\n```\n\n### Standard Configuration Build:\n\nYou can see that my local docker daemon is not running:\n```.shell script\n# Check Docker Daemon Status\nspring-boot-with-jib : $ systemctl status docker\n● docker.service - Docker Application Container Engine\n     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)\n     Active: inactive (dead) since Sat 2020-08-29 16:55:32 IST; 3min 2s ago\nTriggeredBy: ● docker.socket\n       Docs: https://docs.docker.com\n   Main PID: 4631 (code=exited, status=0/SUCCESS)\n\nAug 29 16:49:28 localhost.localdomain dockerd[4639]: time=\"2020-08-29T16:49:28.401304638+05:30\" level=info msg=\"shim reaped\" id=3c5e5b5555fdd8a020bf248d1a7e09d1578de62c0130>\nAug 29 16:49:28 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:49:28.410926927+05:30\" level=info msg=\"ignoring event\" module=libcontainerd namespace=moby topic=/t>\nAug 29 16:55:31 localhost.localdomain systemd[1]: Stopping Docker Application Container Engine...\nAug 29 16:55:31 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:55:31.170170702+05:30\" level=info msg=\"Processing signal 'terminated'\"\nAug 29 16:55:31 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:55:31.174848312+05:30\" level=info msg=\"Daemon shutdown complete\"\nAug 29 16:55:31 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:55:31.174895349+05:30\" level=info msg=\"stopping event stream following graceful shutdown\" error=\"co>\nAug 29 16:55:31 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:55:31.175137692+05:30\" level=info msg=\"stopping healthcheck following graceful shutdown\" module=lib>\nAug 29 16:55:31 localhost.localdomain dockerd[4631]: time=\"2020-08-29T16:55:31.175219226+05:30\" level=info msg=\"stopping event stream following graceful shutdown\" error=\"co>\nAug 29 16:55:32 localhost.localdomain systemd[1]: docker.service: Succeeded.\nAug 29 16:55:32 localhost.localdomain systemd[1]: Stopped Docker Application Container Engine.\nspring-boot-with-jib : $ \n\n```\nTo build project issue this command:\n> mvn docker:build\n```.text\n[INFO] Scanning for projects...\n[INFO] \n[INFO] ---------< io.fabric8.dmp.samples:dmp-sample-spring-boot-jib >----------\n[INFO] Building Docker Maven Plugin :: Spring Boot JIB 0.33-SNAPSHOT\n[INFO] --------------------------------[ jar ]---------------------------------\n[INFO] \n[INFO] --- docker-maven-plugin:0.33-SNAPSHOT:build (default-cli) @ dmp-sample-spring-boot-jib ---\n[INFO] DOCKER> Building Container image with JIB(Java Image Builder) mode\n[INFO] DOCKER> JIB image build started\n[INFO] DOCKER> Preparing assembly files\n[INFO] Copying files to /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/build/maven\n[INFO] Building tar: /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/tmp/docker-build.tar\n[WARNING] Cannot include project artifact: io.fabric8.dmp.samples:dmp-sample-spring-boot-jib:jar:0.33-SNAPSHOT; it doesn't have an associated file or directory.\n[WARNING] The following patterns were never triggered in this artifact inclusion filter:\no  'io.fabric8.dmp.samples:dmp-sample-spring-boot-jib'\nJIB> Base image 'fabric8/java-centos-openjdk8-jdk:1.5.6' does not use a specific image digest - build may not be reproducible\nJIB> Containerizing application with the following files:                                                                    \nJIB> \t:                                                                                                                      \nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/build/maven\nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/build/maven/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar\nJIB> \t:                                                                                                                      \nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/build/Dockerfile\nJIB> Getting manifest for base image fabric8/java-centos-openjdk8-jdk:1.5.6...                                               \nJIB> Building  layer...                                                                                                      \nJIB> Building  layer...                                                                                                      \nJIB> The base image requires auth. Trying again for fabric8/java-centos-openjdk8-jdk:1.5.6...                                \nJIB> Retrieving registry credentials for registry-1.docker.io...                                                             \nJIB> Using base image with digest: sha256:92530aa1eb4c49e3b1d033f94e9cd4dc891d49922459e13f84e59c9d68d800eb                   \nJIB> Container program arguments set to [java, -jar, /maven/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar]                    \nJIB> Building image to tar file...                                                                                           \nJIB> [========================      ] 80.0% complete > writing to tar file\nJIB> [==============================] 100.0% complete\n[INFO] DOCKER>  /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/tmp/docker-build.tar successfully built\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time:  13.587 s\n[INFO] Finished at: 2020-08-29T17:08:10+05:30\n[INFO] ------------------------------------------------------------------------\n```\n\nJIB build creates a tarball as image output(You can see in logs that it shows the build image tarball). You can then load this image into some other docker daemon like this:\n```\nspring-boot-with-jib : $ docker load -i /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-dmp-sample-jib/tmp/docker-build.tar\nf38418c08d5d: Loading layer [==================================================>]  17.85MB/17.85MB\nf1ef686aa9e0: Loading layer [==================================================>]     215B/215B\nLoaded image: rohankanojia/spring-boot-dmp-sample-jib:latest\n```\n\n**Note**: Make sure that `docker.build.jib` is disabled when you're using other plugin goals. Jib mode does not work with other goals. \nAfter this, you can use [`docker:start`](http://dmp.fabric8.io/#docker:start) as usual\n\nOr you may want to push image to some registry as described in next section.\n### Zero Configuration Push\nIn order to push image, you need to make sure that your image name is set with respect to your registry. I'm going to push the image to docker hub so `${docker.user}/spring-boot-dmp-sample-jib` would be my image name.\n\nYou can push image with [`docker:push`](http://dmp.fabric8.io/#docker:push) goal as usual, I already have `docker.build.jib=true` set in project properties:\n```\nspring-boot-with-jib : $ mvn docker:push\n[INFO] Scanning for projects...\n[INFO] \n[INFO] ---------< io.fabric8.dmp.samples:dmp-sample-spring-boot-jib >----------\n[INFO] Building Docker Maven Plugin :: Spring Boot JIB 0.33-SNAPSHOT\n[INFO] --------------------------------[ jar ]---------------------------------\n[INFO] \n[INFO] --- docker-maven-plugin:0.33-SNAPSHOT:push (default-cli) @ dmp-sample-spring-boot-jib ---\n[INFO] DOCKER> Pushing Container image with JIB(Java Image Builder) mode\n[INFO] DOCKER> This push refers to: rohankanojia/spring-boot-dmp-sample-jib\nJIB> Containerizing application with the following files:                                                                    \nJIB> Retrieving registry credentials for registry-1.docker.io...                                                             \nJIB> Container program arguments set to [java, -jar, /maven/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar] (inherited from base image)\nJIB> Pushing manifest for latest...                                                                                          \n\nJIB> [==============================] 100.0% complete\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time:  02:57 min\n[INFO] Finished at: 2020-08-29T17:36:11+05:30\n[INFO] ------------------------------------------------------------------------\nspring-boot-with-jib : $ \n```\n\n## JIB with Customized Assembly\nThis profile tries to add some extra files inside the image. If you see there is an extra directory `static` in project base directory. It is the copied to target image.\n\n### JIB with Customized Assembly Build\nNow to build you need to issue same build goal but with different profile, build goal generates a tarball which needs to be loaded into your docker daemon afterwards. Or maybe you can push it to some registry:\n>  mvn docker:build -PJib-With-Assembly\n```\nspring-boot-with-jib : $ mvn docker:build -PJib-With-Assembly\n[INFO] Scanning for projects...\n[INFO] \n[INFO] ---------< io.fabric8.dmp.samples:dmp-sample-spring-boot-jib >----------\n[INFO] Building Docker Maven Plugin :: Spring Boot JIB 0.33-SNAPSHOT\n[INFO] --------------------------------[ jar ]---------------------------------\n[INFO] \n[INFO] --- docker-maven-plugin:0.33-SNAPSHOT:build (default-cli) @ dmp-sample-spring-boot-jib ---\n[INFO] DOCKER> Building Container image with JIB(Java Image Builder) mode\n[INFO] DOCKER> JIB image build started\n[INFO] DOCKER> Preparing assembly files\n[INFO] Copying files to /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/my-project-assembly\nJIB> Base image 'fabric8/java-centos-openjdk8-jdk:1.5.6' does not use a specific image digest - build may not be reproducible\nJIB> Containerizing application with the following files:                                                                    \nJIB> \t:                                                                                                                      \nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/my-project-assembly\nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/my-project-assembly/static\nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/my-project-assembly/static/testFile.txt\nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/my-project-assembly/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar\nJIB> \t:                                                                                                                      \nJIB> \t\t/home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/build/Dockerfile\nJIB> Getting manifest for base image fabric8/java-centos-openjdk8-jdk:1.5.6...                                               \nJIB> Building  layer...                                                                                                      \nJIB> Building  layer...                                                                                                      \nJIB> The base image requires auth. Trying again for fabric8/java-centos-openjdk8-jdk:1.5.6...                                \nJIB> Retrieving registry credentials for registry-1.docker.io...                                                             \nJIB> Using base image with digest: sha256:92530aa1eb4c49e3b1d033f94e9cd4dc891d49922459e13f84e59c9d68d800eb                   \nJIB> Container program arguments set to [java, -jar, /my-project-assembly/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar]      \nJIB> Building image to tar file...                                                                                           \nJIB> [========================      ] 80.0% complete > writing to tar file\nJIB> [==============================] 100.0% complete\n[INFO] DOCKER>  /home/rohaan/work/repos/docker-maven-plugin/samples/spring-boot-with-jib/target/docker/rohankanojia/spring-boot-sample/tmp/docker-build.tar successfully built\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time:  13.173 s\n[INFO] Finished at: 2020-08-29T17:40:13+05:30\n[INFO] ------------------------------------------------------------------------\n```\n\n### Jib With Customized Assembly Push\nPushing image to docker hub in this case, you can provide registry credentials in plugin XML config, in `~/.m2/settings.xml` or in `~/.docker/config.json`:\n> mvn docker:push -PJib-With-Assembly \n```\nspring-boot-with-jib : $ mvn docker:push -PJib-With-Assembly\n[INFO] Scanning for projects...\n[INFO] \n[INFO] ---------< io.fabric8.dmp.samples:dmp-sample-spring-boot-jib >----------\n[INFO] Building Docker Maven Plugin :: Spring Boot JIB 0.33-SNAPSHOT\n[INFO] --------------------------------[ jar ]---------------------------------\n[INFO] \n[INFO] --- docker-maven-plugin:0.33-SNAPSHOT:push (default-cli) @ dmp-sample-spring-boot-jib ---\n[INFO] DOCKER> Pushing Container image with JIB(Java Image Builder) mode\n[INFO] DOCKER> This push refers to: rohankanojia/spring-boot-sample\nJIB> Containerizing application with the following files:                                                                    \nJIB> Retrieving registry credentials for registry-1.docker.io...                                                             \nJIB> Container program arguments set to [java, -jar, /my-project-assembly/dmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar] (inherited from base image)\nJIB> [====================          ] 66.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:707713df6baa24192428099d6444fed7fe3c91dc10bad2742b7350f0147b1b3b, size: 8851\nJIB> [====================          ] 67.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:109b828942fdee847c02ec43b13105bf0d1bc1701675a4306781193f11c997df, size: 6543\nJIB> [=====================         ] 68.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:a44f5f69f08a44895565ad9cdfa05f3378a46205cf81c64de068efed4f644a46, size: 467\nJIB> Skipping push; BLOB already exists on target registry : digest: sha256:8e2fef958d6c5532e73960cab9b4acf1de2fa0dac8ce1d404751de240157e5cd, size: 739790\nJIB> [=====================         ] 70.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:743ce65d298dc1684e06ba2d255db095cceaf75060dcbdff43baa8a7e525255a, size: 100\nJIB> [=====================         ] 69.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b, size: 75161332\nJIB> [======================        ] 72.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> [======================        ] 71.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:6b82b8ee52d8ef43d1b35637e65d9134277e63fd23316516c084ab0dad7a017b, size: 1375\nJIB> [======================        ] 73.7% complete > pushing blob sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6JIB> Skipping push; BLOB already exists on target registry : digest: sha256:0a2b4dc56d7e822495adcdfa8a2c2316e9ae420d84a6a5e8e9d0b505a5fddcd1, size: 6539\nJIB> [======================        ] 74.7% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> Skipping push; BLOB already exists on target registry : digest: sha256:08f3d26bbf994fd2c8ce6418a0b090bee22878c7998f0e78b7247a860411bbb2, size: 86409418\nJIB> [=======================       ] 75.8% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> Skipping push; BLOB already exists on target registry : digest: sha256:99230c1ff85faaf8fd0c9ba8db6b20d009db4fb35f17f7b6a24a6baa6821c306, size: 4702\nJIB> [==========================    ] 86.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> Skipping push; BLOB already exists on target registry : digest: sha256:18e8deddb3f584f36c47b481067a6fa0a3a977b71df199a8d89ee8720b9c587c, size: 239\nJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 87.9% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.0% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.0% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.0% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.1% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.1% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.2% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.3% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [==========================    ] 88.3% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.4% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.5% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.6% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.7% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.8% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> [===========================   ] 88.8% complete > pushing blob sha256:46839fd680b76fed7005bc63ae1207193c129051c10dJIB> Pushing manifest for latest...                                                                                          \n\nJIB> [==============================] 100.0% complete\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time:  29.416 s\n[INFO] Finished at: 2020-08-29T17:43:57+05:30\n[INFO] ------------------------------------------------------------------------\n```\n\nYou can check afterwards whether the loaded container contains the file you mentioned in assembly or not:\n```\nspring-boot-with-jib : $ docker container run -it rohankanojia/spring-boot-sample:latest /bin/bash\n[root@c402fe994291 /]# ls     \nDockerfile         bin          dev  home  lib64  mnt                  opt   root  sbin  sys  usr\nanaconda-post.log  deployments  etc  lib   media  my-project-assembly  proc  run   srv   tmp  var\n[root@c402fe994291 /]# ls my-project-assembly/\ndmp-sample-spring-boot-jib-0.33-SNAPSHOT.jar  static\n[root@c402fe994291 /]# ls my-project-assembly/static/\ntestFile.txt\n[root@c402fe994291 /]# cat my-project-assembly/static/testFile.txt \nI should be present\n[root@c402fe994291 /]# exit\nexit\n```\n\n### Registry Configuration\nYou can provide registry credentials in 3 formats:\n- You can do a `docker login`(for Docker Hub) or `docker login <your-registry` and plugin would read your `~/.docker/config.json` file\n- You can provide registry credentials in your `~/.m2/settings.xml` file like this and plugin would read it from there:\n```\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n\n  <servers>\n    <server>\n      <id>docker.io</id>\n      <username>testuser</username>\n      <password>testpassword</password>\n    </server>\n    <server>\n      <id>quay.io</id>\n      <username>testuser</username>\n      <password>testpassword</password>\n    </server>\n  </servers>\n\n</settings>\n\n```\n\n-  You can provide registry credentials as part of XML configuration:\n```\n<plugin>\n    <groupId>org.eclipse.jkube</groupId>\n    <artifactId>kubernetes-maven-plugin</artifactId>\n    <version>${project.version}</version>\n    <configuration>\n        <images>\n            <!-- Your Image Configuration -->  \n        </images>\n        <authConfig>\n          <username>testuser</username>\n          <password>testpassword</password>\n        </authConfig>\n    </configuration>\n</plugin>\n\n```\n"
  },
  {
    "path": "samples/spring-boot-with-jib/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>io.fabric8.dmp.samples</groupId>\n        <artifactId>dmp-sample-parent</artifactId>\n        <version>0.34-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>dmp-sample-spring-boot-jib</artifactId>\n    <version>0.33-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <description>Docker Maven Plugin Example with Spring Boot With Build Mode JIB</description>\n\n    <distributionManagement>\n        <snapshotRepository>\n            <id>sonatype-nexus-snapshots</id>\n            <name>Sonatype Nexus Snapshots</name>\n            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n        </snapshotRepository>\n        <repository>\n            <id>sonatype-nexus-staging</id>\n            <name>Nexus Release Repository</name>\n            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n        </repository>\n    </distributionManagement>\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <docker.build.jib>true</docker.build.jib>\n        <docker.user>fabric8</docker.user>\n        <spring.version>2.3.3.RELEASE</spring.version>\n    </properties>\n\n    <dependencies>\n\n        <!-- Boot generator  -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n            <version>${spring.version}</version>\n        </dependency>\n\n        <!-- API, java.xml.bind module -->\n        <dependency>\n            <groupId>jakarta.xml.bind</groupId>\n            <artifactId>jakarta.xml.bind-api</artifactId>\n            <version>2.3.2</version>\n        </dependency>\n\n        <!-- Runtime, com.sun.xml.bind module -->\n        <dependency>\n            <groupId>org.glassfish.jaxb</groupId>\n            <artifactId>jaxb-runtime</artifactId>\n            <version>2.3.2</version>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n              <groupId>io.fabric8</groupId>\n              <artifactId>docker-maven-plugin</artifactId>\n              <version>0.33-SNAPSHOT</version>\n              <configuration>\n                <images>\n                  <image>\n                    <name>${docker.user}/spring-boot-dmp-sample-jib</name>\n                    <build>\n                      <from>fabric8/java-centos-openjdk8-jdk:1.5.6</from>\n                      <assembly>\n                        <descriptorRef>artifact</descriptorRef>\n                      </assembly>\n                      <cmd>java -jar /maven/${project.artifactId}-${project.version}.jar</cmd>\n                      <ports>\n                        <port>8080</port>\n                      </ports>\n                    </build>\n                  </image>\n                </images>\n              </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <profiles>\n        <profile>\n            <id>Jib-With-Assembly</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>io.fabric8</groupId>\n                        <artifactId>docker-maven-plugin</artifactId>\n                        <version>0.33-SNAPSHOT</version>\n                        <configuration>\n                            <images>\n                                <image>\n                                    <name>${docker.user}/spring-boot-dmp-sample-jib</name>\n                                    <build>\n                                        <from>fabric8/java-centos-openjdk8-jdk:1.5.6</from>\n                                        <assembly>\n                                            <name>my-project-assembly</name>\n                                            <inline>\n                                                <id>copy-test-file</id>\n                                                <files>\n                                                    <file>\n                                                        <source>\n                                                            ${project.basedir}/static/testFile.txt\n                                                        </source>\n                                                        <outputDirectory>static</outputDirectory>\n                                                    </file>\n                                                    <file>\n                                                        <source>${project.basedir}/target/${project.artifactId}-${project.version}.jar</source>\n                                                        <outputDirectory>.</outputDirectory>\n                                                    </file>\n                                                </files>\n                                            </inline>\n                                            <targetDir>/deployments</targetDir>\n                                        </assembly>\n                                        <cmd>java -jar /my-project-assembly/${project.artifactId}-${project.version}.jar</cmd>\n                                        <ports>\n                                          <port>8080</port>\n                                        </ports>\n                                    </build>\n                                </image>\n                            </images>\n                        </configuration>\n                    </plugin>\n\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n\n</project>\n"
  },
  {
    "path": "samples/spring-boot-with-jib/src/main/java/io/fabric8/maven/sample/springboot/jib/Application.java",
    "content": "package io.fabric8.maven.sample.springboot.jib;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "samples/spring-boot-with-jib/src/main/java/io/fabric8/maven/sample/springboot/jib/HelloController.java",
    "content": "package io.fabric8.maven.sample.springboot.jib;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n\n@RestController\npublic class HelloController {\n\n    @RequestMapping(\"/\")\n    public String index() {\n        return \"<h1>Greetings from Spring Boot(Powered by JIB)!!</h1>\";\n    }\n\n}\n"
  },
  {
    "path": "samples/spring-boot-with-jib/static/testFile.txt",
    "content": "I should be present\n"
  },
  {
    "path": "samples/test.xml",
    "content": "<c xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://fabric8.io/docker-maven-plugin/test2\"\n         xsi:schemaLocation=\"http://fabric8.io/docker-maven-plugin/test2 file:///tmp/test2.xsd\">\n  <watchMode>build</watchMode>\n  <pushRegistry>blas</pushRegistry>\n  <pushRegistry>blub</pushRegistry>\n  <images>\n    <image>\n      <build>\n\n      </build>\n      <name>Test</name>\n      <alias>blub</alias>\n      <run>\n      </run>\n      <alias></alias>\n    </image>\n  </images>\n</c>"
  },
  {
    "path": "samples/volume/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <!--\n  Sample project for demonstrating the volume creation feature\n\n\n  Call it with 'mvn docker:create-volume'.\n  or\n  Call it with 'mvn docker:verify'\n  It will create the volume \"newVolume\"\n  -->\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n  <artifactId>dmp-sample-volume</artifactId>\n  <version>0.34-SNAPSHOT</version>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <configuration>\n          <verbose>true</verbose>\n          <autoPull>always</autoPull>\n          <startParallel>true</startParallel>\n          <volumes>\n            <volume>\n              <name>newVolume</name>\n            </volume>\n          </volumes>\n        </configuration>\n        <executions>\n          <execution>\n            <id>start</id>\n            <phase>pre-integration-test</phase>\n            <goals>\n              <goal>volume-create</goal>\n              <goal>start</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>stop</id>\n            <phase>post-integration-test</phase>\n            <goals>\n              <goal>stop</goal>\n              <goal>volume-remove</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/zero-config/Dockerfile",
    "content": "FROM openjdk:jre\n\nARG jar_file=target/dmp-sample-zero-config.jar\n\nARG FOO=${}\nADD $jar_file /tmp/zero-config.jar\nCMD java -cp /tmp/zero-config.jar HelloWorld\n"
  },
  {
    "path": "samples/zero-config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>io.fabric8.dmp.samples</groupId>\n    <artifactId>dmp-sample-parent</artifactId>\n    <version>0.34-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n\n\n  <groupId>io.fabric8.dmp.samples</groupId>\n  <artifactId>dmp-sample-zero-config</artifactId>\n  <packaging>jar</packaging>\n  <version>0.34-SNAPSHOT</version>\n\n  <properties>\n    <jar_file>${project.build.directory}/${project.build.finalName}.jar</jar_file>\n    <maven.compiler.source>1.8</maven.compiler.source>\n    <maven.compiler.target>1.8</maven.compiler.target>\n  </properties>\n\n  <build>\n    <finalName>dmp-sample-zero-config</finalName>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "samples/zero-config/src/main/java/HelloWorld.java",
    "content": "public class HelloWorld {\n\n    public static void main(String[] args) {\n        System.out.println(\"Hello world !\");\n    }\n}\n"
  },
  {
    "path": "src/main/asciidoc/inc/_authentication.adoc",
    "content": "\n[[authentication]]\n= Authentication\n\nWhen pulling (via the `autoPull` mode of `{plugin}:start`) or pushing image, it\nmight be necessary to authenticate against a Docker registry.\n\nThere are six different locations searched for credentials.  In order, these are:\n\n* Providing system properties `docker.username` and `docker.password` from the outside.\n* Providing system properties `registry.username` and `registry.password` from the outside.\n* Using a `<authConfig>` section in the plugin configuration with `<username>` and `<password>` elements.\n* Using OpenShift configuration in `~/.config/kube`\n* Using a `<server>` configuration in `~/.m2/settings.xml`\n* Login into a registry with `docker login` (credentials in a credential helper or in `~/.docker/config.json`)\n\nUsing the username and password directly in the `pom.xml` is not\nrecommended since this is widely visible. This is easiest and\ntransparent way, though. Using an `<authConfig>` is straight forward:\n\n[source,xml]\n----\n<plugin>\n  <configuration>\n    <image>consol/tomcat-7.0</image>\n    ...\n    <authConfig>\n      <username>jolokia</username>\n      <password>s!cr!t</password>\n    </authConfig>\n  </configuration>\n</plugin>\n----\n\nThe system property provided credentials are a good compromise when\nusing CI servers like Jenkins. You simply provide the credentials from\nthe outside:\n\n.Example\n[source, sh, subs=\"+attributes\"]\n----\nmvn -Ddocker.username=jolokia -Ddocker.password=s!cr!t {plugin}:push\n----\n\nThe most _mavenish_ way is to add a server to the Maven settings file `~/.m2/settings.xml`:\n\n.Example\n[source,xml]\n----\n<servers>\n  <server>\n    <id>docker.io</id>\n    <username>jolokia</username>\n    <password>s!cr!t</password>\n  </server>\n  ....\n</servers>\n----\n\nThe server id must specify the registry to push to/pull from, which by\ndefault is central index `docker.io` (or `index.docker.io` / `registry.hub.docker.com` as fallbacks).\nHere you should add your docker.io account for your repositories. If you have multiple accounts\nfor the same registry, the second user can be specified as part of the ID. In the example above, if you\nhave a second account 'fabric8io' then use an `<id>docker.io/fabric8io</id>` for this second entry. I.e. add the\nusername with a slash to the id name. The default without username is only taken if no server entry with\na username appended id is chosen.\n\nThe most _secure_ way is to rely on docker's credential store or credential helper and read confidential information\nfrom an external credentials store, such as the native keychain of the operating system. Follow the instruction on\nhttps://docs.docker.com/engine/reference/commandline/login/#credentials-store[the docker login documentation].\n\nAs a final fallback, this plugin consults `$DOCKER_CONFIG/config.json` if `DOCKER_CONFIG` is set, or `~/.docker/config.json` if not, and reads credentials stored directly within this\nfile. This unsafe behavior happened when connecting to a registry with the command `docker login` from the command line\nwith older versions of docker (pre 1.13.0) or when docker is not configured to use a\nhttps://docs.docker.com/engine/reference/commandline/login/#credentials-store[credential store].\n\n== Pull vs. Push Authentication\n\nThe credentials lookup described above is valid for both push and\npull operations. In order to narrow things down, credentials can be\nprovided for pull or push operations alone:\n\nIn an `<authConfig>` section a sub-section `<pull>` and/or `<push>`\ncan be added. In the example below the credentials provider are only\nused for image push operations:\n\n.Example\n[source,xml]\n----\n<plugin>\n  <configuration>\n    <image>consol/tomcat-7.0</image>\n    ...\n    <authConfig>\n      <push>\n         <username>jolokia</username>\n         <password>s!cr!t</password>\n      </push>\n    </authConfig>\n  </configuration>\n</plugin>\n----\n\nWhen the credentials are given on the command line as system\nproperties, then the properties `docker.pull.username` /\n`docker.pull.password` and `docker.push.username` /\n`docker.push.password` are used for pull and push operations,\nrespectively (when given). Either way, the standard lookup algorithm\nas described in the previous section is used as fallback.\n\n== OpenShift Authentication\n\nWhen working with the default registry in OpenShift, the credentials\nto authenticate are the OpenShift username and access token. So, a\ntypical interaction with the OpenShift registry from the outside is:\n\n----\noc login\n...\nmvn -Ddocker.registry=docker-registry.domain.com:80/default/myimage \\\n    -Ddocker.username=$(oc whoami) \\\n    -Ddocker.password=$(oc whoami -t)\n----\n\n(note, that the image's username part (\"default\" here\") must\ncorrespond to an OpenShift project with the same name to which you\ncurrently connected account has access).\n\nThis can be simplified by using the system property\n`docker.useOpenShiftAuth` in which case the plugin does the\nlookup. The equivalent to the example above is\n\n----\noc login\n...\nmvn -Ddocker.registry=docker-registry.domain.com:80/default/myimage \\\n    -Ddocker.useOpenShiftAuth\n----\n\nAlternatively the configuration option `<useOpenShiftAuth>` can be\nadded to the `<authConfig>` section.\n\nFor dedicated _pull_ and _push_ configuration the system properties\n`docker.pull.useOpenShiftAuth` and `docker.push.useOpenShiftAuth` are\navailable as well as the configuration option `<useOpenShiftAuth>` in\nan `<pull>` or `<push>` section within the `<authConfig>`\nconfiguration.\n\nIf `useOpenShiftAuth` is enabled then the OpenShift Konfiguration will be looked up in `$KUBECONFIG` or, if this environment variable is not set, in `~/.kube/config`.\n\n[[password-encryption]]\n== Password encryption\n\nRegardless which mode you choose you can encrypt password as described\nin the\nhttp://maven.apache.org/guides/mini/guide-encryption.html[Maven documentation]. Assuming\nthat you have setup a _master password_ in\n`~/.m2/security-settings.xml` you can create easily encrypt\npasswords:\n\n.Example\n[source,bash]\n----\n$ mvn --encrypt-password\nPassword:\n{QJ6wvuEfacMHklqsmrtrn1/ClOLqLm8hB7yUL23KOKo=}\n----\n\nThis password then can be used in `authConfig`, `docker.password`\nand/or the `<server>` setting configuration. However, putting an\nencrypted password into `authConfig` in the `pom.xml` doesn't make\nmuch sense, since this password is encrypted with an individual master\npassword.\n\n[[extended-authentication]]\n== Extended Authentication\n\nSome docker registries require additional steps to authenticate.\nlink:https://docs.aws.amazon.com/AmazonECR/latest/userguide/ECR_GetStarted.html[Amazon ECR] requires using an IAM access key to obtain temporary docker login credentials.\nThe `docker:push` and `docker:pull` goals automatically execute this exchange for any registry of the form _<awsAccountId>_ *.dkr.ecr.* _<awsRegion>_ *.amazonaws.com*, unless the `skipExtendedAuth` configuration (`docker.skip.extendedAuth` property) is set true.\n\nNote that for an ECR repository with URI `123456789012.dkr.ecr.eu-west-1.amazonaws.com/example/image` the d-m-p's `docker.registry` should be set to `123456789012.dkr.ecr.eu-west-1.amazonaws.com` and `example/image` is the `<name>` of the image.\n\nYou can use any IAM access key with the necessary permissions in any of the locations mentioned above except `~/.docker/config.json`.\nUse the IAM *Access key ID* as the username and the *Secret access key* as the password.\nIn case you're using temporary security credentials provided by the AWS Security Token Service (AWS STS), you have to provide the *security token* as well.\nTo do so, either specify the `docker.auth` system property or provide an `<auth>` element alongside username & password in the `authConfig`.\n\nd-m-p will attempt to read AWS credentials from some well-known spots in case there is no explicit configuration:\n\n* it will pick up ENV variables link:https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html[as documented for the AWS CLI]\n\n* it will pick up temporary credentials of link:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html[the IAM role of an EC2 instance]\n\n* it will pick up temporary credentials of link:https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html[the IAM role of a fargate task (OR ECS with EC2 with ECS_AWSVPC_BLOCK_IMDS as \"true\")]\n\nIf any of these authentication information is accessible, it will be used.\n\n[NOTE]\n====\nFor a more complete, robust and reliable authentication experience, you can add the AWS SDK for Java as a dependency.\n\n[source,xml]\n----\n<plugins>\n    <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <dependencies>\n            <dependency>\n                <groupId>com.amazonaws</groupId>\n                <artifactId>aws-java-sdk-core</artifactId>\n                <version>1.11.707</version>\n            </dependency>\n        </dependencies>\n    </plugin>\n</plugins>\n----\n\nThis extra dependency allows the usage of all link:https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html[options] that the AWS default credential provider chain provides.\n\nIf the AWS SDK is found in the classpath, it takes precedence over the custom AWS credentials lookup mechanisms listed above.\n====\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-build.adoc",
    "content": "\n[[docker:build]]\n== *docker:build*\n\nThis goal will build all images which have a `<build>` configuration section, or, if the global configuration variable `filter` (property:\n`docker.filter`) is set, only the images contained in this variable (comma separated) will be built.\n\ninclude::build/_overview.adoc[]\n\n\n[[build-configuration]]\n=== Configuration\n\ninclude::build/_configuration.adoc[]\n\n[[build-assembly]]\n=== Assembly\n\ninclude::build/_assembly.adoc[]\n\n[[misc-startup]]\n=== Startup Arguments\n\ninclude::misc/_startup.adoc[]\n\n[[build-buildargs]]\n=== Build Args\n\ninclude::build/_buildargs.adoc[]\n\n[[build-healthcheck]]\n=== Healthcheck\n\ninclude::build/_healthcheck.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-logs.adoc",
    "content": "\n[[docker:logs]]\n== *docker:logs*\n\nWith this goal it is possible to print out the logs of containers\nstarted from images configured in this plugin. By default only the\nlatest container started is printed, but this can be changed with a\nproperty. The format of the log output is influenced by run\nconfiguration of the configured images. The following system\nproperties can the behaviour of this goal:\n\n.Logging options\n[cols=\"1,5\"]\n|===\n| Property | Description\n\n| *docker.logAll*\n| If set to `true` the logs of all containers created from images configured for this plugin are printed. The container id is then prefixed before every log line. These images can contain many containers which are already stopped. It is probably a better idea to use `docker logs` diretly from the command line.\n\n| *docker.follow*\n| If given will wait for subsequent log output until CRTL-C is pressed. This is similar to the behaviour of `docker logs -f` (or `tail -f`).\n\n| *docker.filter*\n| Filter to restrict the set of images for which log should be fetched. This can be a comma separated list of image or alias names.\n\n| *docker.logDate*\n| Date format to use. See \"<<start-logging,Logging>>\" for available formats.\n|===\n\n.Example\n[source, sh, subs=\"+attributes\"]\n----\n$ mvn {plugin}:logs -Ddocker.follow -Ddocker.logDate=DEFAULT\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-push.adoc",
    "content": "\n[[docker:push]]\n== *docker:push*\n\ninclude::push/_overview.adoc[]\n\ninclude::push/_configuration.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-remove.adoc",
    "content": "\n[[docker:remove]]\n== *docker:remove*\n\nThis goal can be used to clean up images. By default all images with a build configuration are removed.\nYou can tune this by setting the property `removeMode` (property: `docker.removeMode`) to one of the following values:\n\n.removeMode Values\n[cols=\"1,5\"]\n|===\n| Value | Description\n\n| `build`\n| All images with a <<build-configuration,build configuration>>\n\n| `run`\n| All images without a <<start-configuration,build configuration>>\n\n| `all`\n| All configured images\n\n| `data`\n| All data images, which are images without a <<start-configuration,run configuration>>.\n|===\n\nPreviously, this could be tuned also by providing the property `removeAll` which indicates to remove all images managed by this build. Otherwise only data images were delete before 0.24.0. `removeAll` is deprecated and will be removed soone. Please use `removeMode` instead.\n\nAs with the other goals, the configuration `image` can be used to tune the images to remove. All containers belonging to the images are removed as well as the all tags assigned to this image\n\nConsidering three images 'db','tomcat' and 'data' where 'data' is the only image with a build configuration:\n\n* `mvn {plugin}:remove` will remove 'data'\n* `mvn -Ddocker.removeMode=all {plugin}:remove` will remove all three images\n* `mvn -Ddocker.filter=data,tomcat {plugin}:remove` will remove 'data'\n* `mvn -Ddocker.filter=data,tomcat -Ddocker.removeMode=all {plugin}:remove` will remove 'data' and 'tomcat'\n\n.Remove options\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| *skipTag*\n| If set to `true` this plugin won't remove any tags\n| `docker.skip.tag`\n\n| *removeNamePattern*\n| If a list of <<name-patterns, name patterns>> is provided, any images matching the patterns will be removed,\nindependently of whether there is an <<image-configuration, image configuration>> marked for removal.\n| `docker.removeNamePattern`\n|==="
  },
  {
    "path": "src/main/asciidoc/inc/_docker-save.adoc",
    "content": "\n[[docker:save]]\n== *docker:save*\n\nThe `{plugin}:save` target saves an image defined in the build configuration to a local file, analogous to `docker save`.\nIf the option `saveFile` is not set, the file name is calculated automatically:\n\n* If `saveAlias` is used then the file is stored as `target/<alias>-<project version>.tar.gz`\n* Otherwise the archive is stored as `target/<image name without registry and user part>-<image tag>.tar.gz`\n\nPlease note that the exported image contains all image layers and can be quite large (also, it takes a bit to export the image).\n\n.Controlling image compression\nThe file name extension is used to select a compression method for the output.\n[cols=\"3,2,1\"]\n|===\n| Extensions | Compression | Type\n\n| .tar or unrecognized\n| No compression\n| .tar\n\n| .tar.gz, .tgz\n| GZIP compression\n| .tar.gz\n\n| .tar.bz, .tar.bz2, .tar.bzip2\n| BZIP2 compression\n| .tar.bz\n\n|===\n\n.Attaching the saved image as an artifact\nIf `saveClassifier` is set, the saved archive will be attached to the project using the provided classifier and the type determined from the file name. The placeholder `%a` will be replaced with the image alias.\n\nNote that using overriding the default to use `docker` or `docker-%a` may lead to a conflict if a source archive is also attached with <<{plugin}:source>>.\n\n.Save options\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| *saveName*\n| The name of the image configuration to save. Must not be used together with `alias`.\n| `docker.save.name`\n\n| *saveAlias*\n| The alias of the image configuration to save. Must not be used together with `name`.\n| `docker.save.alias`\n\n| *saveFile*\n| The filename to save.\n| `docker.save.file` or `docker.file` or `file`\n\n| *saveClassifier*\n| If set, attach the the saved archive to the project with the provided classifier. A placeholder of `%a` will be replaced with the image alias.\n| `docker.save.classifier`\n\n| *skipSave*\n| A boolean flag whether to skip execution of the goal.\n| `docker.skip.save`\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-source.adoc",
    "content": "\n[[docker:source]]\n== *docker:source*\n\nThe `{plugin}:source` target can be used to attach a docker build archive containing the Dockerfile and all added files to the Maven project with a certain classifier. It reuses the configuration from <<{plugin}:build>>.\n\nBy default, only the first image configuration is used for creating the source archive.\nYou can export all image configurations by setting the `sourceMode` configuration to `all`:\n\n.Export all image configs\n[source,xml]\n----\n<plugin>\n  <artifactId>docker-maven-plugin</artifactId>\n  <configuration>\n    <!-- source mode can be \"first\" or \"all\" -->\n    <sourceMode>all</sourceMode>\n    <!-- .... -->\n  </configuration>\n</plugin>\n----\n\nFor exporting all image configurations, `{plugin}:source` uses the image's link:image-configuration.md[alias] as part of the classifier, so it is mandatory that the alias is set for\nthis goal to work when all images should be exported this way.\nThe classifier is calculated as `docker-<alias>` so when the alias is set to `service`, then the classifier is `docker-service`.\n\nIf you only export the first image configuration (which is the default), then the classifier is just `docker` (without alias).\n\n`{plugin}:source` can be attached to a Maven execution phase, which is `generate-sources` by default.\n\nFor example, this configuration will attach the docker build archive to the artifacts to store in the repository:\n\n.Example\n[source,xml]\n----\n<plugin>\n  <artifactId>docker-maven-plugin</artifactId>\n  <!-- ..... -->\n  <executions>\n     <execution>\n       <id>sources</id>\n       <goals>\n         <goal>source</goal>\n       </goals>\n     </execution>\n  </executions>\n</plugin>\n----\n\nIf not bound to an execution phase, `docker:source` requires that the artifact has been created so you call it best together with `package`\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-start.adoc",
    "content": "\n[[docker:start]]\n== *docker:start*\n\ninclude::start/_overview.adoc[]\n\n[[start-configuration]]\n=== Configuration\n\ninclude::start/_configuration.adoc[]\n\n[[misc-env]]\n=== Environment and Labels\n\ninclude::misc/_env.adoc[]\n\n[[start-port-mapping]]\n=== Port Mapping\n\ninclude::start/_port-mapping.adoc[]\n\n[[start-links]]\n=== Links\n\ninclude::start/_links.adoc[]\n\n[[network-configuration]]\n=== Network\n\ninclude::start/_network.adoc[]\n\n[[start-depends-on]]\n=== Depends-On\n\ninclude::start/_depends-on.adoc[]\n\n[[start-restart]]\n=== Restart Policy\n\ninclude::start/_restart.adoc[]\n\n[[start-volumes]]\n=== Volumes\n\ninclude::start/_volumes.adoc[]\n\n[[start-wait]]\n=== Wait\n\ninclude::start/_wait.adoc[]\n\n[[start-logging]]\n=== Logging\n\ninclude::start/_logging.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-stop.adoc",
    "content": "\n[[docker:stop]]\n== *docker:stop*\n\nStops and removes a docker container. This goal stops every container started with `<{plugin}:start>` either during the same build (e.g. when bound to lifecycle phases when doing integration tests) or for containers created by a previous call to `<{plugin}:start>`\n\nIf called within the same build run, only the containers that were explicitly started during the run will be stopped. Existing containers started using `{plugin}:start` for the project will not be affected.\n\nIf called as a separate invocation, the plugin will stop and remove any container it finds whose image is defined in the project's configuration. Any existing containers found running whose image name matches but were not started by the plugin will not be affected.\n\nIn case the naming strategy for an image is `alias` (i.e. the container name is set to the given alias), then only the container with this alias is stopped. Other containers originating from the same image are not touched.\n\nIt should be noted that any containers created prior to version `0.13.7` of the plugin may not be stopped correctly by the plugin because the label needed to tie the container to the project may not exist. Should this happen, you will need to use the Docker CLI to clean up the containers and/or use the `docker.allContainers` option listed below.\n\nFor tuning what should happen when stopping there are four global parameters which are typically used as system properties:\n`allContainers`, `keepContainer`, `keepRunning` and `removeVolumes`.\n\n.Stop configuration\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Parameter\n\n| *allContainers*\n| Stops and removes any container that matches an image defined in the current project's configuration. This was the default behavior of the plugin prior up to version 0.13.6\n| `docker.allContainers`\n\n| <<container-name, *containerNamePattern*>>\n| Default pattern that <<{plugin}:start>> uses for naming containers when they are created. See <<container-name, Container Names>> for details.\nThis should match the setting for <<{plugin}:start>> goals if the goals are configured in separate executions.\n| `docker.containerNamePattern`\n\n| *keepContainer*\n| If set to `true` not destroy container after they have been stopped. Default is false.\n| `docker.keepContainer`\n\n| *keepRunning*\n| If set to `true` actually don't stop the container. This apparently makes only sense when used on the command line when doing integration testing (i.e. calling `{plugin}:stop` during a lifecycle binding) so that the container are still running after an integration test. This is useful for analysis of the containers (e.g. by entering it with `docker exec`).\n| `docker.keepRunning`\n\n| *removeVolumes*\n| If set to `true` will remove any volumes associated to the container as well. This option will be ignored if either `keepContainer` or `keepRunning` are true.\n| `docker.removeVolumes`\n\n| *stopNamePattern*\n| If a list of <<name-patterns, name patterns>> is provided, any containers matching the patterns will be stopped and\nremoved (depending on the values of `keepContainer` and `keepRunning`),\nindependently of whether there is an <<image-configuration, image configuration>>.\n| `docker.stopNamePattern`\n|===\n\n.Example\n[source,sh]\n----\n$ mvn -Ddocker.keepRunning clean install\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-tag.adoc",
    "content": "[[docker:tag]]\n== *docker:tag*\n\nThe `{plugin}:tag` tags an image so that it becomes part of a repository. You can use it to tag an already built image. Here is an example of it's usage:\n\n[source]\n----\n~/work/repos/docker-maven-plugin/samples/zero-config : $ mvn docker:tag -Ddocker.image.tag=0.9.0\n[INFO] Scanning for projects...\n[INFO]\n[INFO] -----------< io.fabric8.dmp.samples:demp-sample-zero-config >-----------\n[INFO] Building demp-sample-zero-config 0.33-SNAPSHOT\n[INFO] --------------------------------[ jar ]---------------------------------\n[INFO]\n[INFO] --- docker-maven-plugin:0.33-SNAPSHOT:tag (default-cli) @ demp-sample-zero-config ---\n[INFO] DOCKER> Tagging image samples/demp-sample-zero-config:0.9.0 successful!\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time:  1.155 s\n[INFO] Finished at: 2020-06-27T14:05:33+05:30\n[INFO] ------------------------------------------------------------------------\n~/work/repos/docker-maven-plugin/samples/zero-config : $ docker images | grep 0.9.0\nsamples/demp-sample-zero-config                        0.9.0                       ac5c5991505d        About an hour ago   479MB\n----\n.Supported options\n[cols=\"1,2,1\"]\n|===\n| Element | Description | Property\n\n| *tagName*\n| The name of the new tag.\n| `docker.image.tag`\n\n| *repo*\n| The repository to tag in. For example, `someuser/someimage`.\n| `docker.image.repo`\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-volume-create.adoc",
    "content": "\n[[docker:volume-create]]\n== *docker:volume-create*\n\nThis goals creates one or more standalone https://docs.docker.com/engine/tutorials/dockervolumes/[Docker volume], which can be referenced in a <<docker:start>> configuration for linking to a volume during runtime.\nEach volume has therefore a unique and referenceable name. Beside the volume driver and driver options can be specified.\n\n.Example for a volume configuration\n[source,xml]\n----\n<plugin>\n  <configuration>\n    <volumes>\n       <volume>\n         <name>temp-volume</name>\n         <driver>local</driver>\n         <opts>\n           <type>tmpfs</type>\n           <device>tmpfs</device>\n           <o>size=100m,uid=1000</o>\n         </opts>\n         <labels>\n           <volatileData>true</volatileData>\n         </labels>\n       </volume>\n    </volumes>\n    ...\n  </configuration>\n</plugin>\n----\n\n[[volume-create-configuration]]\n.Configuration\n\nThe following options are available when creating volumes:\n\n.Volume configuration\n|===\n| Element | Description\n\n| *name*\n| Name of the volume\n\n| *driver*\n| Volume driver to use. By default the driver `local` is used which is created on the local file system. Please refer to your Docker installation which additional drivers are available.\n\n| *opts*\n| Driver specific options passed in as custom `<key>value</key>` where its maps to `key=value` pairs for driver options as they can be provided from the Docker CLI, too. Each volume driver supports different options. The options supported by the `local` driver are the well known http://man7.org/linux/man-pages/man8/mount.8.html[Linux mount options].\n\n|*labels*\n| Labels given as `<key>value</key>` similar to image labels described in <<misc-env, Environment and Labels>>. These labels are used to tag the volumes themselves.\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-volume-remove.adoc",
    "content": "[[docker:volume-remove]]\n== *docker:volume-remove*\n\nThis goals is the counterpart to <<docker:volume-create>> and removes a volume.\nDocker volumes are configured outside of Docker images, but can be referenced by them.\nThe configuration is the same as for <<docker:volume-create>>\n\n*Example:*\n\n[source,xml]\n----\n<plugin>\n  <configuration>\n    <volumes>\n       <volume>\n         <name>temp-volume</name>\n         ....\n       </volume>\n    </volumes>\n    ...\n  </configuration>\n</plugin>\n----\n\n[[volume-remove-configuration]]\n.Configuration\n\nThe configuration is quite simple. Only the name of the volume to delete is required.\n\n.Volume configuration\n[options=\"header\"]\n|===\n| Element | Description\n\n| *name*\n| Name of the volume\n\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/_docker-watch.adoc",
    "content": "\n[[docker:watch]]\n== *docker:watch*\n\ninclude::watch/_overview.adoc[]\n\n[[watch-configuration]]\ninclude::watch/_configuration.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_external-configuration.adoc",
    "content": "\n[[external-configuration]]\n= External Configuration\n\nFor special configuration needs, there is the possibility to get the\nruntime and build configuration from places outside the plugin's\nconfiguration. This is done with the help of `<external>`\nconfiguration sections which at least has a `<type>` subelement. This\n`<type>` element selects a specific so called \"handler\" which is\nresponsible for creating the full image configuration. A handler can\ndecide to use the `<run>` and `<build>` configuration which could\nbe provided in addition to this `<external>` section or it can decide\nto completely ignore any extra configuration option.\n\nA handler can also decide to expand this single image configuration to\na list of image configurations. The image configurations resulting\nfrom such a external configuration are added to the _regular_\n`<image>` configurations without an `<external>` section.\n\nThe available handlers are described in the following.\n\n[[property-configuration]]\n== Properties\n\ninclude::external/_property_configuration.adoc[]\n\n[[docker-compose]]\n== Docker Compose\n\ninclude::external/_docker_compose.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_global-configuration.adoc",
    "content": "\n[[global-configuration]]\n= Global configuration\n\nGlobal configuration parameters specify overall behavior like the\nconnection to the Docker host. The corresponding system properties\nwhich can be used to set it from the outside are given in\nparentheses.\n\nThe docker-maven-plugin uses the Docker remote API so the URL of your\nDocker Daemon must somehow be specified. The URL can be specified by\nthe dockerHost or machine configuration, or by the `DOCKER_HOST`\nenvironment variable.\n\nThe Docker remote API supports communication via SSL and\nauthentication with certificates.  The path to the certificates can\nbe specified by the certPath or machine configuration, or by the\n`DOCKER_CERT_PATH` environment variable.\n\n.Global Configuration\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| *apiVersion*\n| Use this variable if you are using an older version of docker not compatible with the current default use to communicate with the server.\n| `docker.apiVersion`\n\n| *authConfig*\n| Authentication information when pulling from or pushing to Docker registry. There is a dedicated section <<authentication, Authentication>> for how doing security.\n|\n\n| *autoCreate CustomNetworks*\n| Create automatically Docker networks during `{plugin}:start` and remove it during `{plugin}:stop` if you provide a custom network in the run configuration of an image. The default is `false`.\n| `docker.autoCreate` `CustomNetworks`\n\n| *autoPull*\na| Decide how to pull missing base images or images to start. This option is *deprecated*, please use <<image-pull-policy, imagePullPolicy>> instead.\n\nThe following values are supported:\n\n * `on` or `once`: Automatic download any missing images (default)\n * `off` : Automatic pulling is switched off always\n * `always` : Pull images always even when they already exist locally.\n\n| `docker.autoPull`\n\n| *buildArchiveOnly*\na| Skip the actual Docker image build and only create the archive holding the Dockerfile and build context. The following values are supported:\n\n* `/path/to/archive` : Create the build tar archive as file with name `/path/to/archive` and then stop without doing the actual image build\n* `true` (or an empty value) : Skip building the image, but don't copy the generated build archive.\n* `false` : Build the image. This is the default behaviour.\n| `docker.buildArchiveOnly`\n\n| *certPath*\n| Path to SSL certificate when SSL is used for communicating with the Docker daemon. These certificates are normally stored in `~/.docker/`. With this configuration the path can be set explicitly. If not set, the fallback is first taken from the environment variable `DOCKER_CERT_PATH` and then as last resort `~/.docker/`. The keys in this are expected with it standard names `ca.pem`, `cert.pem` and `key.pem`. Please refer to the https://docs.docker.com/articles/https[Docker documentation] for more information about SSL security with Docker.\n| `docker.certPath`\n\n| *dockerHost*\na| The URL of the Docker Daemon. If this configuration option is not given, then the optional `<machine>` configuration section is consulted. The scheme of the URL can be either given directly as `http` or `https`\ndepending on whether plain HTTP communication is enabled or SSL should\nbe used. Alternatively the scheme could be `tcp` in which case the\nprotocol is determined via the IANA assigned port: 2375 for `http`\nand 2376 for `https`. Finally, Unix sockets are supported by using\nthe scheme `unix` together with the filesystem path to the unix socket.\nThe discovery sequence used by the docker-maven-plugin to determine\nthe URL is:\n\n. value of *dockerHost* (`docker.host`)\n. the Docker host associated with the docker-machine named in `<machine>`, i.e. the `DOCKER_HOST` from `docker-machine env`. See <<docker-machine,below>> for more information about Docker machine support. If `<machine>` is not set, then no docker-machine detection is used.\n. the value of the environment variable `DOCKER_HOST`.\n. `/var/run/docker.sock` if it is a readable socket (Unix & OS X).\n. `//./pipe/docker_engine` if it is a readable named pipe (Windows)\n| `docker.host`\n\n| *filter*\n| In order to temporarily restrict the operation of plugin goals this configuration option can be used. Typically this will be set via the system property `docker.filter` when Maven is called. The value can be a single image name (either its alias or full name) or it can be a comma separated list with multiple image names. Any name which doesn't refer an image in the configuration will be ignored.\n| `docker.filter`\n\n| [[image-pull-policy]] *imagePullPolicy*\na| Specify whether images should be pull when looking for base images while building or images for starting.\nThis property can take the following values (case insensitive):\n\n * `IfNotPresent`: Automatic download any missing images (default)\n * `Never` : Automatic pulling is switched off always\n * `Always` : Pull images always even when they already exist locally.\n\nBy default a progress meter is printed out on the console, which is omitted when using Maven in batch mode (option `-B`). A very simplified progress meter is provided when using no color output (i.e. with `-Ddocker.useColor=false`).\n| `docker.imagePullPolicy`\n\n| *logDate*\n| Date format which is used for printing out container logs. This configuration can be overwritten by individual run configurations and described below. The format is described in <<loggging,Logging>>.\n| `docker.logDate`\n\n| *logStdout*\n| For all container logging to standard output if set to `true`, regardless whether a `file` for log output is specified. See also <<start-logging,Logging>>\n| `docker.logStdout`\n\n| *machine*\n| Docker machine configuration. See <<docker-machine, Docker Machine>> for possible values\n|\n\n| *maxConnections*\n| Number of parallel connections are allowed to be opened to the Docker Host. For parsing log output, a connection needs to be kept open (as well for the wait features), so don't put that number to low. Default is 100 which should be suitable for most of the cases.\n| `docker.maxConnections`\n\n| *jib*\n| Delegate Image Build process to https://github.com/GoogleContainerTools/jib[JIB], `false` by default. Note that this option is applicable only for <<docker:build,build>> and <<docker:push,push>> goals, other goals won't work if this is enabled (since they dependend on Docker specific features)\n| `docker.build.jib`\n\n| *outputDirectory*\n| Default output directory to be used by this plugin. The default value is `target/docker` and is only used for the goal `{plugin}:build`.\n| `docker.target.dir`\n\n| *portPropertyFile*\n| Global property file into which the mapped properties should be written to. The format of this file and its purpose are also described in <<start-port-mapping,Port Mapping>>.\n|\n\n| *registry*\n| Specify globally a registry to use for pulling and pushing images. See <<registry,Registry handling>> for details.\n| `docker.registry`\n\n| *skip*\n| With this parameter the execution of this plugin can be skipped completely.\n| `docker.skip`\n\n| *skipBuild*\n| If set no images will be build (which implies also _skip.tag_) with `{plugin}:build`\n| `docker.skip.build`\n\n| *skipPush*\n| If set dont push any images even when `{plugin}:push` is called.\n| `docker.skip.push`\n\n| *skipRun*\n| If set dont create and start any containers with `{plugin}:start` or `{plugin}:run`\n| `docker.skip.run`\n\n| *skipTag*\n| If set to `true` this plugin won't add any tags to images that have been built with `{plugin}:build`. +\nIf set to `true` this plugin won't push any tags with `{plugin}:push`. +\nIf set to `true` this plugin won't remove any tags with `{plugin}:remove`. +\n| `docker.skip.tag`\n\n| *skipMachine*\n| Skip using docker machine in any case\n| `docker.skip.machine`\n\n| *sourceDirectory*\n| Default directory that contains the assembly descriptor(s) used by the plugin. The default value is `src/main/docker`. This option is only relevant for the `{plugin}:build` goal.\n| `docker.source.dir`\n\n| *useColor*\n| Whether to use colored log output. By default this is switched on when running on a console, off otherwise.\n| `docker.useColor`\n\n| *verbose*\n| String attribute for switching on verbose output on standard output (stdout). It takes a comma separated list of string values to switch on various verbosity groups.\n\nThe currently known groups are:\n\nbuild::\n Print out Docker build instructions\napi::\n API calls to the Docker daemons are logged\nall::\n All levels are enabled\n\nIf you set an empty string (or only e.g. `-Ddocker.verbose`) then the \"build\" group is enabled. You can also use \"true\" / \"false\" to switch on / off verbose logging.\n\nDefault is that verbose logging is disabled.\n| `docker.verbose`\n|===\n\n.Example\n[source,xml]\n----\n<configuration>\n   <dockerHost>https://localhost:2376</dockerHost>\n   <certPath>src/main/dockerCerts</certPath>\n   <useColor>true</useColor>\n   .....\n</configuration>\n----\n\n[[docker-machine]]\n.Docker Machine\nThis plugin supports also Docker machine (which must be installed locally, of course). A Docker machine configuration can be provided with a top-level `<machine>` configuration section. This configuration section knows the following options:\n\n.Docker Machine Options\n[cols=\"1,4\"]\n|===\n| Element | Description\n\n| *name*\n| Docker machine's name. Default is `default`\n\n| *autoCreate*\n| if set to `true` then a Docker machine will automatically created. Default is `false`.\n\n| *regenerateCertsAfterStart*\n| if set to `true` then certificates will be regenerated after starting the Docker Machine.  This is useful if using the AWS EC2 driver, which will assign machines new IP addresses after each start.  Default is `false`.\n\n| *createOptions*\n| Map with options for Docker machine when auto-creating a machine. See the docker machine documentation for possible options.\n|===\n\n\nWhen no Docker host is configured or available as an environment variable, then the configured Docker machine is used. If the machine exists but is not running, it is started automatically. If it does not exists but `autoCreate` is true, then the machine is created and started. Otherwise, an error is printed. Please note, that a machine which has been created because of `autoCreate` gets never deleted by docker-maven-plugin. This needs to be done manually if required.\n\nIn absence of a `<machine>` configuration section the Maven property `docker.machine.name` can be used to provide the name of a Docker machine. Similarly, the property `docker.machine.autoCreate` can be set to true for creating a Docker machine, too.\n\nYou can use the property `docker.skip.machine` if you want to override the internal detection mechanism to always disable docker machine support.\n\n.Example\n[source,xml]\n----\n<!-- Work with a docker-machine -->\n<configuration>\n  <machine>\n    <name>maven</name>\n    <autoCreate>true</autoCreate>\n    <createOptions>\n      <driver>virtualbox</driver>\n      <virtualbox-cpu-count>2</virtualbox-cpu-count>\n    </createOptions>\n  </machine>\n   .....\n</configuration>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/_goals.adoc",
    "content": "= Maven Goals\n\nThis plugin supports the following goals which are explained in detail\nin the next sections.\n\n.Plugin Goals\n[cols=\"2,3,2\"]\n|===\n|Goal | Description | Default Lifecycle Phase\n\n|**<<{plugin}:build>>**\n|Build images\n|install\n\n|**<<{plugin}:start>>** or **<<{plugin}:start,{plugin}:run>>**\n|Create and start containers\n|pre-integration-test\n\n|**<<{plugin}:stop>>**\n|Stop and destroy containers\n|post-integration-test\n\n|**<<{plugin}:push>>**\n|Push images to a registry\n|deploy\n\n|**<<{plugin}:watch>>**\n|Watch for doing rebuilds and restarts\n|\n\n|**<<{plugin}:remove>>**\n|Remove images from local docker host\n|post-integration-test\n\n|**<<{plugin}:logs>>**\n|Show container logs\n|\n\n|**<<{plugin}:source>>**\n|Attach docker build archive to Maven project\n|package\n\n|**<<{plugin}:save>>**\n|Save images to a file\n|\n\n|**<<{plugin}:volume-create>>**\n|Create a volume for containers to share data\n|pre-integration-test\n\n|**<<{plugin}:volume-remove>>**\n|Remove a volume\n|post-integration-test\n|===\n\nNote that all goals are orthogonal to each other. For example in order\nto start a container for your application you typically have to build\nits image before. `{plugin}:start` does *not* imply building the image\nso you should use it then in combination with `{plugin}:build`.\n"
  },
  {
    "path": "src/main/asciidoc/inc/_image-configuration.adoc",
    "content": "[[image-configuration]]\n= Image configuration\n\nThe plugin's configuration is centered around _images_. These are\nspecified for each image within the `<images>` element of the\nconfiguration with one `<image>` element per image to use.\n\nThe `<image>` element can contain the following sub elements:\n\ninclude::image/_configuration.adoc[]\n\nEither a `<build>` or `<run>` section must be present (except when you are using the <<simple-dockerfile-build,simple Dockerfile build>> mode). These are explained in details in the corresponding goal sections.\n\ninclude::image/_example.adoc[]\n\nWhen using Maven profiles, it can be useful to override settings\nof a particular image. To facilitate this, the element `<imagesMap>`\ncan be used alongside the `<images>` element. Each entry in `<imagesMap>`\ntranslates to an image configuration where the **alias** of the image\nis set to the map entry's **key**. The examples above and below produce\nidentical image configurations.\n\ninclude::image/_example_imagesMap.adoc[]\n\ninclude::image/_naming.adoc[]\n"
  },
  {
    "path": "src/main/asciidoc/inc/_implicit-properties.adoc",
    "content": "= Implicit properties\n\nThere are some implicit configurations in docker maven plugin that are not so straightforward. These are simply workarouds to get docker-maven-plugin's flow right; just to overcome limitations of Maven and other things. Some of these are mentioned below:\n\n* If the only value of the `env` parameter is a docker-maven-plugin internal property which has been set implicitly you have to prefix the property with a single `+` like in `+${docker.container.test.ip}`. This is necessary due to some Maven limitations which simply interpolates a lone, non defined property, to an empty string which can't then be replaced by this plugin after the initial interpolation phase.\n\n* When providing port mapping in a format like `host.ip:host.port:80`, you need to prefix property with a single `+`. In this form, the host ip of the container will be placed into a Maven property name host.ip. If docker reports that value to be 0.0.0.0, the value of docker.host.address will be substituted instead. In the event you want to use this form and have the container bind to a specific hostname/ip address, you can declare a Maven property of the same name (host.ip in this example) containing the value to use. host:port works in the same way as described above.\n"
  },
  {
    "path": "src/main/asciidoc/inc/_installation.adoc",
    "content": "= Installation\n\nThis plugin is available from Maven central and can be connected to\npre- and post-integration phase as seen below. The configuration and\navailable goals are described below.\n\n.Example\n[source,xml,indent=0,subs=\"verbatim,quotes,attributes\"]\n----\n<plugin>\n  <groupId>io.fabric8</groupId>\n  <artifactId>docker-maven-plugin</artifactId>\n  <version>{version}</version>\n\n  <configuration>\n     ....\n     <images>\n        <!-- A single's image configuration -->\n        <image>\n           ....\n        </image>\n        ....\n     </images>\n  </configuration>\n\n  <!-- Connect start/stop to pre- and\n       post-integration-test phase, respectively if you want to start\n       your docker containers during integration tests -->\n  <executions>\n    <execution>\n       <id>start</id>\n       <phase>pre-integration-test</phase>\n       <goals>\n         <!-- \"build\" should be used to create the images with the\n              artifact -->\n         <goal>build</goal>\n         <goal>start</goal>\n       </goals>\n    </execution>\n    <execution>\n       <id>stop</id>\n       <phase>post-integration-test</phase>\n       <goals>\n         <goal>stop</goal>\n      </goals>\n    </execution>\n  </executions>\n</plugin>\n----\n\nWhen working with this plugin you can use an own packaging with a specialized lifecycle in order to keep your pom files small. Three packaging variants are available:\n\n* *docker* : This binds `{plugin}:build` to the package phase and `{plugin}:start` / `{plugin}:stop` to the pre- and post-integration phase respectively. Also `{plugin}:push` is bound to the deploy phase.\n* *docker-build* : Much like the _docker_ packaging, except that there are no integration tests configured by default.\n* *docker-tar* : Create a so called _Docker tar_ archive which is used as the artifact and which later can be used for building an image. It contains essentially a `Dockerfile` with supporting files. See link:docker-source[{plugin}:source] for more details.\n\nThese packaging definitions include the _jar_ lifecycle methods so they are well suited for simple Microservice style projects.\n\n.Example\n[source,xml]\n----\n<pom>\n  <artifactId>demo</artifactId>\n  <version>0.0.1</version>\n  <packaging>docker</packaging>\n  ...\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>io.fabric8</groupId>\n        <artifactId>docker-maven-plugin</artifactId>\n        <extensions>true</extensions>\n        <configuration>\n          <images>\n            <image>\n            ...\n            </image>\n          </images>\n        </configuration>\n      </plugin>\n    </plugins>\n    ....\n  </build>\n</pom>\n----\n\nThis will create the jar (if any), build the Docker images, start the configured Docker containers, runs the integration tests, stops the configured Docker container when you enter `mvn install`. With `mvn deploy` you can additionally push the images to a Docker configuration. Please note the `<extensions>true</extensions>` which is mandatory when you use a custom lifecycle.\n\nThe rest of this manual is now about how to configure the plugin for your images.\n"
  },
  {
    "path": "src/main/asciidoc/inc/_introduction.adoc",
    "content": "\n= Introduction\n\nThis is a Maven plugin for managing Docker images and containers. It focuses on two major aspects for a Docker build integration:\n\n== Building Images\n\nOne purpose of this plugin is to create Docker images holding the actual application. This is done with the <<{plugin}:build>> goal. It is easy to include build artefacts and their dependencies into an image.\n\nSeveral ways for configuring the builds are supported:\n\n* An own configuration syntax can be used to create a Dockerfile. For specifying artefacts and other files, the plugin uses the assembly descriptor format from the maven-assembly-plugin to copy over those file into the Docker image.\n* An external Dockerfile can be specified in which Maven properties can be inserted. This is also the default mode, if only a single image should be built and a top-level `Dockerfile` exists. See <<simple-dockerfile-build,Simple Dockerfile build>> for details of this zero XML configuration mode.\n\nImages that are built with this plugin can be pushed to public or private Docker registries with <<{plugin}:push>>.\n\n== Running Containers\n\nWith this plugin it is possible to run completely isolated integration tests so you don't need to take care of shared resources. Ports can be mapped dynamically and made available as Maven properties to your integration test code.\n\nMultiple containers can be managed at once, which can be linked together or share data via volumes. Containers are created and started with the <<{plugin}:start>> goal and stopped and destroyed with the <<{plugin}:stop>> goal. For integration tests both goals are typically bound to the the pre-integration-test and post-integration-test phase, respectively. It is recommended to use the maven-failsafe-plugin for integration testing in order to stop the docker container even when the tests fail.\n\nFor proper isolation, container exposed ports can be dynamically and flexibly mapped to local host ports. It is easy to specify a Maven property which will be filled in with a dynamically assigned port after a container has been started. This can then be used as parameter for integration tests to connect to the application.\n\n== Configuration\n\nThe plugin configuration contains a global part and a list of image-specific configuration within a `<images>` list, where each image is defined within a `<image>` tag. See <<example,below>> for an example.\n\nThe <<global-configuration,global part>> contains configuration applicable to all images like the Docker URL or the path to the SSL certificates for communication with the Docker Host.\n\nThen, each specific image configuration has three parts:\n\n* A general image part containing the image name and alias.\n* A <<{plugin}:build,<build> >> configuration specifying how images are built\n* A <<{plugin}:start,<run> >> configuration describing how containers should be created and started.\n\nThe `<build>` and `<run>` parts are optional and can be omitted.\n\n[[example]]\n== Example\n\nIn the following examples, two images are specified. One is the official PostgreSQL 9 image from Docker Hub, which internally is referenced with an alias \"database\". It only has a <<{plugin}:start,<run> >> section which declares that the startup should wait until the given text pattern is matched in the log output. Next is a \"service\" image, which has a <<{plugin}:build,<build> >> section. It creates an image which has artifacts and dependencies in the `/maven` directory (and which are specified with an assembly descriptor). Additionally, it specifies the startup command for the container, which in this example fires up a microservice from a jar file copied over via the assembly descriptor. It also exposes port 8080. In the `<run>` section this port is mapped to a dynamically chosen port and then assigned to the Maven property `${tomcat.port}`. This property could be used, for example, by an integration test to access this microservice. An important part is the `<links>` section which indicates that the image with the alias of \"database\" is linked into the \"service\" container, which can access the internal ports in the usual Docker way (via environment variables prefixed with `DB_`).\n\nImages can be specified in any order and the plugin will take care of the proper startup order (and will bail out if it detects circular dependencies).\n\n[source,xml,indent=0,subs=\"verbatim,quotes\"]\n.Example plugin configuration\n----\n<configuration>\n  <images>\n    <image>\n      <alias>service</alias> <!--1-->\n      <name>fabric8/docker-demo:${project.version}</name>\n\n      <build> <!--2-->\n         <from>java:8</from> <!--3-->\n         <assembly>\n           <descriptor>docker-assembly.xml</descriptor> <!--4-->\n         </assembly>\n         <cmd> <!--5-->\n            <shell>java -jar /maven/service.jar</shell>\n         </cmd>\n      </build>\n\n      <run> <!--6-->\n         <ports> <!--7-->\n           <port>tomcat.port:8080</port>\n         </ports>\n         <wait> <!--8-->\n           <http>\n              <url>http://localhost:${tomcat.port}/access</url>\n           </http>\n           <time>10000</time>\n         </wait>\n         <links> <!--9-->\n           <link>database:db</link>\n         </links>\n       </run>\n    </image>\n\n    <image>\n      <alias>database</alias> <!--10-->\n      <name>postgres:9</name>\n      <run>\n        <wait> <!--11-->\n          <log>database system is ready to accept connections</log>\n          <time>20000</time>\n        </wait>\n      </run>\n    </image>\n  </images>\n</configuration>\n----\n<1> Image configuration for a Java service with alias \"service\" and name `fabric8/docker-demo:${project.version}`\n<2> <<{plugin}:build,build configuration>> defines how a Docker image should be created\n<3> Base image, in this case `java:8`\n<4> Content of the image can be specified with an <<build-assembly,assembly descriptor>>\n<5> <<misc-startup,Default command>> to run when a container is created.\n<6> <<{plugin}:start,Run configuration>> defines how a container should be created from this image\n<7> <<start-port-mapping,Port mapping>> defines how container ports should be mapped to host ports\n<8> <<start-wait,Wait>> section which is a readiness check when starting the service\n<9> <<start-links,Network link>> describes how this service's container is linked to the database container\n<10> Second image is a plain database image which is only needed for running (hence there is no `<build>` section). The alias is used in the network link section above\n<11> Wait until the corresponding output appears on stdout when starting the Docker container.\n\n== Features\n\nSome other highlights, in random order:\n\n* Auto pulling of images with a progress indicator\n* Waiting for a container to startup based on time, the reachability of an URL, or a pattern in the log output\n* Support for SSL <<authentication>> and OpenShift credentials\n* Docker machine support\n* Flexible registry handling (i.e. registries can be specified as metadata)\n* Specification of <<password-encryption,encrypted>> registry passwords for push and pull in ~/.m2/settings.xml (i.e., outside the pom.xml)\n* Color output\n* <<{plugin}:watch,Watching>> on project changes and automatic recreation of image\n* <<property-configuration,Properties>> as alternative to the XML configuration\n* Support for Docker daemons accepting http or https request via TCP and for Unix sockets\n"
  },
  {
    "path": "src/main/asciidoc/inc/_links.adoc",
    "content": "\n= Further reading\n\n* Examples:\n** https://github.com/fabric8io/docker-maven-plugin/blob/master/doc/examples.md[Examples]\nare below `samples/` and contain example\nsetups which you can use as blueprints for your own projects.\n** A https://github.com/fabric8io/shootout-docker-maven[Shootout] for\ncomparing docker maven plugins\n** Another\nhttps://github.com/fabric8io/docker-maven-sample[sample project]\nwith a Microservice and a Database.\n* https://github.com/fabric8io/docker-maven-plugin/blob/master/doc/changelog.md[ChangeLog]\nhas the release history of this plugin.\n* https://github.com/fabric8io/docker-maven-plugin/blob/master/CONTRIBUTING.md[Contributing]\nexplains how you can contribute to this project. Pull requests are highly appreciated!\n"
  },
  {
    "path": "src/main/asciidoc/inc/_registry.adoc",
    "content": "\n[[registry]]\n= Registry handling\n\nDocker uses registries to store images. The registry is typically\nspecified as part of the name. I.e. if the first part (everything\nbefore the first `/`) contains a dot (`.`) or colon (`:`) this part is\ninterpreted as an address (with an optionally port) of a remote\nregistry. This registry (or the default `docker.io` if no\nregistry is given) is used during push and pull operations. This\nplugin follows the same semantics, so if an image name is specified\nwith a registry part, this registry is contacted. Authentication is\nexplained in the next <<_authentication,section>>.\n\nThere are some situations however where you want to have more\nflexibility for specifying a remote registry. This might be because\nyou do not want to hard code a registry into `pom.xml` but\nprovide it from the outside with an environment variable or a system\nproperty.\n\nThis plugin supports various ways of specifying a registry:\n\n* If the image name contains a registry part, this registry is used\nunconditionally and can not be overwritten from the outside.\n* If an image name doesn't contain a registry, then by default the\ndefault Docker registry `docker.io` is used for push and pull\noperations. But this can be overwritten through various means:\n** If the `<image>` configuration contains a `<registry>` subelement\nthis registry is used.\n** Otherwise, a global configuration element `<registry>` is\nevaluated which can be also provided as system property via\n`-Ddocker.registry`.\n** Finally an environment variable `DOCKER_REGISTRY` is looked up for\ndetecting a registry.\n\nThis registry is used for pulling (i.e. for autopull the base image\nwhen doing a `{plugin}:build`) and pushing with `{plugin}:push`. However,\nwhen these two goals a are combined on the command line like in `mvn\n-Ddocker.registry=myregistry:5000 package {plugin}:build {plugin}:push`\nthe same registry is used for both operation. For a more fine grained\ncontrol, separate registries for _pull_ and _push_ can be specified.\n\n* In the plugin's configuration with the parameters `<pullRegistry>` and\n`<pushRegistry>`, respectively.\n* With the system properties `docker.pull.registry` and\n`docker.push.registry`, respectively.\n\n.Example\n[source,xml]\n----\n<configuration>\n  <registry>docker.jolokia.org:443</registry>\n  <images>\n    <image>\n      <!-- Without an explicit registry ... -->\n      <name>jolokia/jolokia-java</name>\n      <!-- ... hence use this registry -->\n      <registry>docker.ro14nd.de</registry>\n      ....\n    <image>\n    <image>\n      <name>postgresql</name>\n      <!-- No registry in the name, hence use the globally\n           configured docker.jolokia.org:443 as registry -->\n      ....\n    </image>\n    <image>\n      <!-- Explicitely specified always wins -->\n      <name>docker.example.com:5000/another/server</name>\n    </image>\n  </images>\n</configuration>\n----\n\nThere is some special behaviour when using an externally provided\nregistry like described above:\n\n* When _pulling_, the image pulled will be also tagged with a repository\nname *without* registry. The reasoning behind this is that this\nimage then can be referenced also by the configuration when the\nregistry is not specified anymore explicitly.\n* When _pushing_ a local image, temporarily a tag including the\nregistry is added and removed after the push. This is required\nbecause Docker can only push registry-named images.\n"
  },
  {
    "path": "src/main/asciidoc/inc/build/_assembly.adoc",
    "content": "\nThe `<assembly>` element within `<build>` is has an XML struture and defines how build artifacts and other files can enter the Docker image.\n\n[[config-image-build-assembly]]\n.Assembly Configuration (<<config-image, <image> >> : <<config-image-build, <build> >>)\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *name*\n| Assembly name, which is `maven` by default. This name is used for the archives and directories created during the build. This directory holds the files specified by the assembly. If an <<external-dockerfile,external Dockerfile>> is used than this name is also the relative directory which contains the assembly files.\n\n| *targetDir*\n| Directory under which the files and artifacts contained in the assembly will be copied within the container. The default value for this is `/<assembly name>`, so `/maven` if *name* is not set to a different value. This option has no meaning when an <<external-dockerfile,external Dockerfile>> is used.\n\n| <<build-assembly-descriptor, *inline*>>\n| Inlined assembly descriptor as described in <<build-assembly-descriptor,Assembly Descriptor>> below.\n\n| <<build-assembly-descriptor, *descriptor*>>\n| Path to an assembly descriptor file, whose format is described  <<build-assembly-descriptor,Assembly Descriptor>> below.\n\n| <<build-assembly-descriptor-refs, *descriptorRef*>>\n| Alias to a predefined assembly descriptor. The available aliases are also described in <<build-assembly-descriptor,Assembly Descriptor>> below.\n\n| *dockerFileDir*\n| Directory containing an external Dockerfile. _This option is deprecated, please use <dockerFileDir> directly in the <build> section_.\n\n| *exportTargetDir*\n| Specification whether the `targetDir` should be exported as a volume.  This value is `true` by default except in the case the `targetDir` is set to the container root (`/`). It is also `false` by default when a base image is used with `from` since exporting makes no sense in this case and will waste disk space unnecessarily.\n\n| *ignorePermissions*\n| Specification if existing file permissions should be ignored\nwhen creating the assembly archive with a mode `dir`. This value is `false` by default. _This property is deprecated, use a `permissions` of `ignore` instead._\n\n| *mode*\na| Mode how the how the assembled files should be collected:\n\n* `dir` : Files are simply copied (default),\n* `tar` : Transfer via tar archive\n* `tgz` : Transfer via compressed tar archive\n* `zip` : Transfer via ZIP archive\n\nThe archive formats have the advantage that file permission can be preserved better (since the copying is independent from the underlying files systems), but might triggers internal bugs from the Maven assembler (as it has been reported in https://github.com/fabric8io/docker-maven-plugin/issues/171[#171])\n\n| *permissions*\na| Permission of the files to add:\n\n* `ignore` to use the permission as found on files regardless on any\nassembly configuration\n* `keep` to respect the assembly provided permissions, `exec` for setting the executable bit on all files (required for Windows when using an assembly mode `dir`)\n* `auto` to let the plugin select `exec` on Windows and `keep` on others.\n\n`keep` is the default value.\n\n| *tarLongFileMode*\n| Sets the TarArchiver behaviour on file paths with more than 100 characters length. Valid values are: \"warn\"(default), \"fail\", \"truncate\", \"gnu\", \"posix\", \"posix_warn\" or \"omit\"\n\n| [[config-image-build-assembly-user]] *user*\n| User and/or group under which the files should be added. The user must already exist in the base image.\n\nIt has the general format `user[:group[:run-user]]`. The user and group can be given either as numeric user- and group-id or as names. The group id is optional.\n\nIf a third part is given, then the build changes to user `root` before changing the ownerships, changes the ownerships and then change to user `run-user` which is then used for the final command to execute. This feature might be needed, if the base image already changed the user (e.g. to 'jboss') so that a `chown` from root to this user would fail.\n(_**This third user part has been marked as deprecated and will not be supported in future versions of this plugin.**_)\n\nFor example, the image `jboss/wildfly` use a \"jboss\" user under which all commands are executed. Adding files in Docker always happens under the UID root. These files can only be changed to \"jboss\" is the `chown` command is executed as root. For the following commands to be run again as \"jboss\" (like the final `standalone.sh`), the plugin switches back to user `jboss` (this is this \"run-user\") after changing the file ownership. For this example a specification of\n`jboss:jboss:jboss` would be required.\n|===\n\nIn the event you do not need to include any artifacts with the image, you may safely omit this element from the configuration.\n\n[[build-assembly-descriptor]]\n==== Assembly Descriptor\n\nWith using the `inline`, `descriptor` or `descriptorRef` option\nit is possible to bring local files, artifacts and dependencies into\nthe running Docker container. A `descriptor` points to a file\ndescribing the data to put into an image to build. It has the same\nhttp://maven.apache.org/plugins/maven-assembly-plugin/assembly.html[format] as for creating assemblies with the\nhttp://maven.apache.org/plugins/maven-assembly-plugin[maven-assembly-plugin] with following exceptions:\n\n* `<formats>` are ignored, the assembly will allways use a directory\nwhen preparing the data container (i.e. the format is fixed to\n`dir`)\n* The `<id>` is ignored since only a single assembly descriptor is\nused (no need to distinguish multiple descriptors)\n\nAlso you can inline the assembly description with a `inline` description\ndirectly into the pom file. Adding the proper namespace even allows for\nIDE autocompletion. As an example, refer to the profile `inline` in\nthe `data-jolokia-demo` 's pom.xml.\n\nAlternatively `descriptorRef` can be used with the name of a\npredefined assembly descriptor. The following symbolic names can be\nused for `descriptorRef`:\n\n[[build-assembly-descriptor-refs]]\n.Predefined Assembly Descriptors\n[cols=\"1,3\"]\n|===\n| Assembly Reference | Description\n\n| *artifact-with-dependencies*\n| Attaches project's artifact and all its dependencies. Also, when a `classpath` file exists in the target directory, this will be added to.\n\n| *artifact*\n| Attaches only the project's artifact but no dependencies.\n\n| *project*\n| Attaches the whole Maven project but with out the `target/` directory.\n\n| *rootWar*\n| Copies the artifact as `ROOT.war` to the exposed directory. I.e. Tomcat will then deploy the war under the root context.\n|===\n\n.Example\n[source,xml]\n----\n<images>\n  <image>\n    <build>\n      <assembly>\n         <descriptorRef>artifact-with-dependencies</descriptorRef>\n         .....\n----\n\nwill add the created artifact with the name `${project.build.finalName}.${artifact.extension}` and all jar dependencies in the the `targetDir` (which is `/maven` by default).\n\nAll declared files end up in the configured `targetDir` (or `/maven` by default) in the created image.\n\n.Maven peculiarities when including the artifact\nIf the assembly references the artifact to build with this pom, it is required that the `package` phase is included in the run. Otherwise the artifact file, can't be found by `docker:build`. This is an old https://issues.apache.org/jira/browse/MASSEMBLY-94[outstanding issue] of the assembly plugin which probably can't be fixed because of the way how Maven works. We tried hard to workaround this issue and in 90% of all cases, you won't experience any problem. However, when the following warning happens which might lead to the given error:\n\n[source]\n----\n[WARNING] Cannot include project artifact: io.fabric8:helloworld:jar:0.20.0; it doesn't have an associated file or directory.\n[WARNING] The following patterns were never triggered in this artifact inclusion filter:\no  'io.fabric8:helloworld'\n\n[ERROR] DOCKER> Failed to create assembly for docker image  (with mode 'dir'): Error creating assembly archive docker: You must set at least one file.\n----\n\nthen you have two options to fix this:\n\n* Call `mvn package {plugin}:build` to explicitly run \"package\" and \"docker:build\" in a chain.\n* Bind `build` to an to an execution phase in the plugin's definition. By default `{plugin}:build` will bind to the `install` phase is set in an execution. Then you can use a plain `mvn install` for building the artifact and creating the image.\n\n[source,xml]\n----\n<executions>\n  <execution>\n    <id>docker-build</id>\n    <goals>\n       <goal>build</goal>\n    </goals>\n  </execution>\n</executions>\n----\n\n.Example\n\nIn the following example a dependency from the pom.xml is included and\nmapped to the name `jolokia.war`. With this configuration you will end\nup with an image, based on `busybox` which has a directory `/maven`\ncontaining a single file `jolokia.war`. This volume is also exported\nautomatically.\n\n[source,xml]\n----\n<assembly>\n  <inline>\n    <dependencySets>\n      <dependencySet>\n        <includes>\n          <include>org.jolokia:jolokia-war</include>\n        </includes>\n        <outputDirectory>.</outputDirectory>\n        <outputFileNameMapping>jolokia.war</outputFileNameMapping>\n      </dependencySet>\n    </dependencySets>\n  </inline>\n</assembly>\n----\n\nAnother container can now connect to the volume an 'mount' the\n`/maven` directory. A container  from `consol/tomcat-7.0` will look\ninto `/maven` and copy over everything to `/opt/tomcat/webapps` before\nstarting Tomcat.\n\nIf you are using the `artifact` or `artifact-with-dependencies` descriptor, it is\npossible to change the name of the final build artifact with the following:\n\n.Example\n[source,xml]\n----\n<build>\n  <finalName>your-desired-final-name</finalName>\n  ...\n</build>\n----\n\nPlease note, based upon the following documentation listed http://maven.apache.org/pom.html#BaseBuild_Element[here], there is no guarantee the plugin creating your artifact will honor it in which case you will need to use a custom descriptor like above to achieve the desired naming.\n\nCurrently the `jar` and `war` plugins properly honor the usage of `finalName`.\n"
  },
  {
    "path": "src/main/asciidoc/inc/build/_buildargs.adoc",
    "content": "[[property-buildargs]]\nAs described in section <<build-configuration,Configuration>> for external Dockerfiles https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg[Docker build arg] can be used. In addition to the\nconfiguration within the plugin configuration you can also use properties to specify them:\n\n* Set a system property when running Maven, eg.: `-Ddocker.buildArg.http_proxy=http://proxy:8001`. This is especially\nuseful when using predefined Docker arguments for setting proxies transparently.\n* Set a project property within the `pom.xml`, eg.:\n\n.Example\n[source,xml]\n----\n  <docker.buildArg.myBuildArg>myValue</docker.buildArg.myBuildArg>\n----\n\nPlease note that the system property setting will always override the project property. Also note that for all properties which are not Docker https://docs.docker.com/engine/reference/builder/#arg[predefined] properties, the external Dockerfile must contain an `ARGS` instruction.\n"
  },
  {
    "path": "src/main/asciidoc/inc/build/_configuration.adoc",
    "content": "\nAll build relevant configuration is contained in the `<build>` section\nof an image configuration. The following configuration options are supported:\n\n[[config-image-build]]\n.Build configuration (<<config-image, <image> >>)\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| <<config-image-build-assembly, *assembly*>>\n| specifies the assembly configuration as described in <<build-assembly,Build Assembly>>\n\n| <<build-buildargs, *args*>>\n| Map specifying the value of https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg[Docker build args]\nwhich should be used when building the image with an external Dockerfile which uses build arguments. The key-value syntax is the same as when defining Maven properties (or `labels` or `env`).\nThis argument is ignored when no external Dockerfile is used. Build args can also be specified as properties as\ndescribed in <<build-buildargs,Build Args>>\n\n| *buildOptions*\n| Map specifying the build options to provide to the docker daemon when building the image. These options map to the ones listed as query parameters in the\nhttps://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#build-image-from-a-dockerfile[Docker Remote API] and are restricted to simple options\n(e.g.: memory, shmsize). If you use the respective configuration options for build options natively supported by the build configuration (i.e. `squash`, `noCache`, `cleanup=remove` for buildoption `forcerm=1` and `args` for build args) then these will override any corresponding options given here. The key-value syntax is the same as when defining environment variables or labels as described in <<misc-env,Setting Environment Variables and Labels>>.\n\n| *cleanup*\n| Cleanup dangling (untagged) images after each build (including any containers created from them). Default is `try` which tries to remove the old image, but doesn't fail the build if this is not possible because e.g. the image is still used by a running container. Use `remove` if you want to fail the build and `none` if no cleanup is requested.\n\n| [[context-dir]]*contextDir*\n| Path to a directory used for the build's context. You can specify the `Dockerfile` to use with *dockerFile*, which by default is the Dockerfile found in the `contextDir`. The Dockerfile can be also located outside of the `contextDir`, if provided with an absolute file path. See <<external-dockerfile, External Dockerfile>> for details.\n\n| <<misc-startup, *cmd*>>\n| A command to execute by default (i.e. if no command is provided when a container for this image is started). See <<misc-startup,Startup Arguments>> for details.\n\n| *compression*\n| The compression mode how the build archive is transmitted to the docker daemon (`{plugin}:build`) and how docker build archives are attached to this build as sources (`{plugin}:source`). The value can be `none` (default), `gzip` or `bzip2`.\n\n| *dockerFile*\n| Path to a `Dockerfile` which also triggers _Dockerfile mode_. See <<external-dockerfile, External Dockerfile>> for details.\n\n| *dockerFileDir* (_deprecated_ in favor of *<<context-dir, contextDir>>*)\n| Path to a directory holding a `Dockerfile` and switch on _Dockerfile mode_. See <<external-dockerfile, External Dockerfile>> for details. _This option is deprecated in favor of _contextDir_ and will be removed for the next major release_.\n\n| *dockerArchive*\n| Path to a saved image archive which is then imported. See <<external-dockerfile, Docker archive>> for details.\n\n| <<misc-startup, *entryPoint*>>\n| An entrypoint allows you to configure a container that will run as an executable. See <<misc-startup,Startup Arguments>> for details.\n\n| <<misc-env, *env*>>\n| The environments as described in <<misc-env,Setting Environment Variables and Labels>>.\n\n| *filter*\n| Enable and set the delimiters for property replacements. By default properties in the format `${..}` are replaced with Maven properties. You can switch off property replacement by setting this property to `false`. When using a single char like `@` then this is used as a delimiter (e.g `@...@`). See <<build-filtering, Filtering>> for more details.\n\n| [[build-config-from]]*from*\n| The base image which should be used for this image. If not given this default to `busybox:latest` and is suitable for a pure data image.\nifeval::[\"{plugin}\" == \"fabric8\"]\nIn case of an <<build-openshift,S2I Binary build>> this parameter specifies the S2I Builder Image to use, which by default is `fabric8/s2i-java:latest`. See also <<build-config-from-ext,from-ext>> how to add additional properties for the base image.\n\nendif::[]\n\n| [[build-config-from-ext]]**fromExt**\na| Extended definition for a base image. This field holds a map of defined in `<key>value</key>` format. The known keys are:\n\n* `<name>` : Name of the base image\nifeval::[\"{plugin}\" == \"fabric8\"]\n* `<kind>` : Kind of the reference to the builder image when in S2I build mode. By default its `ImageStreamTag` but can be also `ImageStream`. An alternative would be `DockerImage`\n* `<namespace>` : Namespace where this builder image lives.\n\nendif::[]\n\nA provided `<from>` takes precedence over the name given here. This tag is useful for extensions of this plugin like the https://maven.fabric8.io[fabric8-maven-plugin] which can evaluate the additional information given here.\n\n| <<build-healthcheck, *healthCheck*>>\n| Definition of a health check as described in <<build-healthcheck, Healthcheck>>\n\n| *imagePullPolicy*\n| Specific pull policy for the base image. This overwrites any global pull policy. See the globale configuration option <<image-pull-policy, imagePullPolicy>> for the possible values and the default.\n\n| [[build-config-load-name-pattern]]*loadNamePattern*\na| Scan the archive specified in `dockerArchive` and find the actual repository and tag in the archive that matches this <<name-patterns, name pattern>>. After loading the archive, link the <<config-image, image name>> configured in the POM to the repository and tag matched in the archive.\n\n| <<misc-env, *labels*>>\n| Labels  as described in <<misc-env,Setting Environment Variables and Labels>>.\n\n| *maintainer*\n| The author (`MAINTAINER`) field for the generated image\n\n| *noCache*\n| Don't use Docker's build cache. This can be overwritten by setting a system property `docker.noCache` when running Maven.\n\n| *squash*\n| Squash newly built layers into a single new layer. This can be overwritten by setting a system property `docker.squash` when running Maven.\n\n| *cacheFrom*\n| A list of `<image>` elements specifying image names to use as cache sources.\n\n| *optimise*\n| if set to true then it will compress all the `runCmds` into a single `RUN` directive so that only one image layer is created.\n\n| *ports*\n| The exposed ports which is a list of `<port>` elements, one for each port to expose. Whitespace is trimmed from each element and empty elements are ignored. The format can be either pure numerical (\"8080\") or with the protocol attached (\"8080/tcp\").\n\n| *shell*\n| Shell to be used for the *runCmds*. It contains *arg* elements which are defining the executable and its params.\n\n| *runCmds*\n| Commands to be run during the build process. It contains *run* elements which are passed to the shell. Whitespace is trimmed from each element and empty elements are ignored. The run commands are inserted right after the assembly and after *workdir* into the Dockerfile. This tag is not to be confused with the `<run>` section for this image which specifies the runtime behaviour when starting containers.\n\n| *skip*\n| if set to true disables building of the image. This config option is best used together with a maven property\n\n| *skipPush*\n| if set to true disables pushing of the image. This config option is best used together with a maven property\n\n| *skipTag*\n| If set to `true` this plugin won't add any tags to images. Property: `docker.skip.tag`\n\n| *tags*\n| List of additional `tag` elements with which an image is to be tagged after the build. Whitespace is trimmed from each element and empty elements are ignored.\n\n| *user*\n| User to which the Dockerfile should switch to the end (corresponds to the `USER` Dockerfile directive).\n\n| *volumes*\n| List of `volume` elements to create a container volume. Whitespace is trimmed from each element and empty elements are ignored.\n\n| *workdir*\n| Directory to change to when starting the container.\n|===\n\nFrom this configuration this Plugin creates an in-memory Dockerfile,\ncopies over the assembled files and calls the Docker daemon via its\nremote API.\n\n.Example\n[source,xml]\n----\n<build>\n  <from>java:8u40</from>\n  <maintainer>john.doe@example.com</maintainer>\n  <tags>\n    <tag>latest</tag>\n    <tag>${project.version}</tag>\n  </tags>\n  <ports>\n    <port>8080</port>\n  </ports>\n  <volumes>\n    <volume>/path/to/expose</volume>\n  </volumes>\n  <buildOptions>\n    <shmsize>2147483648</shmsize>\n  </buildOptions>\n\n  <shell>\n    <exec>\n      <arg>/bin/sh</arg>\n      <arg>-c</arg>\n    </exec>\n  </shell>\n  <runCmds>\n    <run>groupadd -r appUser</run>\n    <run>useradd -r -g appUser appUser</run>\n  </runCmds>\n\n  <entryPoint>\n    <!-- exec form for ENTRYPOINT -->\n    <exec>\n      <arg>java</arg>\n      <arg>-jar</arg>\n      <arg>/opt/demo/server.jar</arg>\n    </exec>\n  </entryPoint>\n\n  <assembly>\n    <mode>dir</mode>\n    <targetDir>/opt/demo</targetDir>\n    <descriptor>assembly.xml</descriptor>\n  </assembly>\n</build>\n----\n\nIn order to see the individual build steps you can switch on `verbose` mode either by setting the property `docker.verbose` or by using `<verbose>true</verbose>` in the <<global-configuration,Global configuration>>\n"
  },
  {
    "path": "src/main/asciidoc/inc/build/_healthcheck.adoc",
    "content": "\nHealthchecks has been introduced since Docker 1.12 and are a way to tell Docker how to test a container to check that it's still working. With a health check you specify a command which is periodically executed and checked for its return value. If the healtcheck return with an exit 0 the container is considered to be healthy, if it returns with 1 then the container is not working correctly.\n\nThe healtcheck configuration can have the following options\n\n.Healthcheck Configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n\n| *cmd*\n| Command to execute, which can be given in an shell or exec format as described in <<misc-startup, Startup Arguments>>.\n\n| *interval*\n| Interval for how often to run the healthcheck. The time is specified in seconds, but a time unit can be appended to change this.\n\n| *mode*\n| Mode of the healthcheck. This can be `cmd` which is the default and specifies that the health check should be executed. Or `none` to disable a health check from the base image. Only use this option with `none` for disabling some healthcheck from the base image.\n\n| *retries*\n| How many retries should be performed before the container is to be considered unhealthy.\n\n| *startPeriod*\n| Initialization time for containers that need time to bootstrap. Probe failure during that period will not be counted towards the maximum number of retries. However, if a health check succeeds during the start period, the container is considered started and all consecutive failures will be counted towards the maximum number of retries. Given in seconds, but another time unit can be appended.\n\n| *timeout*\n| Timeout after which healthckeck should be stopped and considered to have failed. Given in seconds, but another time unit can be appended.\n|===\n\nThe following example queries an URL every 10s as an healthcheck:\n\n.Example\n[source,xml]\n----\n<healthCheck>\n  <!-- Check every 5 minutes -->\n  <interval>5m</interval>\n  <!-- Fail if no response after 3 seconds -->\n  <timeout>3s</timeout>\n  <!-- Allow 30 minutes for the container to start before being flagged as unhealthy -->\n  <startPeriod>30m</startPeriod>\n  <!-- Fail 3 times until the container is considerd unhealthy -->\n  <retries>3</retries>\n  <!-- Command to execute in shell form -->\n  <cmd>curl -f http://localhost/ || exit 1</cmd>\n</healthCheck>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/build/_overview.adoc",
    "content": "\n[[build-overview]]\nThere are two different modes how images can be built:\n\n.Inline plugin configuration\nWith an inline plugin configuration all information required to build the image is contained in the plugin configuration. By default its the standard XML based configuration for the plugin but can be switched to a property based configuration syntax as described in the section <<external-configuration,External configuration>>. The XML configuration syntax is recommended because of its more structured and typed nature.\n\nWhen using this mode, the Dockerfile is created on the fly with all instructions extracted from the configuration given.\n\n[[external-dockerfile]]\n.External Dockerfile or Docker archive\nAlternatively an external Dockerfile template or Docker archive can be used. This mode is switched on by using one of these three configuration options within\n\n* *contextDir* specifies docker build context if an external dockerfile is located outside of Docker build context. If not specified, Dockerfile's parent directory is used as build context.\n* *dockerFile* specifies a specific Dockerfile path. The Docker build context directory is set to `contextDir` if given. If not the directory by default is the directory in which the Dockerfile is stored.\n* *dockerArchive* specifies a previously saved image archive to load directly. Such a tar archive can be created with `docker save` or the <<{plugin}:save>> goal. If a `dockerArchive` is provided, no `dockerFile` or `dockerFileDir` must be given.\n* *dockerFileDir* (_deprecated_, use *contextDir*) specifies a directory containing a Dockerfile that will be used to create the image. The name of the Dockerfile is `Dockerfile` by default but can be also set with the option `dockerFile` (see below).\n\nAll paths can be either absolute or relative paths (except when both `dockerFileDir` and `dockerFile` are provided in which case `dockerFile` must not be absolute). A relative path is looked up in `${project.basedir}/src/main/docker` by default. You can make it easily an absolute path by using `${project.basedir}` in your configuration.\n\n.Adding assemblies in Dockerfile mode\nAny additional files located in the `dockerFileDir` directory will also be added to the build context as well.\nYou can also use an assembly if specified in an <<build-assembly,assembly configuration>>.\nHowever, you need to add the files on your own in the Dockerfile with an `ADD` or `COPY` command.\nThe files of the assembly are stored in a build context relative directory `maven/` but can be changed by changing the assembly name with the option `<name>` in the assembly configuration.\n\nE.g. the files can be added with\n\n.Example\n[source,dockerfile]\n----\nCOPY maven/ /my/target/directory\n----\n\nso that the assembly files will end up in `/my/target/directory` within the container.\n\nIf this directory contains a `.maven-dockerignore` (or alternatively, a `.maven-dockerexclude` file), then it is used for excluding files for the build. Each line in this file is treated as a http://ant.apache.org/manual/Types/fileset.html[FileSet exclude pattern] as used by the http://maven.apache.org/plugins/maven-assembly-plugin[maven-assembly-plugin]. It is similar to `.dockerignore` when using Docker but has a slightly different syntax (hence the different name).\n<<ex-build-dockerexclude>> is an  example which excludes all compiled Java classes.\n\n[[ex-build-dockerexclude]]\n.Example `.maven-dockerexclude` or `.maven-dockerignore`\n====\n[source]\n----\ntarget/classes/**  # <1>\n----\n<1> Exclude all compiled classes\n====\n\n\nIf this directory contains a `.maven-dockerinclude` file, then it is used for including only those files for the build. Each line in this file is also treated as a http://ant.apache.org/manual/Types/fileset.html[FileSet exclude pattern] as used by the http://maven.apache.org/plugins/maven-assembly-plugin[maven-assembly-plugin]. <<ex-build-dockerinclude>> shows how to include only jar file that have build to the Docker build context.\n\n[[ex-build-dockerinclude]]\n.Example `.maven-dockerinclude`\n====\n[source]\n----\ntarget/*.jar  # <1>\n----\n<1> Only add jar file to you Docker build context.\n====\n\nExcept for the <<build-assembly,assembly configuration>> all other configuration options are ignored for now.\n\n[[simple-dockerfile-build]]\n.Simple Dockerfile build\n\nWhen only a single image should be built with a Dockerfile no XML configuration is needed at all.\nAll what need to be done is to place a `Dockerfile` into the top-level module directory, alongside to `pom.xml`.\nYou can still configure <<global-configuration, global aspects>> in the plugin configuration, but as soon as you add an `<image>` in the XML configuration, you need to configure also the build explicitly.\n\nThe image name is by default set from the Maven coordinates (`%g/%a:%l`, see <<image-name,Image Name >> for an explanation of the params which are essentially the Maven GAV)\nThis name can be set with the property `docker.name`.\n\nIf you want to add some `<run>` configuration to this image for starting it with `docker:run` then you can add an image configuration but without a `<build>` section in which case the Dockerfile will be picked up, too. This works only for a single image, though.\n\n[[build-filtering]]\n.Filtering\nfabric8-maven-plugin filters given Dockerfile with Maven properties, much like the `maven-resource-plugin` does. Filtering is enabled by default and can be switched off with a build config `<filter>false</filter>`. Properties which we want to replace are specified with the `${..}` syntax.\nReplacement includes Maven project properties such as `${project.artifactId}`, properties set in the build, command-line properties, and system properties. Unresolved properties remain untouched.\n\nThis partial replacement means that you can easily mix it with Docker build arguments and environment variable reference, but you need to be careful.\nIf you want to be more explicit about the property delimiter to clearly separate Docker properties and Maven properties you can redefine the delimiter.\nIn general, the `filter` option can be specified the same way as delimiters in the resource plugin.\nIn particular,  if this configuration contains a * then the parts left, and right of the asterisks are used as delimiters.\n\nFor example, the default `<filter>${*}</filter>` parse Maven properties in the format that we know.\nIf you specify a single character for `<filter>` then this delimiter is taken for both, the start and the end.\nE.g a `<filter>@</filter>` triggers on parameters in the format `@...@`, much like in the `maven-invoker-plugin`.\nUse something like this if you want to clearly separate from Docker builds args.\nThis form of property replacement works for Dockerfile only.\nFor replacing other data in other files targeted for the Docker image, please use the `maven-resource-plugin` or an <<build-assembly,assembly configuration>> with filtering to make them available in the docker build context.\n\n.Example\nThe following example uses a Dockerfile in the directory\n`src/main/docker/demo` and replaces all properties in the format `@property@` within the Dockerfile.\n[source,xml]\n----\n<plugin>\n <configuration>\n   <images>\n     <image>\n       <name>user/demo</name>\n       <build>\n         <dockerFileDir>demo</dockerFileDir>\n         <filter>@</filter>\n       </build>\n     </image>\n   </images>\n </configuration>\n ...\n</plugin>\n----\n\n[[build-plugin]]\n.Build Plugins\n\nThis plugin supports so call *dmp-plugins* which are used during the build phase.\ndmp-plugins are enabled by just declaring a dependency in the plugin declaration:\n\n[source, xml]\n----\n<plugin>\n  <groupId>io.fabric8</groupId>\n  <artifactId>docker-maven-plugin</artifactId>\n\n  <dependencies>\n    <dependency>\n      <groupId>io.fabric8</groupId>\n      <artifactId>run-java-sh</artifactId>\n      <version>1.2.2</version>\n    </dependency>\n  </dependencies>\n</plugin>\n----\n\nThese plugins contain a descriptor `META-INF/maven/io.fabric8/dmp-plugin` with class names, line-by-line:\n\n[source]\n----\nio.fabric8.runsh.RunShLoader\n----\n\nDuring a build with `docker:build`, those classes are loaded and certain fixed method are called.\n\nThe following methods are supported:\n\n[[build-plugin-methods]]\n[cols=\"1,5\"]\n|===\n| Method | Description\n\n| *addExtraFiles*\n| A _static_ method called by dmp with a single `File` argument. This will point to a directory `docker-extra` which can be referenced easily by a Dockerfile or an assembly. A dmp plugin typically will create an own subdirectory to avoid a clash with other dmp-plugins.\n|===\n\nIf a configured plugin does not provide method of this name and signature, then it will be simply ignored.\nAlso, no interface needs to be implemented to keep the coupling low.\n\nThe following official dmp-plugins are known and supported:\n\n[[build-plugins-official]]\n[cols=\"1,1,5\"]\n|===\n| Name | G,A | Description\n\n| https://github.com/fabric8io-images/run-java-sh[run-java.sh]\n| `fabric8.io`, `run-java`\n| General purpose startup script fo running Java applications. The dmp plugin creates a `target/docker-extra/run-java/run-java.sh` which can be included in a Dockerfile (see the example above). See the https://github.com/fabric8io-images/run-java-sh[run-java.sh Documentation] for more details.\n|===\n\nCheck out `samples/run-java` for a fully working example.\n"
  },
  {
    "path": "src/main/asciidoc/inc/external/_docker_compose.adoc",
    "content": "\nThis plugin supports also configuration via a  https://docs.docker.com/compose/[docker-compose] file, especially for running containers specified in `docker-compose.yml`. Docker Compose handling is available also as an external configuration provider.\n\n.Example\n[source,xml]\n----\n<image>\n  <alias>webapp</alias> <!--1-->\n  <name>fabric8/compose-demo:latest</name>\n\n  <external> <!--2-->\n     <type>compose</type> <!--3-->\n     <basedir>src/main/docker</basedir> <!--4-->\n     <composeFile>docker-compose.yml</composeFile>\n  </external>\n\n  <build> <!--5-->\n    <assembly>....</assembly>\n  </build>\n  <run>...</run>\n  <watch>...</watch>\n</image>\n----\n<1> The alias of the image is used as correlation key mapping to a service in the Docker Compose file\n<2> An `<external>` configuration handler needs to be used for Docker Compose support\n<3> The type for the external configuration provider must be set to **compose**\n<4> Additional configuration for the handler where to find the compose file\n<5> Extra `<build>`, `<run>` and `<watch>` configuration can be provided which are used as default configuration for the Docker compose service `webapp` (as specified with the alias)\n\nThe following options can be provided:\n\n.Docker compose configuration\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Default\n\n| *basedir*\n| Basedir where to find the compose file and which is also used as the current directory when examing the compose file.  Any relative volume bindings will be resolved relative to this directory.\n| `${basedir}/src/main/docker`\n\n| *composeFile*\n| Name of the compose file to use\n| `docker-compose.yml`\n\n| *ignoreBuild*\n| Ignore the compose file's `build:` section and use the plugin's build configuration exclusively.\n| `false`\n|===\n\nThe Docker Compose file can contain variables as described in the https://docs.docker.com/compose/compose-file/#/variable-substitution[Docker Compose documentation]. These are substituted with Maven project properties. Please note, when the `docker-compose.yml` with variables is to be used with the `docker-compose` CLI command, that these variables must also be valid environment variables (i.e. must not contain a `.`).\n\nIn addition to the `docker-compose.yml` you can add all known options for <<build-configuration,<build> >>, <<start-configuration,<run> >> and <<watch-configuration,<watch> >> configuration elements which are then used as defaults and are overwritten by the configuration defined in the `docker-compose.yml` file. The merging between the XML configuration and the information found in `docker-compose.yml` is correlated via the `<alias>` name. E.g. if the XML image configuration is aliased with `webapp` then its is used as a default configuration for a Docker Compose _service_ name `webapp`. All other services defined in the compose file are left untouched.\n\n=== Limitations\n\nThe following Docker Compose file keywords are not yet supported:\n\n* `cgroup_parent`, `devices`, `env_file`, `expose`, `pid`, `security_opt`, `stop_signal`, `cpu_quota`, `ipc`, `mac_address`, `read_only` are not yet supported (but might be in a future version).\n* `extend` for including other Docker Compose files is not yet implemented.\n* Only **services** are currently evaluated, there is no supported yet for **volumes** and **networks**.\n"
  },
  {
    "path": "src/main/asciidoc/inc/external/_property_configuration.adoc",
    "content": "\nFor simple needs the image configuration can be completely defined via\nMaven properties which are defined outside of this plugin's\nconfiguration. Such a property based configuration can be selected\nwith an `<type>` of `properties`. As extra configuration a prefix for the\nproperties can be defined which by default is `docker`.\n\nFor single-image configurations it is also possible to active property\nbased configuration via an externally set property.\n\nBy default, property based configuration uses only properties, ignoring\nany `<build>` and `<run>` sections. To combine values from both sources,\nuse the <<combining-property-config,property mode configuration>>.\n\nProperties are read from the Maven project (defined in `<properties>` or global\nMaven configuration from `settings.xml`) and, since 0.25.0, from any `-D`\nflags given to Maven (takes priority over project properties).\n\n.Example\n[source,xml]\n----\n<image>\n  <external>\n     <type>properties</type>\n     <prefix>docker</prefix> <!-- this is the default -->\n     <mode>only</mode> <!-- this is the default -->\n  </external>\n</image>\n----\n\nGiven this example configuration a single image configuration is built\nup from the following properties, which correspond to the corresponding\nvalues in the `<build>` and `<run>` sections. A build configuration is only created\nwhen a `docker.from` or a `docker.fromExt` is set.\n\n\n.External properties\n[cols=\"1,5\"]\n|===\n| *docker.alias*\n| Alias name\n\n| *docker.args.BUILDVAR*\n| Set the value of a build variable. The syntax is the same as for specifying environment variables (see below).\n\n| *docker.assembly.baseDir*\n| Directory name for the exported artifacts as described in an assembly (which is `/maven` by default).\n\n| *docker.assembly.descriptor*\n| Path to the assembly descriptor when building an image\n\n| *docker.assembly.descriptorRef*\n| Name of a predefined assembly to use.\n\n| *docker.assembly.exportBaseDir*\n| If `true` export base directory\n\n| *docker.assembly.ignorePermissions*\n| If set to `true` existing file permissions are ignored when creating the assembly archive. Deprecated, use a permission mode of `ignore` instead.\n\n| *docker.assembly.permissions*\n| can be `ignore` to use the permission as found on files regardless on any assembly configuration, `keep` to respect the assembly provided permissions, `exec` for setting the executable bit on all files (required for Windows when using an assembly mode `dir`) or `auto` to let the plugin select `exec` on Windows and `keep` on others. `keep` is the default value.\n\n| *docker.assembly.dockerFileDir*\n| specifies a directory containing an external Dockerfile that will be used to create the image. This is deprecated please use `docker.dockerFileDir` or `docker.dockerFile` instead.\n\n| *docker.noCache*\n| Don't use Docker's build cache. This can be overwritten by setting a system property `docker.noCache` when running Maven.\n\n| *docker.bind.idx*\n| Sets a list of paths to bind/expose in the container\n\n| *docker.buildArg.VARIABLE*\n| Set a ARG to be available during build of image. *Note*: this is handled separately from external configuration, and is always available. See <<property-buildargs,Build Args>> for more details.\n\n| *docker.capAdd.idx*\n| List of kernel capabilities to add to the container\n\n| *docker.capDrop.idx*\n| List of kernel capabilities to remove from the container\n\n| *docker.cleanup*\n| Cleanup dangling (untagged) images after each build (including any containers created from them). Default is `try` (which wont fail the build if removing fails), other possible values are `none` (no cleanup) or `remove` (remove but fail if unsuccessful)\n\n| *docker.cmd*\n| Command to execute. This is used both when running a container and as default command when creating an image.\n\n| *docker.cpus*\n| Specify how much of the available CPU resources a container can use\n\n| *docker.cpuset*\n| Limit the container to specific CPUs or cores. This can be provided either as a comma-separated list or a hyphen-separated range.\n\n| *docker.cpushares*\n| Set the proportion of the host machine's cpu cycles available to the container\n\n| *docker.cacheFrom*\n| List of image names to use as cache sources\n\n| *docker.domainname*\n| Container domain name\n\n| *docker.dns.idx*\n| List of dns servers to use\n\n| *docker.dnsSearch.idx*\n| List of dns search domains\n\n| *docker.dockerArchive*\n| specify an archive which can be loaded with `docker load`. Use this as an alternative to `docker.dockerFile` or `docker.dockerFileDir`\n\n| *docker.dockerFile*\n| specifies a Dockerfile to use. This property must point to the Dockerfile itself.\n\n| *docker.dockerFileDir*\n| specifies a directory containing an external dockerfile that will be used to create the image. The dockerfile must be name `Dockerfile`\n\n| *docker.entrypoint*\n| Container entry point\n\n| *docker.exposedPropertyKey*\n| Property part for the exposed container properties like internal IP addresses as described in <<start-overview, docker:start>>.\n\n| *docker.env.VARIABLE*\n| Sets an environment variable used in build and run. E.g. `<docker.env.JAVA_OPTS>-Xmx512m</docker.env.JAVA_OPTS>` sets the environment variable `JAVA_OPTS`. Multiple such entries can be provided. This environment is used both for building images and running containers. The value cannot be empty but can contain Maven property names which are resolved before the Dockerfile is created.\n\n| *docker.envBuild.VARIABLE*\n| Sets an environment variable used in build only. E.g. `<docker.envBuild.JAVA_OPTS>-Xmx512m</docker.envBuild.JAVA_OPTS>` sets the environment variable `JAVA_OPTS`. Multiple such entries can be provided. This environment is building images only. The value cannot be empty but can contain Maven property names which are resolved before the Dockerfile is created.\n\n| *docker.envRun.VARIABLE*\n| Sets an environment variable used in run only. E.g. `<docker.envRun.JAVA_OPTS>-Xmx512m</docker.envRun.JAVA_OPTS>` sets the environment variable `JAVA_OPTS`. Multiple such entries can be provided. This environment is used both for running containers only. The value cannot be empty but can contain Maven property names which are resolved before the Dockerfile is created.\n\n| *docker.envPropertyFile*\n| specifies the path to a property file whose properties are used as environment variables in run. The environment variables takes precedence over any other environment variables specified.\n\n| *docker.extraHosts.idx*\n| List of `host:ip` to add to `/etc/hosts`\n\n| *docker.filter*\n| Enable and set the delimiters for property replacements. By default properties in the format `${..}` are replaced with Maven properties. You can switch off property replacement by setting this property to `false`. When using a single char like `@` then this is used as a delimiter (e.g `@...@`). See <<build-filtering, Filtering>> for more details.\n\n| *docker.from*\n| Base image for building an image. Must be set when an image is created (or `fromExt`)\n\n| *docker.fromExt.VARIABLE*\n| Base image for building an image (extended format), which also triggers a build of an image.\n\n| *docker.healthcheck.cmd*\n| Command to use for a healthcheck\n\n| *docker.healthcheck.interval*\n| Interval for how often to run a healthcheck (in seconds or with a given time unit)\n\n| *docker.healthcheck.mode*\n| If se to `none` disable a healthcheck from a base image\n\n| *docker.healthcheck.retries*\n| Number of retries for how often to retry a healthcheck until it is considered to have failed\n\n| *docker.healthcheck.startPeriod*\n| Initialization time for containers that need time to bootstrap. Probe failure during that period will not be counted towards the maximum number of retries. However, if a health check succeeds during the start period, the container is considered started and all consecutive failures will be counted towards the maximum number of retries. (in seconds or with a given time unit)\n\n| *docker.healthcheck.timeout*\n| Timeout after which a healthcheck command is considered to be failed (in seconds or with a given time unit)\n\n| *docker.hostname*\n| Container hostname\n\n| *docker.imagePropertyConfiguration*\n| Special property to activate property configuration without altering XML file (see <<combining-property-config-externally,Activating property configuration externally>>).\n\n| *docker.imagePullPolicy.build*\n| Specific pull policy used when building images. See <<image-pull-policy,imagePullPolicy>> for the possible values.\n\n| *docker.imagePullPolicy.run*\n| Specific pull policy used for downloading images to run. See <<image-pull-policy,imagePullPolicy>> for the possible values.\n\n| *docker.labels.LABEL*\n| Sets a label which works similarly like setting environment variables.\n\n| *docker.loadNamePattern*\n| Search the archive specified in `docker.dockerArchive` for the specified image name and creates a tag from the matched name to the build image name specified in `docker.name`.\n\n| *docker.log.enabled*\n| Use logging (default: `true`)\n\n| *docker.log.prefix*\n| Output prefix\n\n| *docker.log.color*\n| ANSI color to use for the prefix\n\n| *docker.log.date*\n| Date format for printing the timestamp\n\n| *docker.log.driver.name*\n| Name of an alternative log driver\n\n| *docker.log.driver.opts.VARIABLE*\n| Logging driver options (specified similarly as in `docker.env.VARIABLE`)\n\n| *docker.links.idx*\n| defines a list of links to other containers when starting a container. _idx_ can be any suffix which is not used except when _idx_ is numeric it specifies the order within the list (i.e. the list contains first entries with numeric indexes sorted and the all non-numeric indexes in arbitrary order). For example `<docker.links.1>db</docker.links.1>` specifies a link to the image with alias 'db'.\n\n| *docker.maintainer*\n| defines the maintainer's email as used when building an image\n\n| *docker.memory*\n| Container memory (in bytes)\n\n| *docker.memorySwap*\n| Total memory (swap + memory) `-1` to disable swap\n\n| *docker.name*\n| Image name\n\n| *docker.namingStrategy*\n| Container naming (either `none` or `alias`)\n\n| *docker.network.mode*\n| Network mode to use which can be `none`, `host`, `bridged`, `container` or `custom`\n\n| *docker.network.name*\n| Name of the custom network when mode is `custom`, or for mode `container` the image alias name used to create the container.\n\n| *docker.network.alias.idx*\n| One or more aliase for a custom network. Only used when the network mode is `custom`\n\n| *docker.noCache*\n| Don't use a cache when building the image\n\n| *docker.squash*\n| Squash newly built layers into a single layer (API 1.25+, need to be enabled in the Docker daemon configuration)\n\n| *docker.optimise*\n| if set to true then it will compress all the `runCmds` into a single RUN directive so that only one image layer is created.\n\n| *docker.portPropertyFile*\n| specifies a path to a port mapping used when starting a container.\n\n| *docker.ports.idx*\n| Sets a port mapping. For example `<docker.ports.1>jolokia.ports:8080<docker.ports.1>` maps the container port 8080 dynamically to a host port and assigns this host port to the Maven property `${jolokia.port}`. See <<_port-mapping,Port mapping>> for possible mapping options. When creating images images only the right most port is used for exposing the port. For providing multiple port mappings, the index should be count up.\n\n| *docker.registry*\n| Registry to use for pushing images.\n\n| *docker.restartPolicy.name*\n| Container restart policy\n\n| *docker.restartPolicy.retry*\n| Max restart retries if `on-failure` used\n\n| *docker.run.idx*\n| List of commands to `RUN` when creating the image\n\n| *docker.securityOpts.idx*\n| List of `opt` elements to specify kernel security options to add to the container. For example `docker.securityOpt.1=seccomp=unconfined`\n\n| *docker.shmsize*\n| Size of `/dev/shm` in bytes.\n\n| *docker.tags.idx*\n| List of tags to apply to a built image\n\n| *docker.tmpfs.idx*\n| One or more mount points for a *tmpfs*. Add mount options after a `:`\n\n| *docker.ulimits.idx*\n| Ulimits for the container. Ulimit is specified with a soft and hard limit `<type>=<soft limit>[:<hard limit>]`. For example `docker.ulimits.1=memlock=-1:-1`\n\n| *docker.user*\n| User to switch to at the end of a Dockerfile. Not to confuse with `docker.username` which is used for authentication when interacting with a Docker registry.\n\n| *docker.volumes.idx*\n| defines a list of volumes to expose when building an image\n\n| *docker.volumesFrom.idx*\n| defines a list of image aliases from which the volumes should be mounted of the container. The list semantics is the same as for links (see above). For examples `<docker.volumesFrom.1>data</docker.volumesFrom.1>` will mount all volumes exported by the `data` image.\n\n| *docker.wait.http.url*\n| URL to wait for during startup of a container\n\n| *docker.wait.http.method*\n| HTTP method to use for ping check\n\n| *docker.wait.http.status*\n| Status code to wait for when doing HTTP ping check\n\n| *docker.wait.time*\n| Amount of time to wait during startup of a container (in ms)\n\n| *docker.wait.log*\n| Wait for a log output to appear.\n\n| *docker.wait.exec.postStart*\n| Command to execute after the container has start up.\n\n| *docker.wait.exec.preStop*\n| Command to execute before command stops.\n\n| *docker.wait.exec.breakOnError*\n| If set to \"true\" then stop the build if the a `postStart` or `preStop` command failed\n\n| *docker.wait.shutdown*\n| Time in milliseconds to wait between stopping a container and removing it.\n\n| *docker.wait.tcp.mode*\n| Either `mapped` or `direct` when waiting on TCP connections\n\n| *docker.wait.tcp.host*\n| Hostname to use for a TCP wait checks\n\n| *docker.wait.tcp.port.idx*\n| List of ports to use for a TCP check.\n\n| *docker.wait.kill*\n| Time in milliseconds to wait between sending SIGTERM and SIGKILL to a container when stopping it.\n\n| *docker.workdir*\n| Container working directory where the image is build in\n\n| *docker.workingDir*\n| Current Working dir for commands to run in when running containers\n|===\n\nMultiple property configuration handlers can be used if they\nuse different prefixes. As stated above the environment and ports\nconfiguration are both used for running container and building\nimages. If you need a separate configuration you should use explicit\nrun and build configuration sections.\n\n[[combining-property-config]]\n.Combining property and XML configuration\nBy default the property handler will only consider properties and ignore any other image\nconfiguration in the XML/POM file. This can be changed by adding the `<mode>`\nconfiguration (since version 0.25.0), which can have one of the following values:\n\n.Property mode\n[cols=\"1,5\"]\n|===\n|`only`\n| Only look at properties, ignore any `<run>` or `<build>` sections for this image. This is the default, and also the behavior in versions before 0.25.0.\n\n|`override`\n| Use property if set, else fall back to value found in `<run>` or `<build>` sections for this image.\n\n|`fallback`\n| Use value found in `<run>` or `<build>` sections for this image, else fall back to to property value.\n\n|`skip`\n| Effectively disable properties, same as not specifying the `<external>` section at all.\n|===\n\n[[combining-property-config-externally]]\n.Activating property configuration externally\nIt also possible to activate property configuration by setting the property `docker.imagePropertyConfiguration` to a\nvalid `property mode`, without adding an `<external>` section. The plugin will then use any properties with default `docker.` prefix.\nThis can be useful if most of the configuration is specified in XML/POM file, but there\nis need to override certain configuration values without altering the POM file (instead add this to a parent POM or\nglobal settings.xml).\n\nIf set in parent POM, but not wanted in specific project, the property could be overriden locally with the value `skip`\nto disabled property configuration for that particular project.\nIf set in settings.xml however, by Maven design, that value will always take precedence over any properties defined in\npom.xml.\n\nFor configurations with multiple images, using this property will by default produce an error. All images would then\nuse the same `docker` property prefix, resulting in multiple identical configurations.\nThis can be overruled by adding an explicit <external> configuration element with an explicit <prefix> to all\nimages (or at least all but one). Normally you'd want to use different prefix for each image, but if explicitly set\nit does allow you to use the same prefix (even `docker`) on all images. This is useful in case you just want to share\na few properties. This only makes sense when `property mode` is _override_ or _fallback_ and image-specific configuration\nare defined in the POM configuration.\n\nFor examples, see <<externally-property-config-example, here>>\n\n.Merging POM and property values\nFor some fields it may be desired to merge values from both POM and properties. For example, in a certain run environment\nwe might want to inject a `http_proxy` environment variable, but we do not want to add this to the POM file.\n\nThis is solved using a *Combine policy* which can be either `replace` or `merge`. Merge is only available for\nconfiguration of Map or List type. For scalar values such as strings and integers, it is not supported.\nFor Maps, both sources are merged, with the priority source taking precedence. For Lists, they are concatenated, with values\nfrom the priority source being added first.\n\nCombine policy is specified per configuration key/property, and the default in most cases is currently `replace`. The following\nkeys have `merge` as default policy:\n\n* docker.args\n* docker.envBuild\n* docker.envRun\n* docker.labels\n* docker.ports\n* docker.tags\n\nThis can be overridden individually for all configuration keys (of map/list type) by setting an additional property suffixed `._combine`.\nFor example, to not merge ports, set `docker.ports._combine=replace`, and to enable merging of dns, set `docker.dns._combine=merge`.\n\n\n.Example, properties only\n[source,xml]\n----\n<properties>\n  <docker.name>jolokia/demo</docker.name>\n  <docker.alias>service</docker.alias>\n  <docker.from>consol/tomcat:7.0</docker.from>\n  <docker.assembly.descriptor>src/main/docker-assembly.xml</docker.assembly.descriptor>\n  <docker.env.CATALINA_OPTS>-Xmx32m</docker.env.CATALINA_OPTS>\n  <docker.label.version>${project.version}</docker.label.version>\n  <docker.ports.jolokia.port>8080</docker.ports.jolokia.port>\n  <docker.wait.url>http://localhost:${jolokia.port}/jolokia</docker.wait.url>\n</properties>\n\n<build>\n  <plugins>\n    <plugin>\n      <groupId>io.fabric8</groupId>\n      <artifactId>docker-maven-plugin</artifactId>\n      <configuration>\n        <images>\n          <image>\n            <external>\n              <type>properties</type>\n              <prefix>docker</prefix>\n            </external>\n          </image>\n        </images>\n      </configuration>\n    </plugin>\n  </plugins>\n</build>\n----\n\n\n[[externally-property-config-example]]\n.Example, combining properties and XML/POM configuration\n[source,xml]\n----\n<properties>\n  <docker.assembly.descriptor>src/main/docker-assembly.xml</docker.assembly.descriptor>\n  <docker.env.CATALINA_OPTS>-Xmx32m</docker.env.CATALINA_OPTS>\n  <docker.label.version>${project.version}</docker.label.version>\n  <docker.ports.jolokia.port>8080</docker.ports.jolokia.port>\n  <docker.wait.url>http://localhost:${jolokia.port}/jolokia</docker.wait.url>\n</properties>\n\n<build>\n  <plugins>\n    <plugin>\n      <groupId>io.fabric8</groupId>\n      <artifactId>docker-maven-plugin</artifactId>\n      <configuration>\n        <images>\n          <image>\n            <external>\n              <type>properties</type>\n              <prefix>docker</prefix>\n              <mode>override</mode>\n            </external>\n\n            <name>jolokia/demo</name>\n            <alias>service</alias>\n\n            <build>\n              <from>consol/tomcat:7.0</from>\n              <labels>\n                <software>tomcat</software>\n              </labels>\n            </build>\n          </image>\n        </images>\n      </configuration>\n    </plugin>\n  </plugins>\n</build>\n----\n\nThis would build the same image as the previous example.\nIf instead built with `mvn docker:build -Pdocker.from=console/tomcat:8.0 -Ddocker.tags.0=tc8-test` it would build from that image instead, and also add that tag to the image.\n\nIf `-Ddocker.labels.status=beta` is added, the image would be given two labels: `status=beta` and `software=tomcat`.\nIf `-Ddocker.labels._combine=replace` is added, the image would be given one label only: `status=beta`.\n\n\n.Example, external activation of property configuration, single image\n\nGlobal ~/.m2/settings.xml file:\n[source,xml]\n----\n<profiles>\n  <profile>\n    <id>http-proxy</id>\n    <properties>\n      <docker.buildArg.http_proxy>http://proxy.example.com:8080</docker.buildArg.http_proxy>\n      <docker.runArg.http_proxy>http://proxy.example.com:8080</docker.runArg.http_proxy>\n      <docker.imagePropertyConfiguration>override</docker.imagePropertyConfiguration>\n    </properties>\n  </profile>\n</profiles>\n----\n\n[source,xml]\n----\n<build>\n  <plugins>\n    <plugin>\n      <groupId>io.fabric8</groupId>\n      <artifactId>docker-maven-plugin</artifactId>\n      <configuration>\n        <images>\n          <image>\n            <name>jolokia/demo</name>\n            <alias>service</alias>\n            <build>\n              <from>consol/tomcat:7.0</from>\n            </build>\n          </image>\n        </images>\n      </configuration>\n    </plugin>\n  </plugins>\n</build>\n----\n\nWhen the plugin is executed, on a machine with the given settings.xml, the plugin will see the `docker.imagePropertyConfiguration` configuration and enable\nthe property merging feature. When building, it will inject the http_proxy build ARG, and when running, it will inject the http_proxy ENV variable.\nThe rest of the configuration will be sourced from the XML, unless the Maven project has any other `docker.*` properties defined.\n\n\n.Example, external activation of property configuration, two images\nUsing the same global ~/.m2/settings.xml file as in previous example, but with two image definitions and no extra configuration will cause\nan error, saying that you cannot use property docker.imagePropertyConfiguration on projects with multiple images.\n\nBy adding an explicit external configuration directive with the same prefix in both images, this error is disabled.\n\n[source,xml]\n----\n<build>\n  <plugins>\n    <plugin>\n      <groupId>io.fabric8</groupId>\n      <artifactId>docker-maven-plugin</artifactId>\n      <configuration>\n        <images>\n          <image>\n            <external>\n              <type>properties</type>\n              <prefix>docker</prefix>\n              <mode>override</mode>\n            </external>\n\n            <name>jolokia/demo</name>\n            <alias>service</alias>\n            <build>\n              <from>consol/tomcat:7.0</from>\n            </build>\n          </image>\n\n          <image>\n            <external>\n              <type>properties</type>\n              <prefix>docker</prefix>\n              <mode>override</mode>\n            </external>\n\n            <name>jolokia/demo2</name>\n            <alias>service2</alias>\n            <build>\n              <from>consol/tomcat:7.0</from>\n            </build>\n          </image>\n        </images>\n      </configuration>\n    </plugin>\n  </plugins>\n</build>\n----\n\nThe behaviour will now be same as previous example.\nNote that you must explicitly state `<mode>override</mode>`, otherwise it will use the default `only`."
  },
  {
    "path": "src/main/asciidoc/inc/image/_configuration.adoc",
    "content": "\n[[config-image]]\n.Image Configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *name*\n| Each `<image>` configuration has a mandatory, unique docker\nrepository _name_. This can include registry and tag parts, but also placeholder parameters. See below for a detailed explanation.\n\n| *alias*\n| Shortcut name for an image which can be used for\nidentifying the image within this configuration. This is used when\nlinking images together or for specifying it with the global *image* configuration element.\n\n| <<registry, *registry*>>\n| Registry to use for this image. If the `name` already contains a registry this takes precedence. See <<registry,Registry handling>> for more details.\n\n| <<config-image-build, *build*>>\n| Element which contains all the configuration aspects when doing a <<{plugin}:build>>. This element can be omitted if the image is only pulled from a registry e.g. as support for integration tests like database images.\n\nifeval::[\"{plugin}\" == \"docker\"]\n| <<config-image-run, *run*>>\n| Element which describe how containers should be\ncreated and run when <<{plugin}:start>> is called. If this image is only used a _data container_ (i.e. is supposed only to be mounted as a volume) for exporting artifacts via volumes this section can be missing.\n\n| <<external-configuration, *external*>>\n| Specification of external configuration as an alternative to this XML based configuration with `<run>` and `<build>`. It contains a `<type>` element specifying the handler for getting the configuration. See <<external-configuration,External configuration>> for details.\nendif::[]\n\n| *removeNamePattern*\n| When this image is to be removed by <<{plugin}:remove>>, use this <<name-patterns, pattern list>> to find images to\nremove rather than just using the name.\n\n| *stopNamePattern*\n| When containers associated with this image will be stopped by <<{plugin}:stop>>, use this <<name-patterns, pattern list>>\nto find containers to remove rather than just using the associated container name.\n\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/image/_example.adoc",
    "content": "\n.Example\n[source,xml]\n----\n<configuration>\n  ....\n  <images>\n    <image>\n      <name>%g/docker-demo:0.1</name>\n      <alias>service</alias>\n      <run>....</run>\n      <build>....</build>\n    </image>\n  </images>\n</configuration>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/image/_example_imagesMap.adoc",
    "content": "\n.Example\n[source,xml]\n----\n<configuration>\n  ....\n  <imagesMap>\n    <service>\n      <name>%g/docker-demo:0.1</name>\n      <run>....</run>\n      <build>....</build>\n    </service>\n  </images>\n</configuration>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/image/_naming.adoc",
    "content": "[[image-name]]\n## Image Names\nWhen specifying the image name in the configuration with the `<name>` field you can use several placeholders which are replaced during runtime by this plugin. In addition you can use regular Maven properties which are resolved by Maven itself.\n\n[cols=\"1,5\"]\n|===\n| Placeholder | Description\n\n| *%g*\n| The last part of the Maven group name, sanitized so that it can be used as username on GitHub. Only the part after the last dot is used. E.g. for a group id `io.fabric8` this placeholder would insert `fabric8`\n\n| *%a*\n| A sanitized version of the artefact id so that it can be used as part of an Docker image name. I.e. it is converted to all lower case (as required by Docker)\n\n| *%v*\n| The project version. Synonym to `${project.version}`\n\n| *%l*\n| If the project version ends with `-SNAPSHOT` then this placeholder is `latest`, otherwise its the full version (same as `%v`)\n\n| *%t*\n| If the project version ends with `-SNAPSHOT` this placeholder resolves to `snapshot-<timestamp>` where timestamp has the date format `yyMMdd-HHmmss-SSSS` (eg `snapshot-`). This feature is especially useful during development in oder to avoid conflicts when images are to be updated which are still in use. You need to take care yourself of cleaning up old images afterwards, though.\n|===\n\nifeval::[\"{plugin}\" == \"docker\"]\n[[container-name]]\n## Container Names\n\nSimilar to image name placeholders, for starting and stopping containers and alternate set of placeholders can be configured in order to the name the containers to create.\n\nThese placeholders can be used in the top-level configuration value `containerNamePattern` which is used globally for every container that is created.\nThis global pattern can be overwritten individually by each image's <<config-image-run, *run*>> configuration.\nIf neither is given, then by default the pattern `%n-%i` is used.\n\nWhen specifying the container name pattern the following placeholders can be used:\n\n[cols=\"1,5\"]\n|===\n| Placeholder | Description\n\n| *%a*\n| The `<alias>` of an image which must be set. The alias is set in the top-level image configuration\n\n| *%n*\n| A sanitized version of the imag's short name from which this container is created. \"Sanitized\" means that any non letter, digit, dot or dash is replaced by an underscore.\n\n| *%t*\n| The build time stamp. This is the timestamp which created during the building of an image and locally cached. A rebuild of the image will update the timestamp.\n\n| *%i*\n| An index which is incremented if a container has already been created. With this parameter it is easily possible to have multiple, similar containers. See the example below for more details.\n\n|===\n\nYou can combine the placeholders in any combination and will be resolved during `docker:start`, `docker:stop` and `docker:watch`.\n\nThe following example is using a container name pattern of `%n-%i` which is also the default.\nGiven an image `fabric8io/dmp-sample-jolokia:latest`, then during `mvn docker:start` a container with the name `dmp-sample-jolokia-1` is first tried.\nIf there is already a container with this name, then `dmp-sample-jolokia-2` is the second attempt.\nThis goes on until a \"free\" name is found.\n\nSimilar, when stopping containers with `mvn docker:stop` then only the container with the highest index is stopped.\nHowever, if you don't use an index via `%i` then _all_ containers started with `docker:start` are stopped.\nUse `mvn docker:stop -Ddocker.allContainers` to also stop every container named via a `%i` pattern.\nendif::[]\n\n[[name-patterns]]\n## Name Patterns\n\nGoals that need to refer to images or containers where the name of the image or container is not fixed may support name\npatterns for matching. Patterns can use an Ant-like syntax or Java regular expressions.\n\n### Ant-like Name Patterns\nAnt path matching patterns that operate on path names use the convention that a `$$*$$` matches within a single path\ncomponent, while `$$**$$` can match multiple components.\n\nAdapting this style to image names requires some tweaks since image names may include registry information, a path-like\nrepository name and a tag. Consider the following image names:\n\n* `alpine:latest`\n* `fluent/fluentd:edge`\n* `quay.io/operator-framework/helm-operator:v0.9.0`\n* `company.local:5000/division/project/artifact:version`\n\nUnlike in Ant matching of file system paths, the `:` is an important marker, but only at the end where it separates the\nversion from the repository. Also, patterns that match repository names need to anticipate that there may be a registry\nname at the beginning if the image has been tagged for pushing to a registry.\n\nTaking this into account, the name pattern wildcards are:\n\n* `?` matches a single character\n* `*` matches zero or more characters, up to the next slash or the tag separator\n* `**` matches zero or more characters, up to the tag separator\n* `**/` matches zero or more characters, up to the tag separator, and ensures that if any characters are matched, the\nfinal character matched is a slash\n\n#### Examples of Ant-like Name Patterns\n\n[cols=\"1,3,3\"]\n|===\n| Pattern | Matches | Does Not Match\n\n| `$$**tomcat:jdk-11*$$`\na|\n* megacorp/tomcat:jdk-11-alpine\n* megacorp.com:5000/megacorp/project-x-tomcat:jdk-11\n\na|\n* megacorp/tomcat__-operator__:jdk-11\n* megacorp/project-x-tomcat:__jdk-9-alpine__\n\n| `$$**/megacorp/tomcat:*alpine$$`\na|\n* megacorp/tomcat:alpine\n* megacorp.com:5000/megacorp/tomcat:jdk-11-alpine\n\na|\n* megacorp/tomcat:__jdk-11__\n* megacorp.com:5000/__ultra__megacorp/tomcat:jdk-11-alpine\n\n| `$$megacorp/*-operator:*$$`\na|\n* megacorp/tomcat-operator:alpine\n* megacorp/mysql-operator:latest\n\na|\n* megacorp/__tomcat__:jdk-11\n* __megacorp.com:5000/__megacorp/tomcat-operator:alpine\n\n|===\n\n### Java Regular Expression Patterns\n\nTo indicate that a name pattern is a Java regular expression, prefix the regular expression with `$$%regex[$$` and\nsuffix with `$$]$$`.\n\n#### Examples of Java Regular Expression Patterns\n\n[cols=\"1,3,3\"]\n|===\n| Pattern | Matches | Does Not Match\n\n| `$$%regex[j(dk\\|re)-11]$$`\na|\n* megacorp/tomcat:jdk-11-alpine\n* openjdk-11:latest\n\na|\n* openjdk:11-alpine\n\n| `$$%regex[tomcat]$$`\na|\n* megacorp/tomcat:alpine\n* megacorp.com:5000/tomcat-projects/project-x:latest\n\na|\n* megacorp/topcat:edge\n\n|===\n\n[[name-pattern-lists]]\n### Name Pattern Lists\n\nIn goals such as <<{plugin}:stop>> and <<{plugin}:remove>> where multiple patterns are supported, separate patterns\nwith commas.\n\n"
  },
  {
    "path": "src/main/asciidoc/inc/misc/_env.adoc",
    "content": "\n\nWhen creating a container one or more environment variables can be set via configuration with the `env` parameter\n\n.Example\n[source,xml]\n----\n<env>\n  <JAVA_HOME>/opt/jdk8</JAVA_HOME>\n  <CATALINA_OPTS>-Djava.security.egd=file:/dev/./urandom</CATALINA_OPTS>\n</env>\n----\n\nIf you put this configuration into profiles you can easily create various test variants with a single image (e.g. by switching the JDK or whatever).\n\nIt is also possible to set the environment variables from the outside of the plugin's configuration with the parameter `envPropertyFile`. If given, this property file is used to set the environment variables where the keys and values specify the environment variable. Environment variables specified in this file override any environment variables specified in the configuration.\n\nLabels can be set inline the same way as environment variables:\n\n.Example\n[source,xml]\n----\n<labels>\n   <com.example.label-with-value>foo</com.example.label-with-value>\n   <version>${project.version}</version>\n   <artifactId>${project.artifactId}</artifactId>\n</labels>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/misc/_startup.adoc",
    "content": "\n\nUsing `entrypoint` and `cmd` it is possible to specify the https://docs.docker.com/reference/builder/#entrypoint[entry point]\nor https://docs.docker.com/reference/builder/#cmd[cmd] for a container.\n\nThe difference is, that an `entrypoint` is the command that always be executed, with the `cmd` as argument. If no `entrypoint` is provided, it defaults to `/bin/sh -c` so any `cmd` given is executed with a shell. The arguments given to `docker run` are always given as arguments to the\n`entrypoint`, overriding any given `cmd` option. On the other hand if no extra arguments are given to `docker run` the default `cmd` is used as argument to `entrypoint`.\n\n****\nSee this http://stackoverflow.com/questions/21553353/what-is-the-difference-between-cmd-and-entrypoint-in-a-dockerfile[stackoverflow question] for a detailed explanation.\n****\n\nAn entry point or command can be specified in two alternative formats:\n\n.Entrypoint and Command Configuration\n[cols=\"1,5\"]\n|===\n| Mode | Description\n\n| *shell*\n| Shell form in which the whole line is given to `shell -c` for interpretation.\n\n| *exec*\n| List of arguments (with inner `<args>`) arguments which will be given to the `exec` call directly without any shell interpretation.\n|===\n\nEither shell or params should be specified.\n\n.Example\n[source,xml]\n----\n<entrypoint>\n   <!-- shell form  -->\n   <shell>java -jar $HOME/server.jar</shell>\n</entrypoint>\n----\n\nor\n\n.Example\n[source,xml]\n----\n<entrypoint>\n   <!-- exec form  -->\n   <exec>\n     <arg>java</arg>\n     <arg>-jar</arg>\n     <arg>/opt/demo/server.jar</arg>\n   </exec>\n</entrypoint>\n----\n\nThis can be formulated also more dense with:\n\n.Example\n[source,xml]\n----\n<!-- shell form  -->\n<entrypoint>java -jar $HOME/server.jar</entrypoint>\n----\n\nor\n\n.Example\n[source,xml]\n----\n<entrypoint>\n  <!-- exec form  -->\n  <arg>java</arg>\n  <arg>-jar</arg>\n  <arg>/opt/demo/server.jar</arg>\n</entrypoint>\n----\n\n"
  },
  {
    "path": "src/main/asciidoc/inc/push/_configuration.adoc",
    "content": "\n\n.Push options\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| *skipPush*\n| If set to `true` the plugin won't push any images that have been built.\n| `docker.skip.push`\n\n| *skipTag*\n| If set to `true` this plugin won't push any tags\n| `docker.skip.tag`\n\n| *pushRegistry*\n| The registry to use when pushing the image. See <<registry,Registry Handling>> for\nmore details.\n| `docker.push.registry`\n\n| *retries*\n| How often should a push be retried before giving up. This useful for flaky registries which tend to return 500 error codes from time to time. The default is 0 which means no retry at all.\n| `docker.push.retries`\n|===\n"
  },
  {
    "path": "src/main/asciidoc/inc/push/_overview.adoc",
    "content": "\nThis goal uploads images to the registry which have a `<build>` configuration section. The images to push can be restricted with\nthe global option `filter` (see <<global-configuration,Global Configuration>> for details). The registry to push is by default `docker.io` but can be specified as part of the images's `name` name the Docker way. E.g. `docker.test.org:5000/data:1.5` will push the image `data` with tag `1.5` to the registry `docker.test.org` at port `5000`. Security information (i.e. user and password) can be specified in multiple ways as described in section <<authentication,Authentication>>.\n\nBy default a progress meter is printed out on the console, which is omitted when using Maven in batch mode (option `-B`). A very simplified progress meter is provided when using no color output (i.e. with `-Ddocker.useColor=false`).\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_configuration.adoc",
    "content": "In addition to the <<global-configuration>>, this goal supports the following global configuration options.\n\n.Start options\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| <<container-name, *containerNamePattern*>>\n| Default pattern for naming all containers when they are created. See <<container-name, Container Names>> for details.\n| `docker.containerNamePattern`\n\n| *showLogs*\n| In order to switch on globally the logs *showLogs* can be used as global configuration (i.e. outside of `<images>`). If set it will print out all standard\n output and standard error messages for all containers started. As value the images for which logs should be shown can be given as a comma separated list.\n This is probably most useful when used from the command line as system property `docker.showLogs`.\n| `docker.showLogs`\n\n| *startParallel*\n| Starts docker images in parallel while dependencies expressed as <<start-links,Link>> or <<start-depends-on,dependsOn>> are respected. This option can significantly reduce the startup time because independent containers do not need to wait for each other.\n| `docker.startParallel`\n\n|===\n\nThe `<run>` configuration element knows the following sub elements:\n\n[[config-image-run]]\n.Run configuration (<<config-image, <image> >>)\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *capAdd*\n| List of `add` elements to specify kernel parameters to add to the container.\n\n| *capDrop*\n| List of `drop` elements to specify kernel parameters to remove from the container.\n\n| <<misc-startup, *cmd*>>\n| Command which should be executed at the end of the container's startup. If not given, the image's default command is used. See <<misc-startup, Startup Arguments>> for details.\n\n| <<container-name, *containerNamePattern*>>\n| Pattern for naming the container when it is created. See <<container-name, Container Naming Strategy>> for details.\n\n| *domainname*\n| Domain name for the container\n\n| *dns*\n| List of `host` elements specifying dns servers for the container to use\n\n| *dnsSearch*\n| List of `host` elements specifying dns search domains\n\n| *entrypoint*\n| Entry point for the container. See <<misc-startup, Startup Arguments>> for details.\n\n| <<misc-env, *env*>>\n| Environment variables as subelements which are set during startup of the container. They are specified in the typical maven property format as described <<misc-env,Environment and Labels>>.\n\n| *envPropertyFile*\n| Path to a property file holding environment variables. If given, the variables specified in this property file overrides the environment variables specified in the configuration.\n\n| *extraHosts*\n| List of `host` elements in the form `host:ip` to add to the container's `/etc/hosts` file. Additionally, you may specify a `host` element in the form `host:host` to have the right side host ip address resolved at container startup.\n\n| *exposedPropertyKey*\n| Set the property part for the exposed container properties as described <<start-overview,above>>. This will take precedence of the image's `alias` which is the default value. For example, when this property is set to `jboss`, then for this container its IP address is exposed in Maven property `docker.container.jboss.ip` regardless how the image is named.\n\n| *hostname*\n| Hostname of the container\n\n| *imagePullPolicy*\n| Specific pull policy for downloading the image. This overwrites any global pull policy. See the global <<image-pull-policy, imagePullPolicy>> configuration option for the possible values and the default.\n\n| <<misc-env, *labels*>>\n| Labels which should be attached to the  container. They are specified in the typical maven property format as described in <<misc-env,Environment and Labels>>.\n\n| <<start-links, *links*>>\n| Network links for connecting containers together as described in  <<start-links, Network Links>>.\n\n| <<start-logging, *log*>>\n| Log configuration for whether and how log messages from the running containers should be printed. This also can configure the https://docs.docker.com/engine/admin/logging/overview[log driver] to use. See <<start-logging,Logging>> for a detailed description.\n\n| *memory*\n| Memory limit in bytes\n\n| *memorySwap*\n| Total memory usage (memory + swap); use -1 to disable swap.\n\n| *namingStrategy*\na| *This option is deprecated, please use a `containerNamePattern` instead* Naming strategy for how the container name is created:\n\n* *none* : uses randomly assigned names from docker (default)\n* *alias* : uses the `alias` specified in the `image` configuration. An error is thrown, if a container already exists with this name.\n\n| <<network-configuration, *network*>>\n| Network configuration for your container.\n\n| <<start-port-mapping, *portPropertyFile*>>\n| File path into which the mapped port properties are written. The format of this file and its purpose are also described in <<start-port-mapping,Port mapping>>\n\n| <<start-port-mapping, *ports*>>\n| <<start-port-mapping,Port mappings>> for exposing container ports to host ports.\n\n| *privileged*\n| If `true` give container full access to host\n\n| *readOnly*\n| If `true` mount the container's root filesystem as read only\n\n| *autoRemove*\n| If `true` automatically remove the container when it exits. This has no effect if <<start-restart, Restart Policy>> has been set.\n\n| <<start-restart, *restartPolicy*>>\n| Restart Policy\n\n| *securityOpts*\n| List of `<opt>` elements to specify kernel security options to add to the container. See below for an example.\n\n| *shmSize*\n| Size of `/dev/shm` in bytes.\n\n| *skip*\n| If `true` disable creating and starting of the container. This option is best used together with a Maven property which can be set from the outside.\n\n| *stopMode*\n| Specifies how to stop a running container. It supports the modes `graceful` and `kill` as values, with `graceful` being the default.\n\n| *tmpfs*\na| List countaintin `<mount>` elements for directories to mount with a temporary filesystem. Optionally, mount options can be appended after a ':'. See below for an example.\n\n| *ulimits*\na| ulimits for the container. This list contains `<ulimit>` elements which three sub elements:\n\n* `<name>` : The ulimit to set (e.g. `memlock`). Please refer to the Docker documentation for the possible values to set\n* `<hard>` : The hard limit\n* `<soft>` : The soft limit\n\nSee below for an example.\n\n| *user*\n| User used inside the container\n\n| <<start-volumes, *volumes*>>\n| Volume configuration for binding to host directories and from other containers. See Volumes for details.\n\n| <<start-wait, *wait*>>\n| Condition which must be fulfilled for the startup to complete. See <<start-wait,Wait>> for all possible ways to wait for a startup condition.\n\n| *workingDir*\n| Working directory for commands to run in\n|===\n\n.Example\n[source,xml]\n----\n<run>\n  <env>\n    <CATALINA_OPTS>-Xmx32m</CATALINA_OPTS>\n    <JOLOKIA_OFF/>\n  </env>\n  <labels>\n    <environment>development</environment>\n    <version>${project.version}</version>\n  </labels>\n  <ports>\n    <port>jolokia.port:8080</port>\n  </ports>\n  <ulimits>\n    <ulimit>\n       <name>memlock</name>\n       <hard>-1</hard>\n       <soft>-1</soft>\n    </ulimit>\n  <ulimits>\n  <tmpfs>\n    <mount>/var/lib/mysql:size=10m</mount>\n    <mount>/opt/mydata</mount>\n  </tmpfs>\n  <securityOpts>\n    <opt>seccomp=unconfined</opt>\n  </securityOpts>\n  <links>\n    <link>db</db>\n  </links>\n  <wait>\n    <http>\n      <url>http://localhost:${jolokia.port}/jolokia</url>\n    </http>\n    <time>10000</time>\n  </wait>\n  <log>\n    <prefix>DEMO</prefix>\n    <date>ISO8601</date>\n    <color>blue</color>\n  </log>\n  <cmd>java -jar /maven/docker-demo.jar</cmd>\n</run>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_depends-on.adoc",
    "content": "Custom networks do not provide a mechanism like `<links>` to express strong links between containers. They are normally not required because docker ensures that all containers within the same custom network can eventually resolve each other via DNS.\n\nYour containers should preferably be able to deal with temporarily unresolvable dependencies but in some cases it is helpful to be able to rely the availability of other infrastructure containers.\n\nThe `<dependsOn>` configuration can be used to expresses custom network dependencies between your containers. `docker:start` will ensure that all dependencies a container depends on are completely started (fulfilling all `<wait>` conditions) before the depending container is started.\n\nAdditionally, each `<container>` element can specify a comma separated set of containers.  Comma (and whitespace) can be used to separate containers since valid docker container names contain only characters, digits, underscores, periods and dashes.\n\n.Example\n[source,xml]\n----\n<configuration>\n  <!-- .... -->\n  <run>\n    <dependsOn>\n      <container>postgres</container>\n      <container>logstash</container>\n    </dependsOn>\n  </run>\n</configuration>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_links.adoc",
    "content": "\nThe `<links>` configuration contains a list of containers that should\nbe linked to this container according to https://docs.docker.com/userguide/dockerlinks[Docker Links]. Each link can have two parts where the optional right side is separated by a `:` and will be used as the name in the environment variables and the left side refers to the name of the container linking to. This is equivalent to the linking when using the Docker CLI `--link` option.\n\nExample for linking to a container with name or alias _postgres_ :\n\n.Example\n[source,xml]\n----\n<links>\n  <link>postgres:db</link>\n</links>\n----\n\nThis will create the following environment variables, given that the postgres image exposes TCP port 5432:\n\n.Example\n[source,bash]\n----\nDB_NAME=/web2/db\nDB_PORT=tcp://172.17.0.5:5432\nDB_PORT_5432_TCP=tcp://172.17.0.5:5432\nDB_PORT_5432_TCP_PROTO=tcp\nDB_PORT_5432_TCP_PORT=5432\nDB_PORT_5432_TCP_ADDR=172.17.0.5\n----\n\nAdditionally, each `<link>` element can specify a comma separated set of links.  Comma (and whitespace) can be used to separate links since valid docker link names/aliases contain only characters, digits, underscores, periods and dashes.\n\n.Example\n[source,xml]\n----\n<links>\n  <link>postgres:db, search, saml:identity</link>\n</links>\n----\n\nIf you wish to link to existing containers not managed by the plugin, you may do so by specifying the container name obtained via `docker ps` in the configuration.\n\nPlease note that the link behaviour also depends on the network mode selected. Links as described are referred to by Docker as _legacy links_ and might vanish in the future. For custom networks no environments variables are set and links create merely network aliases for the linked container. To express start order dependencies using custom networks refer to the <<start-depends-on,dependsOn>> configuration.\n\nFor a more detailed documentation for the new link handling please refer to the https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks[Docker network documentation]\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_logging.adoc",
    "content": "\nWhen running containers the standard output and standard error of the container can be printed out. Several options are available for configuring the log output:\n\n.Logging configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *enabled*\n| If set to `false` log output is disabled. This is useful if you want to disable log output by default but want to use the other configuration options when log output is switched on on the command line with `-Ddocker.showLogs`. Logging is enabled by default if a `<log>` section is given.\n\n| *prefix*\na| Prefix to use for the log output in order to identify the container. You can use placeholders in the prefix which are replaced on the fly:\n\n* `%a`: Alias of the image, or, if not set, the short container id.\n* `%c`: Short container id (i.e. the first 6 chars of the container id\n* `%C`: The full container id\n* `%n`: The image name\n* `%z`: An empty string\n\nBy default the format is \"%a> \".\n\n| *date*\na| Dateformat to use for log timestamps. If `<date>` is not given no timestamp will be shown. The date specification can be either a constant or a date format. The recognized constants are:\n\n* `NONE` Switch off timestamp output. Useful on the command line\n(`-Ddocker.logDate=NONE`) for switching off otherwise enabled\nlogging.\n* `DEFAULT` A default format in the form `HH:mm:ss.SSS`\n* `MEDIUM` java.time medium date time format\n* `SHORT` java.time short date time format\n* `LONG` java.time long date time format\n* `ISO8601` Full ISO-8601 formatted date time with milliseconds\n\nAs an alternative a date-time format string as recognized by\nhttps://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html[java.time] is possible. In order to set a consistent date format, the global configuration parameter `logDate` can be used.\n\n| *color*\n| Color used for coloring the prefix when coloring is enabled (i.e. if running in a console and `useColor` is set). The available colors are `YELLOW`, `CYAN`, `MAGENTA`, `GREEN`, `RED`, `BLUE`. If coloring is enabled and now color is provided a color is picked for you.\n\n| *file*\n| Path to a file to which the log output is written. This file is overwritten for every run and colors are switched off.\n\n| *driver*\n| Section which can specify a dedicated log driver to use. A `<name>` tag within this section depicts the logging driver with the options specified in `<opts>`. See the example below for how to use this.\n|===\n\n.Example\n[source,xml]\n----\n<log>\n  <prefix>TC</prefix>\n  <date>default</date>\n  <color>cyan</color>\n</log>\n----\n\nThe following example switches on the `gelf` https://docs.docker.com/engine/admin/logging/overview[logging driver] .\nThis is equivalent to the options `--log-driver=gelf --log-opt gelf-address=udp://localhost:12201` when using `docker run`.\n\n[source,xml]\n----\n<log>\n  ...\n  <driver>\n    <name>gelf</name>\n    <opts>\n      <gelf-address>udp://localhost:12201</gelf-address>\n    </opts>\n  </driver>\n</log>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_network.adoc",
    "content": "\nThe `<network>` element in the <<start-configuration, <run> >> configuration section can be used to configure the network mode of the container. This is now the preferred way for linking containers together. It knows the following sub elements:\n\n.Network configuration\n[cols=\"1,7\"]\n|===\n| Element | Description\n\n| *mode*\na| The network mode, which can be one of the following values:\n\n* *bridge* : Bridged mode with the default Docker bridge (default)\n* *host* : Share the Docker host network interfaces\n* *container* : Connect to the network of the specified container. The name of the container is taken from the `<name>` element.\n* *custom* : Use a custom network, which must be created before by using `docker network create`. Alternatively you can set the ```docker.autoCreateCustomNetworks``` <<global-configuration, global configuration>> parameter to ```true``` to automatically create custom networks. Custom networks are available for Docker 1.9 and newer. For more about the networking options please refer to the https://docs.docker.com/engine/userguide/networking/work-with-networks[Docker documentation].\n\n* *none* : No network will be setup.\n\n| *name*\n| For mode `container` this is the container name, which is this image alias. For Mode `custom` this is the name of the custom network.\n\n| *alias*\n| One or more alias element can be provided which gives a way for a container to be discovered by alternate names by any other container within the scope of a particular network. This configuration only has effect for when the network mode is `custom`. More than one alias can be given by providing multiple entries.\n|===\n\nIf no `mode` is given but a `name`, then a `custom` network mode is assumed. For the simple modes which does not take an argument (`none`, `bridge` or `host`) a single `<net>` _mode_ `</net>` can be used as alternative to using `<network>` with a `<mode>` subelement.\n\n.Example <network>\n[source,xml]\n----\n<network>\n   <mode>custom</mode>\n   <name>my-network</name>\n   <alias>box1</alias>\n   <alias>box2</alias>\n</network>\n----\n\nor for a simple `host` network:\n\n.Example <net>\n[source, xml]\n----\n<net>host</net>\n----\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_overview.adoc",
    "content": "\n[[start-overview]]\nThis goal creates and starts docker containers. This goal evaluates the configuration's `<run>` section of all given (and enabled images).\n\nAlso you can specify `docker.follow` as system property so that the `{plugin}:start` will never return but block until CTRL-C is pressed. That similar to the option `-i` for `docker run`. This will automatically switch on `showLogs` so that you can see what is happening within the container. Also, after stopping with CTRL-C, the container is stopped (but not removed so that you can make postmortem analysis). `{plugin}:run` is an alias for `{plugin}:start` with `docker.follow` enabled.\n\nBy default container specific properties are exposed as Maven properties. These properties have the format `docker.container.<alias>.<prop>` where `<alias>` is the name of the container (see below) and `<prop>` is one of the following container properties:\n\n.Properties provided\n[cols=\"1,5\"]\n|===\n| Property | Description\n\n| *ip*\n| Internal IP address of the container.\n\n| *id*\n| Container id\n\n| *net*._<network>_.*ip*\n| Internal IP address of the container in the specified custom network. This works only for custom networks.\n|===\n\nInstead of the `<alias>` a fixed property key can be configured in the image's <<config-image-run, <run> >> configuration with the option `exposedPropertyKey`.\n\nFor example the Maven property `docker.container.tomcat.ip` would hold the Docker internal IP for a container with an alias \"tomcat\". You can set the global configuration *exposeContainerInfo* to an empty string to not expose container information that way or to a string for an other prefix than `docker.container`.\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_port-mapping.adoc",
    "content": "\nThe `<ports>` configuration contains a list of port mappings. Whitespace is trimmed from each element and empty elements are ignored. Each mapping has multiple parts, each separate by a colon. This is equivalent to the port mapping when using the Docker CLI with option `-p`.\n\nA `port` stanza may take one of the following forms:\n\n.Port mapping format\n[cols=\"2,5\"]\n|===\n| Format | Description\n\n| ++18080:8080++\n| Tuple consisting of two numeric values separated by a `:`. This form will result in an explicit mapping between the docker host and the corresponding port inside the container. In the above example, port 18080 would be exposed on the docker host and mapped to port 8080 in the running container.\n\n| **host.port**++:80++\n| Tuple consisting of a string and a numeric value separated by a `:`. In this form, the string portion of the tuple will correspond to a Maven property. If the property is undefined when the `start` task executes, a port will be dynamically selected by Docker in the ephemeral port range and assigned to the property which may then be used later in the same POM file. The ephemeral port range is configured by the `/proc/sys/net/ipv4/ip_local_port_range` kernel parameter, which typically ranges from 32678 to 61000.  If the property exists and has numeric value, that value will be used as the exposed port on the docker host as in the previous form. In the above example, the docker service will elect a new port and assign the value to the property `host.port` which may then later be used in a property expression similar to `<value>${host.port}</value>`. This can be used to pin a port from the outside when doing some initial testing similar to `mvn -Dhost.port=10080 {plugin}:start`\n\n| **bindTo**++:++**host.port**++:80++\n| Tuple consisting of two strings and a numeric value separated by a `:`. In this form, `bindTo` is an ip address on the host the container should bind to. As a convenience, a hostname pointing to the docker host may also be specified. The container will fail to start if the hostname can not be resolved.\n\n| +**host.ip**++:++**host.port**++:80++\n| Tuple consisting of two strings and a numeric value separated by a `:`. In this form, the host ip of the container will be placed into a Maven property name `host.ip`. If docker reports that value to be `0.0.0.0`, the value of `docker.host.address` will be substituted instead. In the event you want to use this form and have the container bind to a specific hostname/ip address, you can declare a Maven property of the same name (`host.ip` in this example) containing the value to use. `host:port` works in the same way as described above.\n|===\n\nBy default TCP is used as protocol but you can also use UDP by appending '/udp' to the port number.\n\nThe following are examples of valid configuration entries:\n\n.Example\n[source,xml]\n----\n<properties>\n  <bind.host.ip>1.2.3.4</bind.host.ip>\n  <bind.host.name>some.host.pvt</bind.host.name>\n</properties>\n\n...\n\n<ports>\n  <port>18080:8080</port>\n  <port>15060:5060/udp</port>\n  <port>host.port:80</port>\n  <port>127.0.0.1:80:80</port>\n  <port>localhost:host.port:80</port>\n  <port>+container.ip.property:host.port:5678</port>\n  <port>+bind.host.ip:host.port:5678</port>\n  <port>+bind.host.name:5678:5678</port>\n</ports>\n----\n\nAnother useful configuration option is `portPropertyFile` which can be used to write out the container's host ip and any dynamic ports that have been resolved. The keys of this property file are the property names defined in the port mapping configuration and their values those of the corresponding docker attributes.\n\nThis property file might be useful with tests or with other maven plugins that will be unable to use the resolved properties because they can only be updated after the container has started and plugins resolve their properties in an earlier lifecycle phase.\n\nIf you don't need to write out such a property file and thus don't need to preserve the property names, you can use normal maven properties as well. E.g. `${host.var}:${port.var}:8080` instead of\n`+host.var:port.var:8080`.\n\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_restart.adoc",
    "content": "\nSpecify the behavior to apply when the container exits. These values can be specified withing a `<restartPolicy>` section with the following sub-elements:\n\n.Restart Policy configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *name*\na| Restart policy name, choose from:\n\n* `always` (_v1.15_) always restart\n* `on-failure` (_v1.15_) restart on container non-exit code of zero\n\n| *retry*\n| If `on-failure` is used, controls max number of attempts to restart before giving up.\n|===\n\nThe behavior to apply when the container exits. The value is an object with a name property of either \"always\" to always restart or \"on-failure\" to restart only when the container exit code is non-zero. If on-failure is used, MaximumRetryCount controls the number of times to retry before giving up. The default is not to restart. (optional)\n\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_volumes.adoc",
    "content": "\nA container can bind (or \"mount\") volumes from various source when starting up: Either from a directory of the host system or from another container which exports one or more directories. The mount configuration is specified within a `<volumes>` section of the run configuration. It can contain the following sub elements:\n\n.Volume configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *from*\n| List of `<image>` elements which specify image names or aliases of containers whose volumes should be imported.\n\n| *bind*\n| List of `<volume>` specifications (or _host mounts_). Use `/path` to create and expose a new volume in the container, `/host_path:/container_path` to mount a host path into the container and `/host_path:/container_path:ro` to bind it read-only.\n|===\n\n.Volumes example\n[source,xml]\n----\n<volumes>\n  <bind>\n    <volume>/logs</volume>\n    <volume>/opt/host_export:/opt/container_import</volume>\n  </bind>\n  <from>\n    <image>jolokia/docker-demo</image>\n  </from>\n</volumes>\n----\n\nIn this example the container creates a new volume named  `/logs` on the container and mounts `/opt/host_export` from the host as `/opt/container_import` on the container. In addition all exported volumes from the container which has been created from the image `jolokia/docker-demo` are mounted directly into the container (with the same directory names under which the exporting container exposes these directories). This image must be also configured for this plugin. Instead of the full image name, an alias name can be used, too.\n\nIf a volume name instead of a path is referenced to in `<bind>` and a <<docker:volume-create,volume configuration>> exists with this name, then this this volume is created upfront with the provided options instead of using default options.\n\nYou can use Maven variables in the path specifications. This should even work for boot2docker and docker-machine:\n\n.Example with absolute paths\n[source,xml]\n----\n<volumes>\n  <bind>\n    <volume>${project.build.directory}/${project.artifactId}-${project.version}:/usr/local/tomcat/webapps/${project.name}</volume>\n    <volume>${project.basedir}/data:/data</volume>\n  </bind>\n</volumes>\n----\n\nYou can also use relative paths.  Relative paths are interpreted relative to the Maven project base directory.  Paths\nthat begin with `~` are interpreted relative to the JVM's `user.home` directory.\n\n.Example with relative paths\n[source,xml]\n----\n<volumes>\n  <bind>\n    <volume>src/main/webapps/foo:/usr/local/tomcat/webapps/foo</volume>\n    <volume>./target:/data</volume>\n    <volume>~:/home/user</volume>\n    <volume>~/.m2/repository:/home/user/.m2/repository</volume>\n  </bind>\n</volumes>\n----\n\nIf you wish to mount volumes from an existing container not managed by the plugin, you may do by specifying the container name obtained via `docker ps` in the configuration.\n"
  },
  {
    "path": "src/main/asciidoc/inc/start/_wait.adoc",
    "content": "\nWhile starting a container is it possible to block the execution until\nsome condition is met. These conditions can be specified within a\n`<wait>` section which the following sub-elements:\n\n.Wait configuration\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *http*\na| HTTP ping check which periodically polls an URL. It knows the following sub-elements:\n\n* *url* holds an URL and is mandatory\n* *method* Optional HTTP method to use.\n* *status* Status code which if returned is considered to be a successful ping. This code can be given either as a single number (200) or as a range (200..399). The default is `200..399`\n* *allowAllHosts* If `url` is an HTTPS url and this option is set, then server certificates are not validated. By default they are checked for a proper CA signature.\n\n| *log*\n| Regular expression which is applied against the log\noutput of an container and blocks until the pattern is matched. You can use `(?s)` in the pattern to switch on multi line matching.\n\n| *time*\n| Time in milliseconds to block.\n\n| *kill*\n| Time in milliseconds between sending `SIGTERM` and `SIGKILL` when stopping a container. Since docker itself uses second granularity, you should use at least 1000 milliseconds.\n\n| *shutdown*\n| Time to wait in milliseconds between stopping a container and removing it. This might be helpful in situation where a Docker croaks with an error when trying to remove a container to fast after it has been stopped.\n\n| *exec*\na| Commands to execute during specified lifecycle of the container. It knows the following sub-elements:\n\n* *postStart* Command to run after the above wait criteria has been met\n* *preStop* Command to run before the container is stopped.\n* *breakOnError* If set to `true` then break the build if a `postStart` or `preStop` command exits with an return code other than 0, otherwise only print an error message.\n\n| *tcp*\na| TCP port check which periodically polls given tcp ports. It knows the following sub-elements:\n\n* *mode* can be either `mapped` which uses the mapped ports or `direct` in which case the container ports are addressed directly. In the later case the host field should be left empty in order to select the container ip (which must be routed which is only the case when running on the Docker daemon's host directly). Default is `direct` when host is _localhost_, `mapped` otherwise. The direct mode might help when a so called _user-proxy_ is enabled on the Docker daemon which makes the mapped ports directly available even when the container is not ready yet.\n* *host* is the hostname or the IP address. It defaults to `${docker.host.address}` for a mapped mode and the container ip address for the direct mode.\n* *ports* is a list of TCP ports to check. These are supposed to be the container internal ports.\n\n| *healthy*\na| Check that waits until the container health state becomes `healthy`. A container is considered healthy when its <<build-healthcheck,  configured healtcheck>> succeeds.\n\nThis behaviour mimics the docker compose dependsOn `condition: service_healthy`.\n\n| *exit*\n| Check that waits until a container finishes with the given exit code.\n\n|===\n\nAs soon as one condition is met the build continues. If you add a `<time>` constraint this works more or less as a timeout for other conditions. The build will abort if you wait on an url or log output and reach the timeout. If only a `<time>` is specified, the build will wait that amount of milliseconds and then continues.\n\n.Example\n[source,xml]\n----\n<wait>\n  <http>\n    <url>http://localhost:${host.port}</url>\n    <method>GET</method>\n    <status>200..399</status>\n  </http>\n  <time>10000</time>\n  <kill>1000</kill>\n  <shutdown>500</shutdown>\n  <exec>\n     <postStart>/opt/init_db.sh</postStart>\n     <preStop>/opt/notify_end.sh</preStop>\n  </exec>\n  <tcp>\n     <host>192.168.99.100</host>\n     <ports>\n        <port>3306</port>\n        <port>9999</port>\n     </ports>\n  </tcp>\n  <healthy>true</healthy>\n</wait>\n----\n\nThis setup will wait for the given URL to be reachable but ten seconds at most. Additionally, it will wait for the TCP ports 3306 and 9999. Also, when stopping the container after integration tests, the build wait for 500 ms before it tries to remove the container (if not `keepContainer` or `keepRunning` is used). You can use maven properties in each condition, too. In the example, the `${host.port}` property is probably set before within a port mapping section.\n\nThe property `${docker.host.address}` is set implicitly to the address of the Docker host. This host will be taken from the `docker.host` configuration if HTTP or HTTPS is used. If a Unix socket is used for communication with the docker daemon, then `localhost` is assumed. You can override this property always by setting this Maven property explicitly.\n\n"
  },
  {
    "path": "src/main/asciidoc/inc/watch/_configuration.adoc",
    "content": "\n\nThis maven goal can be configured with the following top-level parameters:\n\n.Watch configuration\n[cols=\"1,5,1\"]\n|===\n| Element | Description | Property\n\n| <<container-name, *containerNamePattern*>>\n| Default pattern for naming all containers when they are created. See <<container-name, Container Names>> for details.\n| `docker.containerNamePattern`\n\n| *keepContainer*\n| As for `{plugin}:stop`, if this is set to `true` (and `keepRunning` is disabled) then all container will be removed after they have been stopped. The default is `true`.\n| `docker.keepContainer`\n\n| *keepRunning*\n| If set to `true` all container will be kept running after `{plugin}:watch` has been stopped. By default this is set to `false`.\n| `docker.keepRunning`\n\n| *removeVolumes*\n| if set to `true` will remove any volumes associated to the container as well. This option will be ignored if either `keepContainer` or `keepRunning` are `true`.\n| `docker.removeVolumes`\n\n| *watchInterval*\n| Interval in milliseconds how  often to check for changes, which must be larger than 100ms. The default is 5 seconds.\n| `docker.watchInterval`\n\n| *watchMode*\na| Watch mode specifies what should be watched\n\n* `build` : Watch changes in the assembly and rebuild the image in\ncase\n* `run` : Watch a container's image whether it changes and restart\nthe container in case\n* `copy` : Changed files are copied into the container. The container can be either running or might be already exited (when used as a _data container_ linked into a _platform container_). Requires Docker >= 1.8.\n* `both` : `build` and `run` combined\n* `none` : Neither watching for builds nor images. This is useful if\nyou use prefactored images which won't be changed and hence don't\nneed any watching. `none` is best used on an per image level, see\nbelow how this can be specified.\n| `docker.watchMode`\n\n| *watchPostExec*\n| A command which is executed within the container after files are copied into this container when `watchMode` is `copy`. Note that this container must be running.\n|\n\n| *watchPostGoal*\n| A maven goal which should be called if a rebuild or a restart has been performed. This goal must have the format `<pluginGroupId>:<pluginArtifactId>:<goal>` and the plugin must be configured in the `pom.xml`. For example a post-goal `io.fabric8:fabric8:delete-pods` will trigger the deletion of PODs in Kubernetes which in turn triggers are new start of a POD within the Kubernetes cluster. The value specified here is the the default post goal which can be overridden by `<postGoal>` in a `<watch>` configuration.\n|\n|===\n\nImage specific watch configuration goes into an extra image-level `<watch>` section (i.e. `+<image><watch>...</watch></image>+`). The following parameters are recognized:\n\n.Watch configuration for a single image\n[cols=\"1,5\"]\n|===\n| Element | Description\n\n| *mode*\n| Each image can be configured for having individual watch mode. These take precedence of the global watch mode. The mode specified in this configuration takes precedence over the globally specified mode.\n\n| *interval*\n| Watch interval can be specified in milliseconds on image level. If given this will override the global watch interval.\n\n| *postGoal*\n| Post Maven plugin goal after a rebuild or restart. The value here must have the format `<pluginGroupId>:<pluginArtifactId>:<goal>` (e.g. `io.fabric8:fabric8:delete-pods`)\n\n| *postExec*\n| Command to execute after files are copied into a\nrunning container when `mode` is `copy`.\n|===\n\nHere is an example how the watch mode can be tuned:\n\n.Example\n[source,xml]\n----\n<configuration>\n   <!-- Check every 10 seconds by default -->\n   <watchInterval>10000</watchInterval>\n   <!-- Watch for doing rebuilds and restarts -->\n   <watchMode>both</watch>\n   <images>\n      <image>\n         <!-- Service checks every 5 seconds -->\n         <alias>service</alias>\n         ....\n         <watch>\n            <interval>5000</interval>\n         </watch>\n      </image>\n      <image>\n         <!-- Database needs no watching -->\n         <alias>db<alias>\n         ....\n         <watch>\n            <mode>none</mode>\n         </watch>\n      </image>\n      ....\n   </images>\n</configuration>\n----\n\nGiven this configuration\n\n.Example\n[source,sh,subs=\"+attributes\"]\n----\nmvn package {plugin}:build {plugin}:start {plugin}:watch\n----\n\nYou can build the service image, start up all containers and go into a watch loop. Again, you need the `package` phase in order that the assembly can find the artifact build by this project. This is a Maven limitation. The `db` image will never be watch since it assumed to not change while watching.\n"
  },
  {
    "path": "src/main/asciidoc/inc/watch/_overview.adoc",
    "content": "\nWhen developing and testing applications you will often have to rebuild Docker images and restart containers. Typing `{plugin}:build` and `{plugin}:start` all the time is cumbersome. With `{plugin}:watch` you can enable automatic rebuilding of images and restarting of containers in case of updates.\n\n`{plugin}:watch` is the top-level goal which performs these tasks. There are two watch modes, which can be specified in multiple ways:\n\n* `build` : Automatically rebuild one or more Docker images when one of the files selected by an assembly changes. This works for all files included directly in `assembly.xml` but also for arbitrary dependencies.\n\n.Example\n[source, sh, subs=\"+attributes\"]\n----\n$ mvn package {plugin}:build {plugin}:watch -Ddocker.watchMode=build\n----\n\nThis mode works only when there is a `<build>` section in an image configuration. Otherwise no automatically build will be triggered for an image with only a `<run>` section. Note that you need the `package` phase to be executed before otherwise any artifact created by this build can not be included into the assembly. As described in the section about `{plugin}:start` this is a Maven limitation.\n* `run` : Automatically restart container when their associated images changes. This is useful if you pull a new version of an image externally or especially in combination with the `build` mode to restart containers when their image has been automatically rebuilt. This mode works reliably only when used together with `{plugin}:start`.\n\n.Example\n[source, sh, subs=\"+attributes\"]\n----\n$ mvn {plugin}:start {plugin}:watch -Ddocker.watchMode=run\n----\n\n* `both` : Enables both `build` and `run`. This is the default.\n* `none` : Image is completely ignored for watching.\n* `copy` : Copy changed files into the running container. This is the fast way to update a container, however the target container must support hot deploy, too so that it makes sense. Most application servers like Tomcat supports this.\n\nThe mode can also be `both` or `none` to select both or none of these variants, respectively. The default is `both`.\n\n`{plugin}:watch` will run forever until it is interrupted with `CTRL-C` after which it will stop all containers. Depending on the configuration parameters `keepContainer` and `removeVolumes` the stopped containers with their volumes will be removed, too.\n\nWhen an image is removed while watching it, error messages will be printed out periodically.  So don't do that ;-)\n\nDynamically assigned ports stay stable in that they won't change after a container has been stopped and a new container is created and started. The new container will try to allocate the same ports as the previous container.\n\nIf containers are linked together network or volume wise, and you update a container which other containers dependent on, the dependant containers are not restarted for now. E.g. when you have a \"service\" container accessing a \"db\" container and the \"db\" container is updated, then you \"service\" container will fail until it is restarted, too.\n\n****\nA future version of this plugin will take care of restarting\nthese containers, too (in the right order), but for now you would have\nto do this manually.\n****\n"
  },
  {
    "path": "src/main/asciidoc/index.adoc",
    "content": "= fabric8io/docker-maven-plugin\nRoland Huß;\n:revnumber: {version}\n:revdate: {localdate}\n:toc: macro\n:toclevels: 3\n:toc-title: docker-maven-plugin\n:doctype: book\n:icons: font\n:plugin: docker\n\nifndef::ebook-format[:leveloffset: 1]\n\n(C) 2015 - 2020 The original authors.\n\nifdef::basebackend-html[toc::[]]\n\n:numbered:\n\ninclude::inc/_introduction.adoc[]\n\ninclude::inc/_installation.adoc[]\n\ninclude::inc/_global-configuration.adoc[]\n\ninclude::inc/_image-configuration.adoc[]\n\ninclude::inc/_goals.adoc[]\ninclude::inc/_docker-build.adoc[]\ninclude::inc/_docker-start.adoc[]\ninclude::inc/_docker-stop.adoc[]\ninclude::inc/_docker-push.adoc[]\ninclude::inc/_docker-watch.adoc[]\ninclude::inc/_docker-remove.adoc[]\ninclude::inc/_docker-logs.adoc[]\ninclude::inc/_docker-source.adoc[]\ninclude::inc/_docker-save.adoc[]\ninclude::inc/_docker-tag.adoc[]\ninclude::inc/_docker-volume-create.adoc[]\ninclude::inc/_docker-volume-remove.adoc[]\n\ninclude::inc/_external-configuration.adoc[]\n\ninclude::inc/_registry.adoc[]\n\ninclude::inc/_authentication.adoc[]\n\ninclude::inc/_implicit-properties.adoc[]\n\ninclude::inc/_links.adoc[]\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/AbstractBuildSupportMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.service.BuildService;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.archiver.MavenArchiveConfiguration;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.filtering.MavenFileFilter;\nimport org.apache.maven.shared.filtering.MavenReaderFilter;\n\n/**\n * @author roland\n * @author balazsmaria\n * @since 26/06/15\n */\nabstract public class AbstractBuildSupportMojo extends AbstractDockerMojo {\n    // ==============================================================================================================\n    // Parameters required from Maven when building an assembly. They cannot be injected directly\n    // into DockerAssemblyCreator.\n    // See also here: http://maven.40175.n5.nabble.com/Mojo-Java-1-5-Component-MavenProject-returns-null-vs-JavaDoc-parameter-expression-quot-project-quot-s-td5733805.html\n\n    @Parameter\n    private MavenArchiveConfiguration archive;\n\n    @Component\n    private MavenFileFilter mavenFileFilter;\n\n    @Component\n    private MavenReaderFilter mavenFilterReader;\n\n    @Parameter\n    private Map<String, String> buildArgs;\n\n    @Parameter(property = \"docker.pull.registry\")\n    private String pullRegistry;\n\n    @Parameter( defaultValue = \"${reactorProjects}\", required = true, readonly = true )\n    private List<MavenProject> reactorProjects;\n\n\n    protected BuildService.BuildContext getBuildContext() throws MojoExecutionException {\n        return new BuildService.BuildContext.Builder()\n                .buildArgs(buildArgs)\n                .mojoParameters(createMojoParameters())\n                .registryConfig(getRegistryConfig(pullRegistry))\n                .build();\n    }\n\n    protected MojoParameters createMojoParameters() {\n        return new MojoParameters(session, project, archive, mavenFileFilter, mavenFilterReader,\n                                  settings, sourceDirectory, outputDirectory, reactorProjects);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/AbstractDockerMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport com.google.common.collect.ImmutableList;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport io.fabric8.maven.docker.config.DockerMachineConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RegistryAuthConfiguration;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.config.handler.ImageConfigResolver;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.service.DockerAccessFactory;\nimport io.fabric8.maven.docker.service.ImagePullManager;\nimport io.fabric8.maven.docker.service.RegistryService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.service.ServiceHubFactory;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.AuthConfigFactory;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.ImageNameFormatter;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.AbstractMojo;\nimport org.apache.maven.plugin.MojoExecution;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.settings.Settings;\nimport org.apache.maven.shared.utils.logging.MessageUtils;\nimport org.codehaus.plexus.PlexusConstants;\nimport org.codehaus.plexus.PlexusContainer;\nimport org.codehaus.plexus.context.Context;\nimport org.codehaus.plexus.context.ContextException;\nimport org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;\nimport org.fusesource.jansi.Ansi;\n\n/**\n * Base class for this plugin.\n *\n * @author roland\n * @since 26.03.14\n */\npublic abstract class AbstractDockerMojo extends AbstractMojo implements Contextualizable, ConfigHelper.Customizer {\n\n    // Key for indicating that a \"start\" goal has run\n    public static final String CONTEXT_KEY_START_CALLED = \"CONTEXT_KEY_DOCKER_START_CALLED\";\n\n    // Key holding the log dispatcher\n    public static final String CONTEXT_KEY_LOG_DISPATCHER = \"CONTEXT_KEY_DOCKER_LOG_DISPATCHER\";\n\n    // Key under which the build timestamp is stored so that other mojos can reuse it\n    public static final String CONTEXT_KEY_BUILD_TIMESTAMP = \"CONTEXT_KEY_BUILD_TIMESTAMP\";\n\n    // Filename for holding the build timestamp\n    public static final String DOCKER_BUILD_TIMESTAMP = \"docker/build.timestamp\";\n\n    // Current maven project\n    @Parameter(defaultValue= \"${project}\", readonly = true)\n    protected MavenProject project;\n\n    // Settings holding authentication info\n    @Parameter(defaultValue = \"${settings}\", readonly = true)\n    protected Settings settings;\n\n    // Current maven project\n    @Parameter(property= \"session\")\n    protected MavenSession session;\n\n    // Current mojo execution\n    @Parameter(property= \"mojoExecution\")\n    protected MojoExecution execution;\n\n    // Handler for external configurations\n    @Component\n    protected ImageConfigResolver imageConfigResolver;\n\n    @Component\n    protected ServiceHubFactory serviceHubFactory;\n\n    @Component\n    protected DockerAccessFactory dockerAccessFactory;\n\n    @Parameter(property = \"docker.autoPull\")\n    protected String autoPull;\n\n    @Parameter(property = \"docker.imagePullPolicy\")\n    protected String imagePullPolicy;\n\n    // Whether to keep the containers afters stopping (start/watch/stop)\n    @Parameter(property = \"docker.keepContainer\", defaultValue = \"false\")\n    protected boolean keepContainer;\n\n    // Whether to remove volumes when removing the container (start/watch/stop)\n    @Parameter(property = \"docker.removeVolumes\", defaultValue = \"false\")\n    protected boolean removeVolumes;\n\n    @Parameter(property = \"docker.apiVersion\")\n    private String apiVersion;\n\n    /**\n     * URL to docker daemon\n     */\n    @Parameter(property = \"docker.host\")\n    private String dockerHost;\n\n    @Parameter(property = \"docker.certPath\")\n    private String certPath;\n\n    // Whether to use color\n    @Parameter(property = \"docker.useColor\", defaultValue = \"true\")\n    protected boolean useColor;\n\n    // For verbose output\n    @Parameter(property = \"docker.verbose\")\n    protected String verbose;\n\n    // The date format to use when putting out logs\n    @Parameter(property = \"docker.logDate\")\n    private String logDate;\n\n    // Log to stdout regardless if log files are configured or not\n    @Parameter(property = \"docker.logStdout\", defaultValue = \"false\")\n    private boolean logStdout;\n\n    // Whether to skip docker altogether\n    @Parameter(property = \"docker.skip\", defaultValue = \"false\")\n    private boolean skip;\n\n    /**\n     * Whether the usage of docker machine should be skipped competely\n     */\n    @Parameter(property = \"docker.skip.machine\", defaultValue = \"false\")\n    private boolean skipMachine;\n\n    /**\n     * Whether to restrict operation to a single image. This can be either\n     * the image or an alias name. It can also be comma separated list.\n     * This parameter has to be set via the command line s system property.\n     */\n    @Parameter(property = \"docker.filter\")\n    private String filter;\n\n    // Default registry to use if no registry is specified\n    @Parameter(property = \"docker.registry\")\n    protected String registry;\n\n    /**\n     * Skip extended authentication\n     */\n    @Parameter(property = \"docker.skip.extendedAuth\", defaultValue = \"false\")\n    protected boolean skipExtendedAuth;\n\n    // maximum connection to use in parallel for connecting the docker host\n    @Parameter(property = \"docker.maxConnections\", defaultValue = \"100\")\n    private int maxConnections;\n\n    @Parameter(property = \"docker.build.jib\", defaultValue = \"false\")\n    public boolean jib;\n\n    @Parameter(property = \"docker.source.dir\", defaultValue=\"src/main/docker\")\n    public String sourceDirectory;\n\n    @Parameter(property = \"docker.target.dir\", defaultValue=\"target/docker\")\n    public String outputDirectory;\n\n    // Authentication information\n    @Parameter\n    private RegistryAuthConfiguration authConfig;\n\n    /**\n     * Volume configuration\n     */\n    @Parameter\n    private List<VolumeConfiguration> volumes;\n\n    /**\n     * Image configurations configured directly.\n     */\n    @Parameter\n    private List<ImageConfiguration> images;\n\n    /**\n     * Image configurations configured via maps to allow overriding.\n     */\n    @Parameter\n    private Map<String, ImageConfiguration> imagesMap;\n\n    // Docker-machine configuration\n    @Parameter\n    private DockerMachineConfiguration machine;\n\n    // Images resolved with external image resolvers and hooks for subclass to\n    // mangle the image configurations.\n    private List<ImageConfiguration> resolvedImages;\n\n    // Handler dealing with authentication credentials\n    private AuthConfigFactory authConfigFactory;\n\n    protected Logger log;\n\n    private String minimalApiVersion;\n\n    /**\n     * Entry point for this plugin. It will set up the helper class and then calls\n     * {@link #executeInternal(ServiceHub)}\n     * which must be implemented by subclass.\n     *\n     * @throws MojoExecutionException\n     * @throws MojoFailureException\n     */\n    @Override\n    public void execute() throws MojoExecutionException, MojoFailureException {\n        if (!skip) {\n            boolean ansiRestore = Ansi.isEnabled();\n            log = new AnsiLogger(getLog(), useColorForLogging(), verbose, !settings.getInteractiveMode(), getLogPrefix());\n\n            try {\n                authConfigFactory.setLog(log);\n                imageConfigResolver.setLog(log);\n\n                LogOutputSpecFactory logSpecFactory = new LogOutputSpecFactory(useColor, logStdout, logDate);\n\n                ConfigHelper.validateExternalPropertyActivation(project, getAllImages());\n\n                DockerAccess access = null;\n                try {\n                    // The 'real' images configuration to use (configured images + externally resolved images)\n                    this.minimalApiVersion = initImageConfiguration(getBuildTimestamp());\n                    if (isDockerAccessRequired()) {\n                        DockerAccessFactory.DockerAccessContext dockerAccessContext = getDockerAccessContext();\n                        access = dockerAccessFactory.createDockerAccess(dockerAccessContext);\n                    }\n                    ServiceHub serviceHub = serviceHubFactory.createServiceHub(project, session, access, log, logSpecFactory);\n                    executeInternal(serviceHub);\n                } catch (IOException | ExecException exp) {\n                    logException(exp);\n                    throw new MojoExecutionException(log.errorMessage(exp.getMessage()), exp);\n                } catch (MojoExecutionException exp) {\n                    logException(exp);\n                    throw exp;\n                } finally {\n                    if (access != null) {\n                        access.shutdown();\n                    }\n                }\n            } finally {\n                Ansi.setEnabled(ansiRestore);\n            }\n        }\n    }\n\n    private void logException(Exception exp) {\n        if (exp.getCause() != null) {\n            log.error(\"%s [%s]\", exp.getMessage(), exp.getCause().getMessage());\n        } else {\n            log.error(\"%s\", exp.getMessage());\n        }\n    }\n\n    protected DockerAccessFactory.DockerAccessContext getDockerAccessContext() {\n        return new DockerAccessFactory.DockerAccessContext.Builder()\n                .dockerHost(dockerHost)\n                .certPath(certPath)\n                .machine(machine)\n                .maxConnections(maxConnections)\n                .minimalApiVersion(minimalApiVersion)\n                .projectProperties(project.getProperties())\n                .skipMachine(skipMachine)\n                .log(log)\n                .build();\n    }\n\n    protected RegistryService.RegistryConfig getRegistryConfig(String specificRegistry) throws MojoExecutionException {\n        return new RegistryService.RegistryConfig.Builder()\n                .settings(settings)\n                .authConfig(authConfig != null ? authConfig.toMap() : null)\n                .authConfigFactory(authConfigFactory)\n                .skipExtendedAuth(skipExtendedAuth)\n                .registry(specificRegistry != null ? specificRegistry : registry)\n                .build();\n    }\n\n    /**\n     * Get the current build timestamp. this has either already been created by a previous\n     * call or a new current date is created\n     * @return timestamp to use\n     */\n    protected synchronized Date getBuildTimestamp() throws IOException {\n        Date now = (Date) getPluginContext().get(CONTEXT_KEY_BUILD_TIMESTAMP);\n        if (now == null) {\n            now = getReferenceDate();\n            getPluginContext().put(CONTEXT_KEY_BUILD_TIMESTAMP,now);\n        }\n        return now;\n    }\n\n    // Get the reference date for the build. By default this is picked up\n    // from an existing build date file. If this does not exist, the current date is used.\n    protected Date getReferenceDate() throws IOException {\n        Date referenceDate = EnvUtil.loadTimestamp(getBuildTimestampFile());\n        return referenceDate != null ? referenceDate : new Date();\n    }\n\n    // used for storing a timestamp\n    protected File getBuildTimestampFile() {\n        return new File(project.getBuild().getDirectory(), DOCKER_BUILD_TIMESTAMP);\n    }\n\n    /**\n     * Log prefix to use when doing the logs\n     * @return\n     */\n    protected String getLogPrefix() {\n        return AnsiLogger.DEFAULT_LOG_PREFIX;\n    }\n\n    /**\n     * Determine whether to enable colorized log messages\n     * @return true if log statements should be colorized\n     */\n    private boolean useColorForLogging() {\n        return useColor && MessageUtils.isColorEnabled()\n                && !(EnvUtil.isWindows() && !EnvUtil.isMaven350OrLater(session));\n    }\n\n    // Resolve and customize image configuration\n    private String initImageConfiguration(Date buildTimeStamp)  {\n        // Resolve images\n        resolvedImages = ConfigHelper.resolveImages(\n            log,\n                getAllImages(), // Unresolved images\n            new ConfigHelper.Resolver() {\n                    @Override\n                    public List<ImageConfiguration> resolve(ImageConfiguration image) {\n                        return imageConfigResolver.resolve(image, project, session);\n                    }\n                },\n           filter,                   // A filter which image to process\n            this);                     // customizer (can be overwritten by a subclass)\n\n        // Check for simple Dockerfile mode\n        File topDockerfile = new File(project.getBasedir(),\"Dockerfile\");\n        if (topDockerfile.exists()) {\n            if (resolvedImages.isEmpty()) {\n                resolvedImages.add(createSimpleDockerfileConfig(topDockerfile));\n            } else if (resolvedImages.size() == 1 && resolvedImages.get(0).getBuildConfiguration() == null) {\n                resolvedImages.set(0, addSimpleDockerfileConfig(resolvedImages.get(0), topDockerfile));\n            }\n        }\n\n        // Initialize configuration and detect minimal API version\n        return ConfigHelper.initAndValidate(resolvedImages, apiVersion, new ImageNameFormatter(project, buildTimeStamp), log);\n    }\n\n    // Customization hook for subclasses to influence the final configuration. This method is called\n    // before initialization and validation of the configuration.\n    @Override\n    public List<ImageConfiguration> customizeConfig(List<ImageConfiguration> imageConfigs) {\n        return imageConfigs;\n    }\n\n   /**\n     * Override this if your mojo doesnt require access to a Docker host (like creating and attaching\n     * docker tar archives)\n     *\n     * @return <code >true</code> as the default value\n     */\n    protected boolean isDockerAccessRequired() {\n        return Boolean.FALSE.equals(jib);\n    }\n\n    /**\n     * Hook for subclass for doing the real job\n     *\n     * @param serviceHub context for accessing backends\n     */\n    protected abstract void executeInternal(ServiceHub serviceHub)\n        throws IOException, ExecException, MojoExecutionException;\n\n    // =============================================================================================\n\n    /**\n     * Get all images to use. Can be restricted via -Ddocker.filter to pick a one or more images.\n     * The values are taken as comma separated list.\n     *\n     * @return list of image configuration to be use. Can be empty but never null.\n     */\n    protected List<ImageConfiguration> getResolvedImages() {\n        return resolvedImages;\n    }\n\n    /**\n     * Get all volumes which are defined separately from the images\n     *\n     * @return defined volumes\n     */\n    protected List<VolumeConfiguration> getVolumes() {\n        return volumes;\n    }\n\n    // =================================================================================\n\n    @Override\n    public void contextualize(Context context) throws ContextException {\n        authConfigFactory = new AuthConfigFactory((PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY));\n    }\n\n    // =================================================================================\n\n    protected GavLabel getGavLabel() {\n        // Label used for this run\n        return new GavLabel(project.getGroupId(), project.getArtifactId(), project.getVersion());\n    }\n\n    protected LogDispatcher getLogDispatcher(ServiceHub hub) {\n        LogDispatcher dispatcher = (LogDispatcher) getPluginContext().get(CONTEXT_KEY_LOG_DISPATCHER);\n        if (dispatcher == null) {\n            dispatcher = new LogDispatcher(hub.getDockerAccess());\n            getPluginContext().put(CONTEXT_KEY_LOG_DISPATCHER, dispatcher);\n        }\n        return dispatcher;\n    }\n\n    private ImmutableList<ImageConfiguration> getAllImages() {\n        ImmutableList.Builder<ImageConfiguration> allImages = ImmutableList.builder();\n        if (images != null) {\n            allImages.addAll(images);\n        }\n        if (imagesMap != null) {\n            imagesMap.forEach((alias, config) -> {\n                if (config.getAlias() == null) {\n                    config.setAlias(alias);\n                }\n                allImages.add(config);\n            });\n        }\n        return allImages.build();\n    }\n\n    public ImagePullManager getImagePullManager(String imagePullPolicy, String autoPull) {\n        return new ImagePullManager(getSessionCacheStore(), imagePullPolicy, autoPull);\n    }\n\n    private ImagePullManager.CacheStore getSessionCacheStore() {\n        return new ImagePullManager.CacheStore() {\n            @Override\n            public String get(String key) {\n                Properties userProperties = session.getUserProperties();\n                return userProperties.getProperty(key);\n            }\n\n            @Override\n            public void put(String key, String value) {\n                Properties userProperties = session.getUserProperties();\n                userProperties.setProperty(key, value);\n            }\n        };\n    }\n\n    private ImageConfiguration createSimpleDockerfileConfig(File dockerFile) {\n        // No configured name, so create one from maven GAV\n        String name = EnvUtil.getPropertiesWithSystemOverrides(project).getProperty(\"docker.name\");\n        if (name == null) {\n            // Default name group/artifact:version (or 'latest' if SNAPSHOT)\n            name = \"%g/%a:%l\";\n        }\n\n        BuildImageConfiguration buildConfig =\n            new BuildImageConfiguration.Builder()\n                .dockerFile(dockerFile.getPath())\n                .build();\n\n        return new ImageConfiguration.Builder()\n            .name(name)\n            .buildConfig(buildConfig)\n            .build();\n    }\n\n    private ImageConfiguration addSimpleDockerfileConfig(ImageConfiguration image, File dockerfile) {\n        BuildImageConfiguration buildConfig =\n            new BuildImageConfiguration.Builder()\n                .dockerFile(dockerfile.getPath())\n                .build();\n        return new ImageConfiguration.Builder(image).buildConfig(buildConfig).build();\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/BuildMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.LineNumberReader;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.util.Date;\nimport java.util.Enumeration;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.service.BuildService;\nimport io.fabric8.maven.docker.service.ImagePullManager;\nimport io.fabric8.maven.docker.service.JibBuildService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Mojo for building a data image\n *\n * @author roland\n * @since 28.07.14\n */\n@Mojo(name = \"build\", defaultPhase = LifecyclePhase.INSTALL)\npublic class BuildMojo extends AbstractBuildSupportMojo {\n\n    public static final String DMP_PLUGIN_DESCRIPTOR = \"META-INF/maven/io.fabric8/dmp-plugin\";\n    public static final String DOCKER_EXTRA_DIR = \"docker-extra\";\n\n    @Parameter(property = \"docker.skip.build\", defaultValue = \"false\")\n    protected boolean skipBuild;\n\n    @Parameter(property = \"docker.name\", defaultValue = \"\")\n    protected String name;\n\n    /**\n     * Skip Sending created tarball to docker daemon\n     */\n    @Parameter(property = \"docker.buildArchiveOnly\", defaultValue = \"false\")\n    protected String buildArchiveOnly;\n\n    /**\n     * Skip building tags\n     */\n    @Parameter(property = \"docker.skip.tag\", defaultValue = \"false\")\n    protected boolean skipTag;\n\n    @Override\n    protected void executeInternal(ServiceHub hub) throws IOException, MojoExecutionException {\n        if (skipBuild) {\n            return;\n        }\n\n        // Check for build plugins\n        executeBuildPlugins();\n\n        // Iterate over all the ImageConfigurations and process one by one\n        for (ImageConfiguration imageConfig : getResolvedImages()) {\n            processImageConfig(hub, imageConfig);\n        }\n    }\n\n    protected void buildAndTag(ServiceHub hub, ImageConfiguration imageConfig)\n            throws MojoExecutionException, IOException {\n\n        EnvUtil.storeTimestamp(getBuildTimestampFile(), getBuildTimestamp());\n\n        BuildService.BuildContext buildContext = getBuildContext();\n        ImagePullManager pullManager = getImagePullManager(determinePullPolicy(imageConfig.getBuildConfiguration()), autoPull);\n        proceedWithBuildProcess(hub, buildContext, imageConfig, pullManager);\n    }\n\n    private void proceedWithBuildProcess(ServiceHub hub, BuildService.BuildContext buildContext, ImageConfiguration imageConfig, ImagePullManager pullManager) throws MojoExecutionException, IOException {\n        if (Boolean.TRUE.equals(jib)) {\n            proceedWithJibBuild(hub, buildContext, imageConfig);\n        } else {\n            proceedWithDockerBuild(hub.getBuildService(), buildContext, imageConfig, pullManager);\n        }\n    }\n\n    private void proceedWithJibBuild(ServiceHub hub, BuildService.BuildContext buildContext, ImageConfiguration imageConfig) throws MojoExecutionException {\n        log.info(\"Building Container image with [[B]]JIB(Java Image Builder)[[B]] mode\");\n        new JibBuildService(hub, createMojoParameters(), log).build(imageConfig, buildContext.getRegistryConfig());\n    }\n\n    private void proceedWithDockerBuild(BuildService buildService, BuildService.BuildContext buildContext, ImageConfiguration imageConfig, ImagePullManager pullManager) throws MojoExecutionException, IOException {\n        File buildArchiveFile = buildService.buildArchive(imageConfig, buildContext, resolveBuildArchiveParameter());\n        if (Boolean.FALSE.equals(shallBuildArchiveOnly())) {\n            buildService.buildImage(imageConfig, pullManager, buildContext, buildArchiveFile);\n        }\n        if (!skipTag) {\n            buildService.tagImage(imageConfig);\n        }\n    }\n\n    // We ignore an already existing date file and always return the current date\n\n    @Override\n    protected Date getReferenceDate() {\n        return new Date();\n    }\n\n    private String resolveBuildArchiveParameter() {\n        if (buildArchiveOnly != null && !buildArchiveOnly.isEmpty()) {\n            if (!(buildArchiveOnly.equalsIgnoreCase(\"false\") ||\n                buildArchiveOnly.equalsIgnoreCase(\"true\"))) {\n                return buildArchiveOnly;\n            }\n        }\n        return null;\n    }\n\n    private boolean shallBuildArchiveOnly() {\n        if (buildArchiveOnly != null && !buildArchiveOnly.isEmpty()) {\n            if (buildArchiveOnly.equalsIgnoreCase(\"false\") ||\n                    buildArchiveOnly.equalsIgnoreCase(\"true\")) {\n                return Boolean.parseBoolean(buildArchiveOnly);\n            }\n        }\n        return false;\n    }\n\n    private String determinePullPolicy(BuildImageConfiguration buildConfig) {\n        return buildConfig != null && buildConfig.getImagePullPolicy() != null ? buildConfig.getImagePullPolicy() : imagePullPolicy;\n    }\n\n    /**\n     * Helper method to process an ImageConfiguration.\n     *\n     * @param hub ServiceHub\n     * @param aImageConfig ImageConfiguration that would be forwarded to build and tag\n     * @throws DockerAccessException\n     * @throws MojoExecutionException\n     */\n    private void processImageConfig(ServiceHub hub, ImageConfiguration aImageConfig) throws IOException, MojoExecutionException {\n        BuildImageConfiguration buildConfig = aImageConfig.getBuildConfiguration();\n\n        if (buildConfig != null) {\n            if(buildConfig.skip()) {\n                log.info(\"%s : Skipped building\", aImageConfig.getDescription());\n            } else {\n                buildAndTag(hub, aImageConfig);\n            }\n        }\n    }\n\n    // check for a run-java.sh dependency an extract the script to target/ if found\n    private void executeBuildPlugins() {\n        try {\n            Enumeration<URL> dmpPlugins = Thread.currentThread().getContextClassLoader().getResources(DMP_PLUGIN_DESCRIPTOR);\n            while (dmpPlugins.hasMoreElements()) {\n\n                URL dmpPlugin = dmpPlugins.nextElement();\n                File outputDir = getAndEnsureOutputDirectory();\n                processDmpPluginDescription(dmpPlugin, outputDir);\n            }\n        } catch (IOException e) {\n            log.error(\"Cannot load dmp-plugins from %s\", DMP_PLUGIN_DESCRIPTOR);\n        }\n    }\n\n    private void processDmpPluginDescription(URL pluginDesc, File outputDir) throws IOException {\n        String line = null;\n        try (LineNumberReader reader =\n                 new LineNumberReader(new InputStreamReader(pluginDesc.openStream(), \"UTF8\"))) {\n            line = reader.readLine();\n            while (line != null) {\n                if (line.matches(\"^\\\\s*#\")) {\n                    // Skip comments\n                    continue;\n                }\n                callBuildPlugin(outputDir, line);\n                line = reader.readLine();\n            }\n        } catch (ClassNotFoundException e) {\n            // Not declared as dependency, so just ignoring ...\n        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"Found dmp-plugin %s but could not be called : %s\",\n                     line,\n                     e.getMessage());\n        }\n    }\n\n    private File getAndEnsureOutputDirectory() {\n        File outputDir = new File(new File(project.getBuild().getDirectory()), DOCKER_EXTRA_DIR);\n        if (!outputDir.exists()) {\n            outputDir.mkdirs();\n        }\n        return outputDir;\n    }\n\n    private void callBuildPlugin(File outputDir, String buildPluginClass) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {\n        Class buildPlugin = Class.forName(buildPluginClass);\n        try {\n            Method method = buildPlugin.getMethod(\"addExtraFiles\", File.class);\n            method.invoke(null, outputDir);\n            log.info(\"Extra files from %s extracted\", buildPluginClass);\n        } catch (NoSuchMethodException exp) {\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"Build plugin %s does not support 'addExtraFiles' method\", buildPluginClass);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/LogsMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.log.LogOutputSpec;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n\n/**\n * Mojo for printing logs of a container. By default the logs of all containers are shown interwoven\n * with the time occured. The log output can be highly customized in the plugin configuration, please\n * refer to the reference manual for documentation.\n *\n * This Mojo is intended for standalone usage. See {@link StartMojo} for how to enabling logging when\n * starting containers.\n *\n * @author roland\n * @since 26.03.14\n *\n */\n@Mojo(name = \"logs\")\npublic class LogsMojo extends AbstractDockerMojo {\n\n    // Whether to log infinitely or to show only the logs happened until now.\n    @Parameter(property = \"docker.follow\", defaultValue = \"false\")\n    private boolean follow;\n\n    // Whether to log all containers or only the newest ones\n    @Parameter(property = \"docker.logAll\", defaultValue = \"false\")\n    private boolean logAll;\n\n    @Override\n    protected void executeInternal(ServiceHub hub) throws MojoExecutionException, DockerAccessException {\n        QueryService queryService = hub.getQueryService();\n        LogDispatcher logDispatcher = getLogDispatcher(hub);\n\n        for (ImageConfiguration image : getResolvedImages()) {\n            String imageName = image.getName();\n            if (logAll) {\n                for (Container container : queryService.getContainersForImage(imageName, false)) {\n                    doLogging(logDispatcher, image, container.getId());\n                }\n            } else {\n                Container container = queryService.getLatestContainerForImage(imageName);\n                if (container != null) {\n                    doLogging(logDispatcher, image, container.getId());\n                }\n            }\n        }\n        if (follow) {\n            // Block forever ....\n            waitForEver();\n        }\n    }\n\n    private void doLogging(LogDispatcher logDispatcher, ImageConfiguration imageConfig, String container) {\n        LogOutputSpec spec = serviceHubFactory.getLogOutputSpecFactory().createSpec(container, imageConfig);\n        if (follow) {\n            logDispatcher.trackContainerLog(container, spec);\n        } else {\n            logDispatcher.fetchContainerLog(container, spec);\n        }\n    }\n\n    private synchronized void waitForEver() {\n        while (true) {\n            try {\n                this.wait();\n            } catch (InterruptedException e) {\n                // sleep again\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/PushMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.service.BuildService;\nimport io.fabric8.maven.docker.service.JibBuildService;\nimport io.fabric8.maven.docker.service.ServiceHub;\n\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Goal for pushing a data-docker container\n *\n * @author roland\n */\n@Mojo(name = \"push\", defaultPhase = LifecyclePhase.DEPLOY)\npublic class PushMojo extends AbstractDockerMojo {\n\n    // Registry to use for push operations if no registry is specified\n    @Parameter(property = \"docker.push.registry\")\n    private String pushRegistry;\n\n    @Parameter(property = \"docker.skip.push\", defaultValue = \"false\")\n    private boolean skipPush;\n    \n    /** \n     * Skip building tags\n     */\n    @Parameter(property = \"docker.skip.tag\", defaultValue = \"false\")\n    private boolean skipTag;\n    \n    @Parameter(property = \"docker.push.retries\", defaultValue = \"0\")\n    private int retries;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void executeInternal(ServiceHub hub) throws DockerAccessException, MojoExecutionException {\n        if (skipPush) {\n            return;\n        }\n\n        if (Boolean.TRUE.equals(jib)) {\n            executeJibPush(hub);\n        } else {\n            executeDockerPush(hub);\n        }\n    }\n\n    private void executeDockerPush(ServiceHub hub) throws MojoExecutionException, DockerAccessException {\n        hub.getRegistryService().pushImages(getResolvedImages(), retries, getRegistryConfig(pushRegistry), skipTag);\n    }\n\n    private void executeJibPush(ServiceHub hub) throws MojoExecutionException {\n        log.info(\"Pushing Container image with [[B]]JIB(Java Image Builder)[[B]] mode\");\n        JibBuildService jibBuildService = new JibBuildService(hub, new MojoParameters(session, project, null, null, null,\n                settings, sourceDirectory, outputDirectory, null), log);\n        jibBuildService.push(getResolvedImages(), retries, getRegistryConfig(pushRegistry), skipTag);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/RemoveMojo.java",
    "content": "package io.fabric8.maven.docker;/*\n *\n * Copyright 2014 Roland Huss\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\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.NamePatternUtil;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * Mojo for removing images. By default only data images are removed. Data images are\n * images without a run configuration.\n *\n * By setting <code>removeAll</code> (property: <code>docker.removeAll</code>) also other\n * images can be removed.\n *\n * In order to explicitly restrict the images to remove, use <code>images</code> to specify a\n * (comma separated) list of images to remove.\n *\n * @author roland\n * @since 23.10.14\n *\n */\n@Mojo(name = \"remove\", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)\npublic class RemoveMojo extends AbstractDockerMojo {\n\n    // Should all configured images should be removed?\n    @Parameter(property = \"docker.removeAll\")\n    @Deprecated\n    private Boolean removeAll;\n\n    @Parameter(property = \"docker.removeMode\")\n    private String removeMode;\n    \n    /** \n     * Skip building tags\n     */\n    @Parameter(property = \"docker.skip.tag\", defaultValue = \"false\")\n    private boolean skipTag;\n\n    @Parameter(property = \"docker.removeNamePattern\")\n    private String removeNamePattern;\n\n    @Override\n    protected void executeInternal(ServiceHub hub) throws DockerAccessException, MojoExecutionException {\n        for (ImageConfiguration image : getResolvedImages()) {\n            if (imageShouldBeRemoved(image)) {\n                for(String name : getImageNamesToRemoveForImage(hub, image)) {\n                    removeImage(hub, name);\n                }\n\n                if(!skipTag) {\n                    // Remove any tagged images\n                    for (String tag: getImageBuildTags(image)){\n                        removeImage(hub, new ImageName(image.getName(), tag).getFullName());\n                    }\n                }\n            }\n        }\n\n        // If a global pattern option is provided, process it after per-image patterns\n        for(String name : getImageNamesToRemoveForMojo(hub)) {\n            removeImage(hub, name);\n        }\n    }\n\n    private boolean imageShouldBeRemoved(ImageConfiguration image) {\n        if (\"all\".equalsIgnoreCase(removeMode)) {\n            return true;\n        }\n        if (\"build\".equalsIgnoreCase(removeMode)) {\n            return image.getBuildConfiguration() != null;\n        }\n        if (\"run\".equalsIgnoreCase(removeMode)) {\n            return image.getBuildConfiguration() == null;\n        }\n        if (\"data\".equalsIgnoreCase(removeMode)) {\n            return image.isDataImage();\n        }\n        if (removeAll != null) {\n            return removeAll || image.isDataImage();\n        }\n        // Default\n        return image.getBuildConfiguration() != null;\n    }\n\n    private Collection<String> getImageNamesMatchingPattern(ServiceHub hub, Matcher imageNameMatcher)\n            throws MojoExecutionException, DockerAccessException {\n        return hub.getQueryService().listImages(false)\n                .stream()\n                .flatMap(image -> image.getRepoTags().stream())\n                .filter(repoTag -> imageNameMatcher.reset(repoTag).matches())\n                .collect(Collectors.toList());\n    }\n\n    private Collection<String> getImageNamesToRemoveForMojo(ServiceHub hub)\n            throws MojoExecutionException, DockerAccessException {\n        if(removeNamePattern != null) {\n            Matcher imageNameMatcher = getImageNameMatcher(removeNamePattern);\n\n            if(imageNameMatcher == null) {\n                log.warn(\"There are no image name patterns in removeNamePattern for docker:remove\");\n                return Collections.emptyList();\n            }\n\n            return getImageNamesMatchingPattern(hub, imageNameMatcher);\n        }\n\n        return Collections.emptyList();\n    }\n\n    private Collection<String> getImageNamesToRemoveForImage(ServiceHub hub, ImageConfiguration imageConfiguration)\n            throws MojoExecutionException, DockerAccessException {\n\n        if(imageConfiguration.getRemoveNamePattern() != null) {\n            Matcher imageNameMatcher = getImageNameMatcher(imageConfiguration.getRemoveNamePattern());\n\n            if(imageNameMatcher == null) {\n                log.warn(\"There are no image name patterns in removeNamePattern for image %s: no images will be removed\", imageConfiguration.getName());\n                return Collections.emptyList();\n            }\n\n            return getImageNamesMatchingPattern(hub, imageNameMatcher);\n        }\n\n        return Collections.singleton(imageConfiguration.getName());\n    }\n\n    private Matcher getImageNameMatcher(String removeNamePattern) throws MojoExecutionException {\n        try {\n            String imageNameRegex = NamePatternUtil.convertNamePatternList(removeNamePattern, NamePatternUtil.IMAGE_FIELD, true);\n            if(imageNameRegex == null) {\n                log.debug(\"No image name patterns in removeNamePattern %s\", removeNamePattern);\n                return null;\n            } else {\n                log.debug(\"Converted removeNamePattern %s into image name regular expression %s\", removeNamePattern, imageNameRegex);\n                return Pattern.compile(imageNameRegex).matcher(\"\");\n            }\n        } catch(IllegalArgumentException e) {\n            throw new MojoExecutionException(e.getMessage(), e);\n        }\n    }\n\n    private void removeImage(ServiceHub hub, String name) throws DockerAccessException {\n        QueryService queryService = hub.getQueryService();\n        if (queryService.hasImage(name)) {\n            if (hub.getDockerAccess().removeImage(name,true)) {\n                log.info(\"%s: Remove\", name);\n            }\n        }\n    }\n\n    private List<String> getImageBuildTags(ImageConfiguration image){\n        return image.getBuildConfiguration() != null ?\n            image.getBuildConfiguration().getTags() :\n            Collections.<String>emptyList();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/RunMojo.java",
    "content": "package io.fabric8.maven.docker;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\n\n/**\n * Alias for `docker:start`\n * @author roland\n * @since 26/04/16\n */\n@Mojo(name = \"run\", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST)\npublic class RunMojo extends StartMojo {\n\n    @Override\n    protected Boolean followLogs() {\n        // Follow logs by default\n        return Boolean.valueOf(System.getProperty(\"docker.follow\", \"true\"));\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/SaveMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.project.MavenProjectHelper;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.service.ServiceHub;\n\n@Mojo(name = \"save\")\npublic class SaveMojo extends AbstractDockerMojo {\n\n    // Used when not automatically determined\n\tprivate static final ArchiveCompression STANDARD_ARCHIVE_COMPRESSION = ArchiveCompression.gzip;\n\n\t@Component\n\tprivate MavenProjectHelper projectHelper;\n\n\t@Parameter(property = \"docker.save.name\")\n\tprivate String saveName;\n\n\t@Parameter(property = \"docker.save.alias\")\n\tprivate String saveAlias;\n\n\t@Parameter\n\tprivate String saveFile;\n\n\t@Parameter(property = \"docker.skip.save\", defaultValue = \"false\")\n\tprivate boolean skipSave;\n\n\t@Parameter(property = \"docker.save.classifier\")\n\tprivate String saveClassifier;\n\n\t@Override\n\tprotected void executeInternal(ServiceHub serviceHub) throws DockerAccessException, MojoExecutionException {\n\n\t\tList<ImageConfiguration> images = getResolvedImages();\n\t\tif (skipSaveFor(images)) {\n\t\t\treturn;\n\t\t}\n\n\t\tImageConfiguration image = getImageToSave(images);\n\t\tString imageName = image.getName();\n\t\tString fileName = getFileName(imageName);\n\t\tensureSaveDir(fileName);\n\t\tlog.info(\"Saving image %s to %s\", imageName, fileName);\n\t\tif (!serviceHub.getQueryService().hasImage(imageName)) {\n\t\t\tthrow new MojoExecutionException(\"No image \" + imageName + \" exists\");\n\t\t}\n\n\t\tlong time = System.currentTimeMillis();\n\t\tArchiveCompression compression = ArchiveCompression.fromFileName(fileName);\n\t\tserviceHub.getDockerAccess().saveImage(imageName, fileName, compression);\n\t\tlog.info(\"%s: Saved image to %s in %s\", imageName, fileName, EnvUtil.formatDurationTill(time));\n\n\t\tString classifier = getClassifier(image);\n\t\tif(classifier != null) {\n\t\t\tprojectHelper.attachArtifact(project, compression.getFileSuffix(), classifier, new File(fileName));\n\t\t}\n\t}\n\n\tprivate boolean skipSaveFor(List<ImageConfiguration> images) {\n\t\tif (skipSave) {\n\t\t\tlog.info(\"docker:save skipped because `skipSave` config is set to true\");\n\t\t\treturn true;\n\t\t}\n\n\t\tif (saveName == null &&\n\t\t\tsaveAlias == null &&\n\t\t\timages.stream().allMatch(i -> i.getBuildConfiguration() == null)) {\n\t\t\tlog.info(\"docker:save skipped because no image has a build configuration defined\");\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate String getFileName(String iName) throws MojoExecutionException {\n\t    String configuredFileName = getConfiguredFileName();\n\t    if (configuredFileName != null) {\n\t        return configuredFileName;\n        }\n\t\tif (saveAlias != null) {\n\t\t\treturn completeCalculatedFileName(saveAlias +\n                                              \"-\" + project.getVersion() +\n                                              \".\" + STANDARD_ARCHIVE_COMPRESSION.getFileSuffix());\n\t\t}\n        ImageName imageName = new ImageName(iName);\n        return completeCalculatedFileName(imageName.getSimpleName() +\n                                          \"-\" + imageName.getTag()) +\n                                          \".\" + STANDARD_ARCHIVE_COMPRESSION.getFileSuffix();\n    }\n\n    private String getConfiguredFileName() {\n        Properties[] propsList = new Properties[] { System.getProperties(), project.getProperties() };\n        for (String key : new String[] { \"docker.save.file\", \"docker.file\", \"file\" }) {\n            for (Properties props : propsList) {\n                if (props.containsKey(key)) {\n                    return props.getProperty(key);\n                }\n            }\n        }\n        return saveFile;\n    }\n\n    private String completeCalculatedFileName(String file) throws MojoExecutionException {\n        return project.getBuild().getDirectory() + \"/\" + file.replace(\"/\",\"-\");\n    }\n\n    private void ensureSaveDir(String fileName) throws MojoExecutionException {\n        File saveDir = new File(fileName).getAbsoluteFile().getParentFile();\n        if (!saveDir.exists()) {\n            if (!saveDir.mkdirs()) {\n                throw new MojoExecutionException(\"Can not create directory \" + saveDir + \" for storing save file\");\n            }\n        }\n    }\n\n\tprivate ImageConfiguration getImageToSave(List<ImageConfiguration> images) throws MojoExecutionException {\n\t\t// specify image by name or alias\n\t\tif (saveName == null && saveAlias == null) {\n\t\t\tList<ImageConfiguration> buildImages = getImagesWithBuildConfig(images);\n\t\t\tif (buildImages.size() == 1) {\n\t\t\t\treturn buildImages.get(0);\n\t\t\t}\n\t\t\tthrow new MojoExecutionException(\"More than one image with build configuration is defined. Please specify the image with 'docker.name' or 'docker.alias'.\");\n\t\t}\n\t\tif (saveName != null && saveAlias != null) {\n\t\t\tthrow new MojoExecutionException(\"Cannot specify both name and alias.\");\n\t\t}\n\t\tfor (ImageConfiguration ic : images) {\n\t\t\tif (equalName(ic) || equalAlias(ic)) {\n\t\t\t\treturn ic;\n\t\t\t}\n\t\t}\n\t\tthrow new MojoExecutionException(saveName != null ?\n\t\t\t\t\t\t\t\t\t\t\t \"Can not find image with name '\" + saveName + \"'\" :\n\t\t\t\t\t\t\t\t\t\t\t \"Can not find image with alias '\"+ saveAlias + \"'\");\n\t}\n\n\tprivate List<ImageConfiguration> getImagesWithBuildConfig(List<ImageConfiguration> images) {\n\t\tList<ImageConfiguration> ret = new ArrayList<>();\n\t\tfor (ImageConfiguration image : images) {\n\t\t\tif (image.getBuildConfiguration() != null) {\n\t\t\t\tret.add(image);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate String getClassifier(ImageConfiguration image) {\n\t\tif(saveClassifier == null || saveClassifier.length() == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn saveClassifier.replace(\"%a\", image.getAlias() == null ? \"\" : image.getAlias());\n\t}\n\n\n\tprivate boolean equalAlias(ImageConfiguration ic) {\n\t\treturn saveAlias != null && saveAlias.equals(ic.getAlias());\n\t}\n\n\tprivate boolean equalName(ImageConfiguration ic) {\n\t\treturn saveName != null && saveName.equals(ic.getName());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/SourceMojo.java",
    "content": "package io.fabric8.maven.docker;\n/*\n *\n * Copyright 2015 Roland Huss\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\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageSelectMode;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.MojoParameters;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.project.MavenProjectHelper;\n\n/**\n * Mojo for attaching one more source docker tar file to an artifact.\n *\n * If used in a lifecycle called 'docker-tar' the artifacts are attached\n * without classifier. Otherwise the classifier is \"docker\" and, depending on the\n * selected {@link BuildImageSelectMode}, the alias name of the build configuration.\n *\n * @author roland\n * @since 25/10/15\n */\n@Mojo(name = \"source\", defaultPhase = LifecyclePhase.PACKAGE)\npublic class SourceMojo extends AbstractBuildSupportMojo {\n\n    @Component\n    private MavenProjectHelper projectHelper;\n\n    /**\n     * Mode how to attach the source artifact:\n     *\n     * <ul>\n     *     <li><strong>first</strong> : The first image with build configuration</li>\n     *     <li><strong>all</strong> : All images with build configuration. Each image must have an alias\n     *        configured which is use as part of the classifier.\n     *     </li>\n     * </ul>\n     *\n     */\n    @Parameter\n    private BuildImageSelectMode sourceMode = BuildImageSelectMode.first;\n\n    @Override\n    protected void executeInternal(ServiceHub hub) throws DockerAccessException, MojoExecutionException {\n        MojoParameters params = createMojoParameters();\n        List<ImageConfiguration> imageConfigs = new ArrayList<>();\n        for (ImageConfiguration imageConfig : getResolvedImages()) {\n            BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n            if (buildConfig != null) {\n                if (buildConfig.skip()) {\n                    log.info(\"%s: Skipped creating source\",imageConfig.getDescription());\n                } else {\n                    imageConfigs.add(imageConfig);\n                }\n            }\n        }\n        if (sourceMode == BuildImageSelectMode.first && imageConfigs.size() > 0) {\n            ImageConfiguration imageConfig = imageConfigs.get(0);\n            File dockerTar = hub.getArchiveService().createDockerBuildArchive(imageConfig, params);\n            projectHelper.attachArtifact(project, getArchiveType(imageConfig),\n                                         getClassifier(null), dockerTar);\n        } else {\n            for (ImageConfiguration imageConfig : imageConfigs) {\n                File dockerTar = hub.getArchiveService().createDockerBuildArchive(imageConfig, params);\n                String alias = imageConfig.getAlias();\n                if (alias == null) {\n                    throw new IllegalArgumentException(\n                        \"Image \" + imageConfig.getDescription() + \" must have an 'alias' configured to be \" +\n                        \"used as a classifier for attaching a docker build tar as source to the maven build\");\n                }\n                projectHelper.attachArtifact(project, getArchiveType(imageConfig), getClassifier(alias), dockerTar);\n            }\n        }\n    }\n\n    private String getClassifier(String alias) {\n        String packaging = project.getPackaging();\n        if (\"docker-tar\".equalsIgnoreCase(packaging)) {\n            return alias;\n        } else {\n            return \"docker\" + (alias != null ? \"-\" + alias : \"\");\n        }\n    }\n\n    private String getArchiveType(ImageConfiguration imageConfig) {\n        return imageConfig.getBuildConfiguration().getCompression().getFileSuffix();\n    }\n\n    @Override\n    protected boolean isDockerAccessRequired() {\n        // dont need a running docker host for creating the docker tar\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/StartMojo.java",
    "content": "package io.fabric8.maven.docker;\n\n/*\n * Copyright 2009-2014 Roland Huss Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by\n * applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\nimport java.io.IOException;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\nimport com.google.common.util.concurrent.MoreExecutors;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.service.ImagePullManager;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.RegistryService;\nimport io.fabric8.maven.docker.service.RunService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.service.helper.StartContainerExecutor;\nimport io.fabric8.maven.docker.util.ContainerNamingUtil;\nimport io.fabric8.maven.docker.util.StartOrderResolver;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n\n/**\n * Goal for creating and starting a docker container. This goal evaluates the image configuration\n *\n * @author roland\n */\n@Mojo(name = \"start\", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST)\npublic class StartMojo extends AbstractDockerMojo {\n\n    @Parameter(property = \"docker.showLogs\")\n    private String showLogs;\n\n    @Parameter(property = \"docker.pull.registry\")\n    private String pullRegistry;\n\n    @Parameter(property = \"docker.skip.run\", defaultValue = \"false\")\n    private boolean skipRun;\n\n    @Parameter(property = \"docker.startParallel\", defaultValue = \"false\")\n    private boolean startParallel;\n\n    // whether to block during to start. Set it via System property docker.follow\n    private boolean follow;\n\n    /**\n     * Expose container information like the internal IP as Maven properties which\n     * can be reused in the build information. The value of this property is the prefix\n     * used for the properties. The default prefix is \"docker.container\". Only information\n     * of images having an alias are exposed and have the format <code>&lt;prefix&gt;.&lt;alias&gt;.&lt;property&gt;</code>.\n     * (e.g. <code>docker.container.mycontainer.ip</code>\n     * The following properties are currently supported:\n     * <ul>\n     *   <li><strong>ip</strong> : the container's internal IP address as chosen by Docker</li>\n     * </ul>\n     *\n     * If set to an empty string, no properties are exposed.\n     */\n    @Parameter(property = \"docker.exposeContainerInfo\")\n    private String exposeContainerProps = \"docker.container\";\n\n    /**\n     * Naming pattern for how to name containers when started\n     */\n    @Parameter(property = \"docker.containerNamePattern\")\n    private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;\n\n    /**\n     * Whether to create the customs networks (user-defined bridge networks) before starting automatically\n     */\n    @Parameter(property = \"docker.autoCreateCustomNetworks\", defaultValue = \"false\")\n    protected boolean autoCreateCustomNetworks;\n\n    // property file to write out with port mappings\n    @Parameter\n    protected String portPropertyFile;\n\n    private static final class StartedContainer {\n        public final ImageConfiguration imageConfig;\n        public final String containerId;\n\n        private StartedContainer(ImageConfiguration imageConfig, String containerId) {\n            this.imageConfig = imageConfig;\n            this.containerId = containerId;\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public synchronized void executeInternal(final ServiceHub hub) throws DockerAccessException,\n                                                                          ExecException,\n                                                                          MojoExecutionException {\n        if (skipRun) {\n            return;\n        }\n        getPluginContext().put(CONTEXT_KEY_START_CALLED, true);\n\n        this.follow = followLogs();\n\n        QueryService queryService = hub.getQueryService();\n        final RunService runService = hub.getRunService();\n\n        PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper = new PortMapping.PropertyWriteHelper(portPropertyFile);\n\n        boolean success = false;\n\n        final ExecutorService executorService = getExecutorService();\n        final ExecutorCompletionService<StartedContainer> containerStartupService = new ExecutorCompletionService<>(executorService);\n\n        try {\n            // All aliases which are provided in the image configuration:\n            final Set<String> imageAliases = new HashSet<>();\n            // Remember all aliases which has been started\n            final Set<String> startedContainerAliases = new HashSet<>();\n\n            // All images to to start\n            Queue<ImageConfiguration> imagesWaitingToStart = prepareStart(hub, queryService, runService, imageAliases);\n\n            // Queue of images to start as containers\n            final Queue<ImageConfiguration> imagesStarting = new ArrayDeque<>();\n\n            // Prepare the shutdown hook for stopping containers if we are going to follow them.  Add the hook before starting any\n            // of the containers so that partial or aborted starts will behave the same as fully-successful ones.\n            if (follow) {\n                runService.addShutdownHookForStoppingContainers(keepContainer, removeVolumes, autoCreateCustomNetworks);\n            }\n\n            // Loop until every image has been started and the start of all images has been completed\n            while (!hasBeenAllImagesStarted(imagesWaitingToStart, imagesStarting)) {\n\n                final List<ImageConfiguration> imagesReadyToStart =\n                    getImagesWhoseDependenciesHasStarted(imagesWaitingToStart, startedContainerAliases, imageAliases);\n\n                for (final ImageConfiguration image : imagesReadyToStart) {\n\n                    startImage(image, hub, containerStartupService, portMappingPropertyWriteHelper);\n\n                    // Move from waiting to starting status\n                    imagesStarting.add(image);\n                    imagesWaitingToStart.remove(image);\n\n                    if (!startParallel) {\n                        waitForStartedContainer(containerStartupService, startedContainerAliases, imagesStarting);\n                    }\n                }\n\n                if (startParallel) {\n                    waitForStartedContainer(containerStartupService, startedContainerAliases, imagesStarting);\n                }\n            }\n            portMappingPropertyWriteHelper.write();\n\n            if (follow) {\n                wait();\n            }\n\n            success = true;\n        } catch (InterruptedException e) {\n            log.warn(\"Interrupted\");\n            Thread.currentThread().interrupt();\n            throw new MojoExecutionException(\"interrupted\", e);\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"I/O Error\", e);\n        } finally {\n            shutdownExecutorService(executorService);\n\n            // Rollback if not all could be started\n            if (!success) {\n                log.error(\"Error occurred during container startup, shutting down...\");\n                runService.stopStartedContainers(keepContainer, removeVolumes, autoCreateCustomNetworks, getGavLabel());\n            }\n        }\n    }\n\n    private void waitForStartedContainer(\n            final ExecutorCompletionService<StartedContainer> containerStartupService,\n            final Set<String> startedContainerAliases, final Queue<ImageConfiguration> imagesStarting)\n            throws InterruptedException, IOException, ExecException {\n        final Future<StartedContainer> startedContainerFuture = containerStartupService.take();\n        try {\n            final StartedContainer startedContainer = startedContainerFuture.get();\n            final ImageConfiguration imageConfig = startedContainer.imageConfig;\n\n            updateAliasesSet(startedContainerAliases, imageConfig.getAlias());\n\n            // All done with this image\n            imagesStarting.remove(imageConfig);\n        } catch (ExecutionException e) {\n            rethrowCause(e);\n        }\n    }\n\n    protected Boolean followLogs() {\n        return Boolean.valueOf(System.getProperty(\"docker.follow\", \"false\"));\n    }\n\n    // Check if we are done\n    private boolean hasBeenAllImagesStarted(Queue<ImageConfiguration> imagesWaitingToStart, Queue<ImageConfiguration> imagesStarting) {\n        return imagesWaitingToStart.isEmpty() && imagesStarting.isEmpty();\n    }\n\n    private void shutdownExecutorService(ExecutorService executorService) {\n        if (!executorService.isShutdown()) {\n            executorService.shutdown();\n            try {\n                executorService.awaitTermination(10, TimeUnit.SECONDS);\n            } catch (InterruptedException e) {\n                log.warn(\"ExecutorService did not shutdown normally.\");\n                executorService.shutdownNow();\n            }\n        }\n    }\n\n    private void rethrowCause(ExecutionException e) throws IOException, InterruptedException, ExecException {\n        Throwable cause = e.getCause();\n        if (cause instanceof RuntimeException) {\n            throw (RuntimeException) cause;\n        } else if (cause instanceof IOException) {\n            throw (IOException) cause;\n        } else if (cause instanceof ExecException) {\n            throw (ExecException) cause;\n        } else if (cause instanceof InterruptedException) {\n                throw (InterruptedException) cause;\n        } else {\n            throw new RuntimeException(\"Start-Job failed with unexpected exception: \" + e.getCause().getMessage(),\n                                       e.getCause());\n        }\n    }\n\n    private void updateAliasesSet(Set<String> aliasesSet, String alias) {\n        // Add the alias to the set only when it is set. When it's\n        // not set it cant be used in the dependency resolution anyway, so we are ignoring\n        // it hence.\n        if (alias != null) {\n            aliasesSet.add(alias);\n        }\n    }\n\n    private void startImage(final ImageConfiguration imageConfig,\n                            final ServiceHub hub,\n                            final ExecutorCompletionService<StartedContainer> startingContainers,\n                            final PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper) throws IOException {\n\n        final RunService runService = hub.getRunService();\n        final Properties projProperties = project.getProperties();\n        final RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n        final PortMapping portMapping = runService.createPortMapping(runConfig, projProperties);\n        final LogDispatcher dispatcher = getLogDispatcher(hub);\n\n        StartContainerExecutor startExecutor = new StartContainerExecutor.Builder()\n            .exposeContainerProps(exposeContainerProps)\n            .dispatcher(dispatcher)\n            .follow(follow)\n            .log(log)\n            .portMapping(portMapping)\n            .gavLabel(getGavLabel())\n            .projectProperties(project.getProperties())\n            .basedir(project.getBasedir())\n            .imageConfig(imageConfig)\n            .serviceHub(hub)\n            .logOutputSpecFactory(serviceHubFactory.getLogOutputSpecFactory())\n            .showLogs(showLogs)\n            .containerNamePattern(containerNamePattern)\n            .buildTimestamp(getBuildTimestamp())\n            .build();\n\n        startingContainers.submit(() -> {\n\n            String containerId = startExecutor.startContainer();\n\n            // Update port-mapping writer\n            portMappingPropertyWriteHelper.add(portMapping, runConfig.getPortPropertyFile());\n\n            return new StartedContainer(imageConfig, containerId);\n        });\n    }\n\n    // Pick out all images who can be started right now because all their dependencies has been started\n    private List<ImageConfiguration> getImagesWhoseDependenciesHasStarted(Queue<ImageConfiguration> imagesRemaining,\n                                                                          Set<String> containersStarted,\n                                                                          Set<String> aliases) {\n        final List<ImageConfiguration> ret = new ArrayList<>();\n\n        // Check for all images which can be already started\n        for (ImageConfiguration imageWaitingToStart : imagesRemaining) {\n            List<String> allDependencies = imageWaitingToStart.getDependencies();\n            List<String> aliasDependencies = filterOutNonAliases(aliases, allDependencies);\n            if (containersStarted.containsAll(aliasDependencies)) {\n                ret.add(imageWaitingToStart);\n            }\n        }\n        return ret;\n    }\n\n    // Prepare start like creating custom networks, auto pull images, map aliases and return the list of images\n    // to start in the correct order\n    private Queue<ImageConfiguration> prepareStart(ServiceHub hub, QueryService queryService, RunService runService, Set<String> imageAliases)\n        throws DockerAccessException, MojoExecutionException {\n        final Queue<ImageConfiguration> imagesWaitingToStart = new ArrayDeque<>();\n        for (StartOrderResolver.Resolvable resolvable : runService.getImagesConfigsInOrder(queryService, getResolvedImages())) {\n            final ImageConfiguration imageConfig = (ImageConfiguration) resolvable;\n\n            // Still to check: How to work with linking, volumes, etc ....\n            //String imageName = new ImageName(imageConfig.getName()).getFullNameWithTag(registry);\n            RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n\n            RegistryService.RegistryConfig registryConfig = getRegistryConfig(pullRegistry);\n            ImagePullManager pullManager = getImagePullManager(determinePullPolicy(runConfig), autoPull);\n\n            hub.getRegistryService().pullImageWithPolicy(imageConfig.getName(), pullManager, registryConfig,\n                                                         queryService.hasImage(imageConfig.getName()));\n\n            NetworkConfig config = runConfig.getNetworkingConfig();\n            List<String> bindMounts = extractBindMounts(runConfig.getVolumeConfiguration());\n            List<VolumeConfiguration> volumes = getVolumes();\n            if(!bindMounts.isEmpty() && volumes != null) {\n                runService.createVolumesAsPerVolumeBinds(hub, bindMounts, volumes);\n            }\n            if (autoCreateCustomNetworks && config.isCustomNetwork()) {\n                runService.createCustomNetworkIfNotExistant(config.getCustomNetwork());\n            }\n            imagesWaitingToStart.add(imageConfig);\n            updateAliasesSet(imageAliases, imageConfig.getAlias());\n        }\n        return imagesWaitingToStart;\n    }\n\n    private List<String> extractBindMounts(RunVolumeConfiguration volumeConfiguration) {\n        if (volumeConfiguration == null) {\n            return Collections.emptyList();\n        }\n        return volumeConfiguration.getBind() != null ? volumeConfiguration.getBind() : Collections.emptyList();\n    }\n\n    private String determinePullPolicy(RunImageConfiguration runConfig) {\n        return runConfig.getImagePullPolicy() != null ? runConfig.getImagePullPolicy() : imagePullPolicy;\n    }\n\n    private List<String> filterOutNonAliases(Set<String> imageAliases, List<String> dependencies) {\n        List<String> ret = new ArrayList<>();\n        for (String alias : dependencies) {\n            if (imageAliases.contains(alias)) {\n                ret.add(alias);\n            }\n        }\n        return ret;\n    }\n\n    private ExecutorService getExecutorService() {\n        final ExecutorService executorService;\n        if (startParallel) {\n            executorService = Executors.newCachedThreadPool();\n        } else {\n            executorService = MoreExecutors.newDirectExecutorService();\n        }\n        return executorService;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/StopMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.Network;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.RunService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.ContainerNamingUtil;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.NamePatternUtil;\n\n\n/**\n * Mojo for stopping containers. If called together with <code>docker:start</code> (i.e.\n * when configured for integration testing in a lifefcycle phase), then only the container\n * started by this goal will be stopped and removed by default (this can be tuned with the\n * system property <code>docker.keepContainer</code>).\n *\n * If this goal is called standalone, then <em>all</em> containers are stopped, for which images\n * has been configured in the pom.xml\n *\n * @author roland\n * @since 26.03.14\n *\n */\n@Mojo(name = \"stop\", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)\npublic class StopMojo extends AbstractDockerMojo {\n\n    @Parameter(property = \"docker.keepRunning\", defaultValue = \"false\")\n    private boolean keepRunning;\n\n    /**\n     * Whether to create the customs networks (user-defined bridge networks) before starting automatically\n     */\n    @Parameter(property = \"docker.autoCreateCustomNetworks\", defaultValue = \"false\")\n    protected boolean autoCreateCustomNetworks;\n\n    @Parameter( property = \"docker.allContainers\", defaultValue = \"false\" )\n    private boolean allContainers;\n\n    @Parameter( property = \"docker.sledgeHammer\", defaultValue = \"false\" )\n    private boolean sledgeHammer;\n\n    /**\n     * Naming pattern for how to name containers when started\n     */\n    @Parameter(property = \"docker.containerNamePattern\")\n    private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;\n\n    @Parameter(property = \"docker.stopNamePattern\")\n    private String stopNamePattern;\n\n    @Override\n    protected void executeInternal(ServiceHub hub) throws MojoExecutionException, IOException, ExecException {\n        QueryService queryService = hub.getQueryService();\n        RunService runService = hub.getRunService();\n\n        GavLabel gavLabel = getGavLabel();\n\n        if (!keepRunning) {\n            if (invokedTogetherWithDockerStart()) {\n                runService.stopStartedContainers(keepContainer, removeVolumes, autoCreateCustomNetworks, gavLabel);\n            } else {\n                stopContainers(queryService, runService, gavLabel);\n            }\n        }\n\n        // Switch off all logging\n        LogDispatcher dispatcher = getLogDispatcher(hub);\n        dispatcher.untrackAllContainerLogs();\n    }\n\n    private void stopContainers(QueryService queryService, RunService runService, GavLabel gavLabel)\n            throws MojoExecutionException, IOException, ExecException {\n\n        Collection<Network> networksToRemove = getNetworksToRemove(queryService, gavLabel);\n\t\tList<DockerAccessException> thrownExceptions = new ArrayList<>();\n        for (ImageConfiguration image : getResolvedImages()) {\n\n            Collection<Container> existingContainers\n                    = getContainersForImage(queryService, image);\n\n            for (Container container : existingContainers) {\n                if (shouldStopContainer(container, gavLabel)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\trunService.stopContainer(container.getId(), image, keepContainer, removeVolumes);\n\t\t\t\t\t} catch (DockerAccessException exc) {\n\t\t\t\t\t\tthrownExceptions.add(exc);\n\t\t\t\t\t}\n                }\n            }\n        }\n        // If the mojo has a stopNamePattern, check to see if there are matching containers\n        for (Container container : getContainersForMojo(queryService)) {\n            if (shouldStopContainer(container, gavLabel)) {\n            \ttry {\n\t                runService.stopContainer(container.getId(),\n\t                        new ImageConfiguration.Builder().name(container.getImage()).build(),\n\t                        keepContainer, removeVolumes);\n            \t} catch (DockerAccessException exc) {\n\t\t\t\t\tthrownExceptions.add(exc);\n\t\t\t\t}\n            }\n        }\n\n\t\ttry {\n\t\t\trunService.removeCustomNetworks(networksToRemove);\n\t\t} catch (DockerAccessException exc) {\n\t\t\tthrownExceptions.add(exc);\n\t\t}\n\n\t\tif (!thrownExceptions.isEmpty()) {\n\t\t\tDockerAccessException exception = new DockerAccessException(\"At least one exception thrown during container removal.\");\n\t\t\tfor (DockerAccessException dae : thrownExceptions) {\n\t\t\t\texception.addSuppressed(dae);\n\t\t\t}\n\t\t\tthrow exception;\n\t\t}\n    }\n\n    private Collection<Container> getContainersForPattern(QueryService queryService, Matcher imageNameMatcher,\n                                                          Matcher containerNameMatcher)\n            throws IOException {\n        return queryService.listContainers(!keepContainer)\n                .stream()\n                .filter(c -> containerMatchesPattern(c, imageNameMatcher, containerNameMatcher))\n                .collect(Collectors.toList());\n    }\n\n    private Collection<Container> getContainersForMojo(QueryService queryService)\n            throws MojoExecutionException, IOException {\n        if(stopNamePattern != null) {\n            Matcher imageNameMatcher = getImageNameMatcher(stopNamePattern);\n\n            Matcher containerNameMatcher = getContainerNameMatcher(stopNamePattern);\n\n            if(imageNameMatcher == null && containerNameMatcher == null) {\n                log.warn(\"There are no image name or container name patterns in non-empty stopNamePattern: no containers will be stopped\");\n            } else {\n                return getContainersForPattern(queryService, imageNameMatcher, containerNameMatcher);\n            }\n        }\n\n        return Collections.emptyList();\n    }\n\n    private Collection<Container> getContainersForImage(QueryService queryService, ImageConfiguration image)\n            throws MojoExecutionException, IOException {\n\n        if(image.getStopNamePattern() != null) {\n            Matcher imageNameMatcher = getImageNameMatcher(image.getStopNamePattern());\n\n            Matcher containerNameMatcher = getContainerNameMatcher(image.getStopNamePattern());\n\n            if(imageNameMatcher == null && containerNameMatcher == null) {\n                log.warn(\"There are no image name or container name patterns in stopNamePattern for image %s: no containers will be stopped\", image.getName());\n                return Collections.emptyList();\n            }\n\n            return getContainersForPattern(queryService, imageNameMatcher, containerNameMatcher);\n        }\n\n        return ContainerNamingUtil.getContainersToStop(image,\n                containerNamePattern,\n                getBuildTimestamp(),\n                queryService.getContainersForImage(image.getName(), !keepContainer));\n    }\n\n    private Matcher getImageNameMatcher(String stopNamePattern) throws MojoExecutionException {\n        try {\n            String imageNameRegex = NamePatternUtil.convertNamePatternList(stopNamePattern, NamePatternUtil.IMAGE_FIELD, true);\n            if(imageNameRegex == null) {\n                log.debug(\"No image name patterns in stopNamePattern %s\", stopNamePattern);\n                return null;\n            } else {\n                log.debug(\"Converted stopNamePattern %s into image name regular expression %s\", stopNamePattern, imageNameRegex);\n                return Pattern.compile(imageNameRegex).matcher(\"\");\n            }\n        } catch(IllegalArgumentException e) {\n            throw new MojoExecutionException(e.getMessage(), e);\n        }\n    }\n\n    private Matcher getContainerNameMatcher(String stopNamePattern) throws MojoExecutionException {\n        try {\n            String containerNameRegex = NamePatternUtil.convertNamePatternList(stopNamePattern, NamePatternUtil.NAME_FIELD, true);;\n            if(containerNameRegex == null) {\n                log.debug(\"No container name patterns in stopNamePattern %s\", stopNamePattern);\n                return null;\n            } else {\n                log.debug(\"Converted stopNamePattern %s into container name regular expression %s\", stopNamePattern, containerNameRegex);\n                return Pattern.compile(containerNameRegex).matcher(\"\");\n            }\n        } catch(IllegalArgumentException e) {\n            throw new MojoExecutionException(e.getMessage(), e);\n        }\n    }\n\n    private boolean containerMatchesPattern(Container container, Matcher imageNameMatcher, Matcher containerNameMatcher) {\n        if(imageNameMatcher != null && container.getImage() != null && imageNameMatcher.reset(container.getImage()).find()) {\n            log.debug(\"Container image %s matched stopNamePattern\", container.getImage());\n            return true;\n        } else if(containerNameMatcher != null && container.getName() != null && containerNameMatcher.reset(container.getName()).find()) {\n            log.debug(\"Container name %s matched stopNamePattern\", container.getName());\n            return true;\n        } else {\n            log.debug(\"Neither container image %s nor name %s matched stopNamePattern\", container.getImage(), container.getName());\n            return false;\n        }\n    }\n\n    private boolean shouldStopContainer(Container container, GavLabel gavLabel) {\n        if (isStopAllContainers()) {\n            return true;\n        }\n        String key = gavLabel.getKey();\n        Map<String, String> labels = container.getLabels();\n        return labels.containsKey(key) && gavLabel.equals(new GavLabel(labels.get(key)));\n    }\n\n    private boolean isStopAllContainers() {\n        return (allContainers || sledgeHammer);\n    }\n\n    private boolean invokedTogetherWithDockerStart() {\n        Boolean startCalled = (Boolean) getPluginContext().get(CONTEXT_KEY_START_CALLED);\n        return startCalled != null && startCalled;\n    }\n\n    private Set<Network> getNetworksToRemove(QueryService queryService, GavLabel gavLabel) throws IOException {\n        if (!autoCreateCustomNetworks) {\n            return Collections.emptySet();\n        }\n        Set<Network> customNetworks = new HashSet<>();\n        Set<Network> networks = queryService.getNetworks();\n\n        for (ImageConfiguration image : getResolvedImages()) {\n\n            final NetworkConfig config = image.getRunConfiguration().getNetworkingConfig();\n            if (!config.isCustomNetwork() || config.getName() == null) {\n                continue;\n            }\n            final Network network = getNetworkByName(networks, config.getCustomNetwork());\n            if (network == null) {\n                continue;\n            }\n            customNetworks.add(network);\n            Collection<Container> existingContainers =\n                ContainerNamingUtil.getContainersToStop(image,\n                                                        containerNamePattern,\n                                                        getBuildTimestamp(),\n                                                        queryService.getContainersForImage(image.getName(), !keepContainer));\n\n            for (Container container : existingContainers) {\n                if (!shouldStopContainer(container, gavLabel)) {\n                    // it's sill in use don't collect it\n                    customNetworks.remove(network);\n                }\n            }\n        }\n        return customNetworks;\n    }\n\n    private Network getNetworkByName(Set<Network> networks, String networkName) {\n        for (Network network : networks) {\n            if (networkName.equals(network.getName())) {\n                return network;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/TagMojo.java",
    "content": "package io.fabric8.maven.docker;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\nimport java.util.List;\n\n/**\n * Goal for Tagging an image so that it becomes part of a repository.\n *\n */\n@Mojo(name = \"tag\", defaultPhase = LifecyclePhase.INSTALL)\npublic class TagMojo extends AbstractDockerMojo {\n    @Parameter(property = \"docker.skip.tag\", defaultValue = \"false\")\n    private boolean skipTag;\n\n    @Parameter(property = \"docker.image.tag\")\n    private String tagName;\n\n    @Parameter(property = \"docker.image.repo\")\n    private String repo;\n\n    @Override\n    public void executeInternal(ServiceHub hub) throws DockerAccessException, MojoExecutionException {\n        if (skipTag) {\n            return;\n        }\n\n        List<ImageConfiguration> imageConfigs = getResolvedImages();\n        for (ImageConfiguration imageConfig : imageConfigs) {\n            hub.getBuildService().tagImage(imageConfig.getName(), tagName, repo);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/VolumeCreateMojo.java",
    "content": "package io.fabric8.maven.docker;\n\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.service.VolumeService;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\n\n/**\n * Mojo to create named volumes, useful for preparing integration tests\n *\n * @author Tom Burton\n * @version Dec 15, 2016\n */\n@Mojo(name = \"volume-create\", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST)\npublic class VolumeCreateMojo extends AbstractDockerMojo {\n\n    @Override\n    protected void executeInternal(ServiceHub serviceHub) throws DockerAccessException, MojoExecutionException {\n        if (getVolumes() == null) {\n            log.info(\"No volume configuration found.\");\n            return;\n        }\n        VolumeService volService = serviceHub.getVolumeService();\n\n        for (VolumeConfiguration volume : getVolumes()) {\n            log.info(\"Creating volume '%s'\", volume.getName());\n            volService.createVolume(volume);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/VolumeRemoveMojo.java",
    "content": "package io.fabric8.maven.docker;\n\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.service.VolumeService;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\n\n/**\n *  Mojo to remove volumes created with {@link VolumeCreateMojo}\n *\n *  @author Tom Burton\n */\n@Mojo(name = \"volume-remove\", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)\npublic class VolumeRemoveMojo extends AbstractDockerMojo {\n\n   @Override\n   protected void executeInternal(ServiceHub serviceHub)\n         throws DockerAccessException, MojoExecutionException  {\n       if(getVolumes() == null){\n           log.info(\"No volume configuration found.\");\n           return;\n       }\n       VolumeService volService = serviceHub.getVolumeService();\n\n       for ( VolumeConfiguration volume : getVolumes()) {\n           log.info(\"Removing volume %s\", volume.getName());\n           volService.removeVolume(volume.getName());\n       }\n   }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/WatchMojo.java",
    "content": "package io.fabric8.maven.docker;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.IOException;\n\nimport io.fabric8.maven.docker.config.WatchMode;\nimport io.fabric8.maven.docker.service.BuildService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.service.WatchService;\nimport io.fabric8.maven.docker.util.ContainerNamingUtil;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Mojo for watching source code changes.\n *\n * This Mojo does essentially\n * two things when it detects a image content change:\n *\n * <ul>\n *     <li>Rebuilding one or more images</li>\n *     <li>Restarting restarting one or more containers </li>\n * </ul>\n *\n * @author roland\n * @since 16/06/15\n */\n@Mojo(name = \"watch\")\npublic class WatchMojo extends AbstractBuildSupportMojo {\n\n    /**\n     * Watching mode for rebuilding images\n     */\n    @Parameter(property = \"docker.watchMode\", defaultValue = \"both\")\n    private WatchMode watchMode;\n\n    @Parameter(property = \"docker.watchInterval\", defaultValue = \"5000\")\n    private int watchInterval;\n\n    @Parameter(property = \"docker.keepRunning\", defaultValue = \"false\")\n    private boolean keepRunning;\n\n    @Parameter(property = \"docker.watchPostGoal\")\n    private String watchPostGoal;\n\n    @Parameter(property = \"docker.watchPostExec\")\n    private String watchPostExec;\n\n    /**\n     * Naming pattern for how to name containers when started\n     */\n    @Parameter(property = \"docker.containerNamePattern\")\n    private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;\n\n    /**\n     * Whether to create the customs networks (user-defined bridge networks) before starting automatically\n     */\n    @Parameter(property = \"docker.autoCreateCustomNetworks\", defaultValue = \"false\")\n    protected boolean autoCreateCustomNetworks;\n\n    @Override\n    protected synchronized void executeInternal(ServiceHub hub) throws IOException,\n                                                                       MojoExecutionException {\n\n        BuildService.BuildContext buildContext = getBuildContext();\n        WatchService.WatchContext watchContext = getWatchContext(hub);\n\n        hub.getWatchService().watch(watchContext, buildContext, getResolvedImages());\n    }\n\n    protected WatchService.WatchContext getWatchContext(ServiceHub hub) throws IOException {\n        return new WatchService.WatchContext.Builder()\n                .watchInterval(watchInterval)\n                .watchMode(watchMode)\n                .watchPostGoal(watchPostGoal)\n                .watchPostExec(watchPostExec)\n                .autoCreateCustomNetworks(autoCreateCustomNetworks)\n                .keepContainer(keepContainer)\n                .keepRunning(keepRunning)\n                .removeVolumes(removeVolumes)\n                .containerNamePattern(containerNamePattern)\n                .buildTimestamp(getBuildTimestamp())\n                .pomLabel(getGavLabel())\n                .mojoParameters(createMojoParameters())\n                .follow(follow())\n                .showLogs(showLogs())\n                .serviceHubFactory(serviceHubFactory)\n                .hub(hub)\n                .dispatcher(getLogDispatcher(hub))\n                .build();\n    }\n\n    private String showLogs() {\n        return System.getProperty(\"docker.showLogs\");\n    }\n\n    private boolean follow() {\n        return Boolean.valueOf(System.getProperty(\"docker.follow\", \"false\"));\n    }\n}"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/AuthConfig.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonObject;\n\nimport org.apache.commons.codec.binary.Base64;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n\n/**\n * Configuration object holding auth information for\n * pushing to Docker\n *\n * @author roland\n * @since 22.07.14\n */\npublic class AuthConfig {\n\n    public final static AuthConfig EMPTY_AUTH_CONFIG = new AuthConfig(\"\", \"\", \"\", \"\");\n\n    public static final String AUTH_USERNAME = \"username\";\n    public static final String AUTH_PASSWORD = \"password\";\n    public static final String AUTH_EMAIL = \"email\";\n    public static final String AUTH_AUTH = \"auth\";\n    public static final String AUTH_IDENTITY_TOKEN = \"identityToken\";\n\n    private final String username;\n    private final String password;\n    private final String email;\n    private final String auth;\n    private final String identityToken;\n\n    private final String authEncoded;\n\n    public AuthConfig(Map<String,String> params) {\n        this(params.get(AUTH_USERNAME),\n                params.get(AUTH_PASSWORD),\n                params.get(AUTH_EMAIL),\n                params.get(AUTH_AUTH),\n                params.get(AUTH_IDENTITY_TOKEN));\n    }\n\n    public AuthConfig(String username, String password, String email, String auth) {\n        this(username, password, email, auth, null);\n    }\n\n    public AuthConfig(String username, String password, String email, String auth, String identityToken) {\n        this.username = username;\n        this.password = password;\n        this.email = email;\n        this.auth = auth;\n        this.identityToken = identityToken;\n        authEncoded = createAuthEncoded();\n    }\n\n    /**\n     * Constructor which takes an base64 encoded credentials in the form 'user:password'\n     *\n     * @param credentialsEncoded the docker encoded user and password\n     * @param email the email to use for authentication\n     */\n    public AuthConfig(String credentialsEncoded, String email) {\n        this(credentialsEncoded, email, null);\n    }\n\n    /**\n     * Constructor which takes an base64 encoded credentials in the form 'user:password'\n     *\n     * @param credentialsEncoded the docker encoded user and password\n     * @param email the email to use for authentication\n     */\n    public AuthConfig(String credentialsEncoded, String email, String identityToken) {\n        String credentials = new String(Base64.decodeBase64(credentialsEncoded));\n        String[] parsedCreds = credentials.split(\":\",2);\n        username = parsedCreds[0];\n        password = parsedCreds[1];\n        this.email = email;\n        this.identityToken = identityToken;\n        auth = null;\n        authEncoded = createAuthEncoded();\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public String getAuth() {\n        return auth;\n    }\n\n    public String getIdentityToken() {\n        return identityToken;\n    }\n\n    public String toHeaderValue() {\n        return authEncoded;\n    }\n\n    // ======================================================================================================\n\n    private String createAuthEncoded() {\n        JsonObject ret = new JsonObject();\n        if(identityToken != null) {\n            putNonNull(ret, AUTH_IDENTITY_TOKEN, identityToken);\n        } else {\n            putNonNull(ret, AUTH_USERNAME, username);\n            putNonNull(ret, AUTH_PASSWORD, password);\n            putNonNull(ret, AUTH_EMAIL, email);\n            putNonNull(ret, AUTH_AUTH, auth);\n        }\n\n        try {\n            return encodeBase64ChunkedURLSafeString(ret.toString().getBytes(\"UTF-8\"));\n        } catch (UnsupportedEncodingException e) {\n            return encodeBase64ChunkedURLSafeString(ret.toString().getBytes());\n        }\n    }\n\n    /**\n     * Encodes the given binaryData in a format that is compatible with the Docker Engine API.\n     * That is, base64 encoded, padded, and URL safe.\n     *\n     * @param binaryData data to encode\n     * @return encoded data\n     */\n    private String encodeBase64ChunkedURLSafeString(final byte[] binaryData) {\n        return Base64.encodeBase64String(binaryData)\n                .replace('+', '-')\n                .replace('/', '_');\n    }\n\n    private void putNonNull(JsonObject ret, String key, String value) {\n        if (value != null) {\n            ret.addProperty(key,value);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/BuildOptions.java",
    "content": "package io.fabric8.maven.docker.access;/*\n *\n * Copyright 2015-2016 Red Hat, Inc.\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\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.util.JsonFactory;\n\n/**\n * @author roland\n * @since 03/01/17\n */\npublic class BuildOptions {\n\n    private Map<String, String> options;\n\n    public BuildOptions() {\n        this(new HashMap<String, String>());\n    }\n\n    public BuildOptions(Map<String, String> options) {\n        this.options = options != null ? new HashMap<>(options) : new HashMap<String, String>();\n    }\n\n    public BuildOptions addOption(String key, String value) {\n        options.put(key,value);\n        return this;\n    }\n\n    public BuildOptions dockerfile(String name) {\n        if (name != null) {\n            options.put(\"dockerfile\", name);\n        }\n        return this;\n    }\n\n    public BuildOptions forceRemove(boolean forceRm) {\n        if (forceRm) {\n            options.put(\"forcerm\", \"1\");\n        }\n        return this;\n    }\n\n    public BuildOptions noCache(boolean noCache) {\n        options.put(\"nocache\", noCache ? \"1\" : \"0\");\n        return this;\n    }\n\n    public BuildOptions squash(boolean squash) {\n        options.put(\"squash\", squash ? \"1\" : \"0\");\n        return this;\n    }\n\n    public BuildOptions cacheFrom(List<String> cacheFrom) {\n        if (cacheFrom == null || cacheFrom.isEmpty()) {\n            options.remove(\"cachefrom\");\n        } else {\n            options.put(\"cachefrom\", JsonFactory.newJsonArray(cacheFrom).toString());\n        }\n        return this;\n    }\n\n    public BuildOptions buildArgs(Map<String, String> buildArgs) {\n        if (buildArgs != null && buildArgs.size() > 0) {\n            options.put(\"buildargs\", JsonFactory.newJsonObject(buildArgs).toString());\n        }\n        return this;\n    }\n\n    public BuildOptions network(String network) {\n        if (network != null && !network.isEmpty()) {\n            options.put(\"networkmode\", network);\n        }\n        return this;\n    }\n\n    public Map<String, String> getOptions() {\n        return options;\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ContainerCreateConfig.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport org.apache.commons.text.StrSubstitutor;\n\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.JsonFactory;\n\npublic class ContainerCreateConfig {\n\n    private final JsonObject createConfig = new JsonObject();\n    private final String imageName;\n\n    public ContainerCreateConfig(String imageName) {\n        this.imageName = imageName;\n        createConfig.addProperty(\"Image\", imageName);\n    }\n\n    public ContainerCreateConfig binds(List<String> volumes) {\n        if (volumes != null && !volumes.isEmpty()) {\n            JsonObject extractedVolumes = new JsonObject();\n\n            for (String volume : volumes) {\n                extractedVolumes.add(extractContainerPath(volume),\n                                     new JsonObject());\n            }\n            createConfig.add(\"Volumes\", extractedVolumes);\n        }\n        return this;\n    }\n\n    public ContainerCreateConfig command(Arguments command) {\n        if (command != null) {\n            createConfig.add(\"Cmd\", JsonFactory.newJsonArray(command.asStrings()));\n        }\n        return this;\n    }\n\n    public ContainerCreateConfig domainname(String domainname) {\n        return add(\"Domainname\", domainname);\n    }\n\n    public ContainerCreateConfig entrypoint(Arguments entrypoint) {\n        if (entrypoint != null) {\n            createConfig.add(\"Entrypoint\", JsonFactory.newJsonArray(entrypoint.asStrings()));\n        }\n        return this;\n    }\n\n    public ContainerCreateConfig environment(String envPropsFile, Map<String, String> env, Map mavenProps) throws IllegalArgumentException {\n\n        Properties envProps = new Properties();\n        if (env != null && env.size() > 0) {\n            for (Map.Entry<String, String> entry : env.entrySet()) {\n                String value = entry.getValue();\n                if (value == null) {\n                    value = \"\";\n                } else if(value.matches(\"^\\\\+\\\\$\\\\{.*}$\")) {\n                    /*\n                     * This case is to handle the Maven interpolation issue which used\n                     * to occur when using ${..} only without any suffix.\n                     */\n                    value = value.substring(1, value.length());\n                }\n                envProps.put(entry.getKey(), StrSubstitutor.replace(value, mavenProps));\n            }\n        }\n        if (envPropsFile != null) {\n            // Props from external file take precedence\n            addPropertiesFromFile(envPropsFile, envProps);\n        }\n\n        if (envProps.size() > 0) {\n            addEnvironment(envProps);\n        }\n        return this;\n    }\n\n    public ContainerCreateConfig labels(Map<String,String> labels) {\n        if (labels != null && labels.size() > 0) {\n            createConfig.add(\"Labels\", JsonFactory.newJsonObject(labels));\n        }\n        return this;\n    }\n\n    public ContainerCreateConfig exposedPorts(Set<String> portSpecs) {\n        if (portSpecs != null && portSpecs.size() > 0) {\n            JsonObject exposedPorts = new JsonObject();\n            for (String portSpec : portSpecs) {\n                exposedPorts.add(portSpec, new JsonObject());\n            }\n            createConfig.add(\"ExposedPorts\", exposedPorts);\n        }\n        return this;\n    }\n\n    public String getImageName() {\n        return imageName;\n    }\n\n    public ContainerCreateConfig hostname(String hostname) {\n        return add(\"Hostname\", hostname);\n    }\n\n    public ContainerCreateConfig user(String user) {\n        return add(\"User\", user);\n    }\n\n    public ContainerCreateConfig workingDir(String workingDir) {\n        return add(\"WorkingDir\", workingDir);\n    }\n\n    public ContainerCreateConfig hostConfig(ContainerHostConfig startConfig) {\n        return add(\"HostConfig\", startConfig.toJsonObject());\n    }\n\n    public ContainerCreateConfig networkingConfig(ContainerNetworkingConfig networkingConfig) {\n        return add(\"NetworkingConfig\", networkingConfig.toJsonObject());\n    }\n\n    /**\n     * Get JSON which is used for <em>creating</em> a container\n     *\n     * @return string representation for JSON representing creating a container\n     */\n    public String toJson() {\n        return createConfig.toString();\n    }\n\n    // =======================================================================\n\n    private ContainerCreateConfig add(String name, String value) {\n        if (value != null) {\n            createConfig.addProperty(name, value);\n        }\n        return this;\n    }\n\n    private ContainerCreateConfig add(String name, JsonObject value) {\n        if (value != null) {\n            createConfig.add(name, value);\n        }\n        return this;\n    }\n\n    private String extractContainerPath(String volume) {\n        String path  = EnvUtil.fixupPath(volume);\n        if (path.contains(\":\")) {\n            String[] parts = path.split(\":\");\n            if (parts.length > 1) {\n                return parts[1];\n            }\n        }\n        return path;\n    }\n\n    private void addEnvironment(Properties envProps) {\n        JsonArray containerEnv = new JsonArray();\n        Enumeration keys = envProps.keys();\n        while (keys.hasMoreElements()) {\n            String key = (String) keys.nextElement();\n            String value = envProps.getProperty(key);\n            if (value == null) {\n                value = \"\";\n            }\n            containerEnv.add(key + \"=\" + value);\n        }\n        createConfig.add(\"Env\", containerEnv);\n    }\n\n    private void addPropertiesFromFile(String envPropsFile, Properties envProps) {\n        // External properties override internally specified properties\n        try {\n            FileReader reader = new FileReader(envPropsFile);\n            envProps.load(reader);\n        } catch (FileNotFoundException e) {\n            throw new IllegalArgumentException(String.format(\"Cannot find environment property file '%s'\", envPropsFile));\n        } catch (IOException e) {\n            throw new IllegalArgumentException(String.format(\"Error while loading environment properties: %s\", e.getMessage()), e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ContainerHostConfig.java",
    "content": "package io.fabric8.maven.docker.access;\r\n\r\nimport com.google.gson.JsonArray;\r\nimport com.google.gson.JsonObject;\r\n\r\nimport java.net.InetAddress;\r\nimport java.net.UnknownHostException;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport io.fabric8.maven.docker.config.LogConfiguration;\r\nimport io.fabric8.maven.docker.config.UlimitConfig;\r\nimport io.fabric8.maven.docker.util.EnvUtil;\r\nimport io.fabric8.maven.docker.util.JsonFactory;\r\n\r\npublic class ContainerHostConfig {\r\n\r\n    final JsonObject startConfig = new JsonObject();\r\n\r\n    public ContainerHostConfig() {}\r\n\r\n    public ContainerHostConfig binds(List<String> bind) {\r\n        if (bind != null && !bind.isEmpty()) {\r\n            JsonArray binds = new JsonArray();\r\n\r\n            for (String volume : bind) {\r\n                volume = EnvUtil.fixupPath(volume);\r\n\r\n                if (volume.contains(\":\")) {\r\n                    binds.add(volume);\r\n                }\r\n            }\r\n            startConfig.add(\"Binds\", binds);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    public ContainerHostConfig capAdd(List<String> capAdd) {\r\n        return addAsArray(\"CapAdd\", capAdd);\r\n    }\r\n\r\n    public ContainerHostConfig capDrop(List<String> capDrop) {\r\n        return addAsArray(\"CapDrop\", capDrop);\r\n    }\r\n\r\n    public ContainerHostConfig securityOpts(List<String> securityOpt) {\r\n        return addAsArray(\"SecurityOpt\", securityOpt);\r\n    }\r\n\r\n    public ContainerHostConfig memory(Long memory) {\r\n        return add(\"Memory\", memory);\r\n    }\r\n\r\n    public ContainerHostConfig memorySwap(Long memorySwap) {\r\n        return add(\"MemorySwap\", memorySwap);\r\n    }\r\n\r\n    public ContainerHostConfig dns(List<String> dns) {\r\n        return addAsArray(\"Dns\", dns);\r\n    }\r\n\r\n    public ContainerHostConfig networkMode(String net) {\r\n        return add(\"NetworkMode\",net);\r\n    }\r\n\r\n    public ContainerHostConfig dnsSearch(List<String> dnsSearch) {\r\n        return addAsArray(\"DnsSearch\", dnsSearch);\r\n    }\r\n\r\n    public ContainerHostConfig cpuShares(Long cpuShares) {\r\n        return add(\"CpuShares\", cpuShares);\r\n    }\r\n\r\n    public ContainerHostConfig cpus(Long cpus) {\r\n        return add (\"NanoCpus\", cpus);\r\n    }\r\n\r\n    public ContainerHostConfig cpuSet(String cpuSet) {\r\n        return add(\"CpusetCpus\", cpuSet);\r\n    }\r\n\r\n    public ContainerHostConfig extraHosts(List<String> extraHosts) throws IllegalArgumentException {\r\n        if (extraHosts != null) {\r\n            List<String> mapped = new ArrayList<>();\r\n            for (int i = 0; i < extraHosts.size(); i++) {\r\n                String[] parts = extraHosts.get(i).split(\":\");\r\n                if (parts.length == 1) {\r\n                    throw new IllegalArgumentException(\"extraHosts must be in the form <host:host|ip>\");\r\n                }\r\n\r\n                try {\r\n                    mapped.add(i, parts[0] + \":\" + InetAddress.getByName(parts[1]).getHostAddress());\r\n                } catch (UnknownHostException e) {\r\n                    throw new IllegalArgumentException(\"unable to resolve ip address for \" + parts[1], e);\r\n                }\r\n            }\r\n            return addAsArray(\"ExtraHosts\", mapped);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    public ContainerHostConfig volumesFrom(List<String> volumesFrom) {\r\n        return addAsArray(\"VolumesFrom\", volumesFrom);\r\n    }\r\n\r\n    public ContainerHostConfig ulimits(List<UlimitConfig> ulimitsConfig) {\r\n    \tif (ulimitsConfig != null && ulimitsConfig.size() > 0) {\r\n            JsonArray ulimits = new JsonArray();\r\n            for (UlimitConfig ulimit : ulimitsConfig) {\r\n                JsonObject ulimitConfigJson = new JsonObject();\r\n                ulimitConfigJson.addProperty(\"Name\", ulimit.getName());\r\n                addIfNotNull(ulimitConfigJson, \"Hard\", ulimit.getHard());\r\n                addIfNotNull(ulimitConfigJson, \"Soft\", ulimit.getSoft());\r\n                ulimits.add(ulimitConfigJson);\r\n            }\r\n\r\n            startConfig.add(\"Ulimits\", ulimits);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private void addIfNotNull(JsonObject json, String key, Integer value) {\r\n        if (value != null) {\r\n            json.addProperty(key, value);\r\n        }\r\n    }\r\n\r\n    public ContainerHostConfig links(List<String> links) {\r\n        return addAsArray(\"Links\", links);\r\n    }\r\n\r\n    public ContainerHostConfig portBindings(PortMapping portMapping) {\r\n        JsonObject portBindings = portMapping.toDockerPortBindingsJson();\r\n        if (portBindings != null) {\r\n            startConfig.add(\"PortBindings\", portBindings);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    public ContainerHostConfig privileged(Boolean privileged) {\r\n        return add(\"Privileged\", privileged);\r\n    }\r\n\r\n    public ContainerHostConfig tmpfs(List<String> mounts) {\r\n        if (mounts != null && mounts.size() > 0) {\r\n            JsonObject tmpfs = new JsonObject();\r\n            for (String mount : mounts) {\r\n                int idx = mount.indexOf(':');\r\n                if (idx > -1) {\r\n                    tmpfs.addProperty(mount.substring(0,idx), mount.substring(idx+1));\r\n                } else {\r\n                    tmpfs.addProperty(mount, \"\");\r\n                }\r\n            }\r\n            startConfig.add(\"Tmpfs\", tmpfs);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    public ContainerHostConfig shmSize(Long shmSize) {\r\n        return add(\"ShmSize\", shmSize);\r\n    }\r\n\r\n    public ContainerHostConfig restartPolicy(String name, int retry) {\r\n        if (name != null) {\r\n            JsonObject policy = new JsonObject();\r\n            policy.addProperty(\"Name\", name);\r\n            policy.addProperty(\"MaximumRetryCount\", retry);\r\n\r\n            startConfig.add(\"RestartPolicy\", policy);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    public ContainerHostConfig logConfig(LogConfiguration logConfig) {\r\n        if (logConfig != null) {\r\n            LogConfiguration.LogDriver logDriver = logConfig.getDriver();\r\n            if (logDriver != null) {\r\n                JsonObject logConfigJson = new JsonObject();\r\n                logConfigJson.addProperty(\"Type\", logDriver.getName());\r\n\r\n                Map<String,String> opts = logDriver.getOpts();\r\n                if (opts != null && opts.size() > 0) {\r\n                    JsonObject config = new JsonObject();\r\n                    for (Map.Entry<String, String> logOpt : opts.entrySet()) {\r\n                        config.addProperty(logOpt.getKey(), logOpt.getValue());\r\n                    }\r\n                    logConfigJson.add(\"Config\", config);\r\n                }\r\n\r\n                startConfig.add(\"LogConfig\", logConfigJson);\r\n            }\r\n        }\r\n        return this;\r\n    }\r\n    \r\n    public ContainerHostConfig readonlyRootfs(Boolean readOnly) {\r\n        return add(\"ReadonlyRootfs\", readOnly);\r\n    }\r\n\r\n    public ContainerHostConfig autoRemove(Boolean autoRemove) {\r\n        return add(\"AutoRemove\", autoRemove);\r\n    }\r\n\r\n    /**\r\n     * Get JSON which is used for <em>starting</em> a container\r\n     *\r\n     * @return string representation for JSON representing the configuration for starting a container\r\n     */\r\n    public String toJson() {\r\n        return startConfig.toString();\r\n    }\r\n\r\n    public JsonObject toJsonObject() {\r\n        return startConfig;\r\n    }\r\n\r\n    ContainerHostConfig addAsArray(String propKey, List<String> props) {\r\n        if (props != null) {\r\n            startConfig.add(propKey, JsonFactory.newJsonArray(props));\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private ContainerHostConfig add(String name, String value) {\r\n        if (value != null) {\r\n            startConfig.addProperty(name, value);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private ContainerHostConfig add(String name, Boolean value) {\r\n        if (value != null) {\r\n            startConfig.addProperty(name, value);\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private ContainerHostConfig add(String name, Long value) {\r\n        if (value != null) {\r\n            startConfig.addProperty(name, value);\r\n        }\r\n        return this;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ContainerNetworkingConfig.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.util.JsonFactory;\n\npublic class ContainerNetworkingConfig {\n\n    private final JsonObject networkingConfig = new JsonObject();\n\n    /**\n     * Add networking aliases to a custom network\n     *\n     * @param config network config as configured in the pom.xml\n     * @return this configuration\n     */\n    public ContainerNetworkingConfig aliases(NetworkConfig config) {\n        JsonObject endPoints = new JsonObject();\n        endPoints.add(\"Aliases\", JsonFactory.newJsonArray(config.getAliases()));\n\n        JsonObject endpointConfigMap = new JsonObject();\n        endpointConfigMap.add(config.getCustomNetwork(), endPoints);\n\n        networkingConfig.add(\"EndpointsConfig\", endpointConfigMap);\n        return this;\n    }\n\n    public String toJson() {\n        return networkingConfig.toString();\n    }\n\n    public JsonObject toJsonObject() {\n        return networkingConfig;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/DockerAccess.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.File;\nimport java.util.List;\n\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.access.log.LogGetHandle;\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.log.LogOutputSpec;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.model.ExecDetails;\nimport io.fabric8.maven.docker.model.Image;\nimport io.fabric8.maven.docker.model.Network;\n\n/**\n * Access to the <a href=\"http://docs.docker.io/en/latest/reference/api/docker_remote_api/\">Docker API</a> which\n * provides the methods needed bu this maven plugin.\n *\n * @author roland\n * @since 04.04.14\n */\npublic interface DockerAccess {\n\n    /**\n     * Get the API version of the running server\n     *\n     * @return api version in the form \"1.24\"\n     * @throws DockerAccessException if the api version could not be obtained\n     */\n    String getServerApiVersion() throws DockerAccessException;\n\n    /**\n     * Get a container\n     *\n     * @param containerIdOrName container id or name\n     * @return <code>ContainerDetails<code> representing the container or null if none could be found\n     * @throws DockerAccessException if the container could not be inspected\n     */\n    ContainerDetails getContainer(String containerIdOrName) throws DockerAccessException;\n\n    /**\n     * Get an exec container which is the result of executing a command in a running container.\n     *\n     * @param containerIdOrName exec container id or name\n     * @return <code>ExecDetails<code> representing the container or null if none could be found\n     * @throws DockerAccessException if the container could not be inspected\n     */\n    ExecDetails getExecContainer(String containerIdOrName) throws DockerAccessException;\n\n    /**\n     * Check whether the given name exists as image at the docker daemon\n     *\n     * @param name image name to check\n     * @return true if the image exists\n     */\n    boolean hasImage(String name) throws DockerAccessException;\n\n    /**\n     * Get the image id of a given name or <code>null</code> if no such image exists\n     *\n     * @param name name to lookup\n     * @return the image id or <code>null</code>\n     */\n    String getImageId(String name) throws DockerAccessException;\n\n    /**\n     * List all containers from the Docker server.\n     *\n     * @param all whether to fetch also stopped containers. If false only running containers are returned\n     * @return list of <code>Container</code> objects or an empty list if none is found\n     * @throws DockerAccessException if the request fails\n     */\n    List<Container> listContainers(boolean all) throws DockerAccessException;\n\n    /**\n     * Get all containers which are build from an image. By default only the last containers are considered but this\n     * can be tuned with a global parameters.\n     *\n     * @param image for which its container are looked up\n     * @param all whether to fetch also stopped containers. If false only running containers are returned\n     * @return list of <code>Container</code> objects or an empty list if none is found\n     * @throws DockerAccessException if the request fails\n     */\n    List<Container> getContainersForImage(String image, boolean all) throws DockerAccessException;\n\n    /**\n     * Starts a previously set up exec instance (via {@link #createExecContainer(String, Arguments)} container\n     * this API sets up a session with the exec command. Output is streamed to the log. This methods\n     * returns only when the exec command has finished (i.e this method calls the command in a non-detached mode).\n     *\n     * @param containerId id of the exec container\n     * @param outputSpec how to print out the output of the command\n     * @throws DockerAccessException if the container could not be created.\n     */\n    void startExecContainer(String containerId, LogOutputSpec outputSpec) throws DockerAccessException;\n\n    /**\n     * Sets up an exec instance for a running container id\n     *\n     * @param containerId id of the running container which the exec container will be created for\n     * @param arguments container exec commands to run\n     * @throws DockerAccessException if the container could not be created.\n     */\n    String createExecContainer(String containerId, Arguments arguments) throws DockerAccessException;\n\n    /**\n     * Create a container from the given image.\n     *\n     * <p>The <code>container id</code> will be set on the <code>container</code> upon successful creation.</p>\n     *\n     * @param configuration container configuration\n     * @param containerName name container should be created with or <code>null</code> for a docker provided name\n     * @throws DockerAccessException if the container could not be created.\n     */\n    String createContainer(ContainerCreateConfig configuration, String containerName) throws DockerAccessException;\n\n    /**\n     * Start a container.\n     *\n     * @param containerId id of the container to start\n     * @throws DockerAccessException if the container could not be started.\n     */\n    void startContainer(String containerId) throws DockerAccessException;\n\n    /**\n     * Stop a container.\n     *\n     * @param containerId the container id\n     * @param killWait the time to wait between stop and kill (in seconds)\n     * @throws DockerAccessException if the container could not be stopped.\n     */\n    void stopContainer(String containerId, int killWait) throws DockerAccessException;\n\n    /**\n     * Kill a container\n     *\n     * @param containerId the container id\n     * @throws DockerAccessException if container failed to be killed\n     */\n    void killContainer(String containerId) throws DockerAccessException;\n\n    /** Copy an archive (must be a tar) into a running container\n     * Get all containers matching a certain label. This might not be a cheap operation especially if many containers\n     * are running. Use with care.\n     *\n     * @param containerId container to copy into\n     * @param archive local archive to copy into\n     * @param targetPath target path to use\n     * @throws DockerAccessException if the archive could not be copied\n     */\n    void copyArchive(String containerId, File archive, String targetPath)\n            throws DockerAccessException;\n\n    /**\n     * Get logs for a container up to now synchronously.\n     *\n     * @param containerId container id\n     * @param callback which is called for each line received\n     */\n    void getLogSync(String containerId, LogCallback callback);\n\n    /**\n     * Get logs asynchronously. This call will start a thread in the background for doing the request.\n     * It returns a handle which can be used to abort the request on demand.\n     *\n     * @param containerId id of the container for which to fetch the logs\n     * @param callback to call when log data arrives\n     * @return handle for managing the lifecycle of the thread\n     */\n    LogGetHandle getLogAsync(String containerId, LogCallback callback);\n\n    /**\n     * Remove a container with the given id\n     *\n     * @param containerId container id for the container to remove\n     * @param removeVolumes if true, will remove any volumes associated to container\n     * @throws DockerAccessException if the container couldn't be removed.\n     */\n    void removeContainer(String containerId, boolean removeVolumes) throws DockerAccessException;\n\n    /**\n     * List the containers on the server\n     * @param all if true, return untagged images\n     * @return the images list (may be empty but never null)\n     * @throws DockerAccessException if the list couldn't be retrieved\n     */\n    List<Image> listImages(boolean all) throws DockerAccessException;\n\n    /**\n     * Load an image from an archive.\n     *\n     * @param image the image to pull.\n     * @param tarArchive archive file\n     * @throws DockerAccessException if the image couldn't be loaded.\n     */\n    void loadImage(String image, File tarArchive) throws DockerAccessException;\n\n    /**\n     * Pull an image from a remote registry and store it locally.\n     *\n     * @param image the image to pull.\n     * @param authConfig authentication configuration used when pulling an image\n     * @param registry an optional registry from where to pull the image. Can be null.\n     * @throws DockerAccessException if the image couldn't be pulled.\n     */\n    void pullImage(String image, AuthConfig authConfig, String registry) throws DockerAccessException;\n\n    /**\n     * Push an image to a registry. An registry can be specified which is used as target\n     * if the image name the image does not contain a registry.\n     *\n     * If an optional registry is used, the image is also tagged with the full name containing the registry as\n     * part (if not already existent)\n     *\n     * @param image image name to push\n     * @param authConfig authentication configuration\n     * @param registry optional registry to which the image should be pushed.\n     * @param retries optional number of times the push should be retried on a 500 error\n     * @throws DockerAccessException in case pushing fails\n     */\n    void pushImage(String image, AuthConfig authConfig, String registry, int retries) throws DockerAccessException;\n\n    /**\n     * Create an docker image from a given archive\n     *\n     * @param image name of the image to build or <code>null</code> if none should be used\n     * @param dockerArchive from which the docker image should be build\n     * @param options additional query arguments to add when building the image. Can be null.\n     * @throws DockerAccessException if docker host reports an error during building of an image\n     */\n    void buildImage(String image, File dockerArchive, BuildOptions options) throws DockerAccessException;\n\n    /**\n     * Alias an image in the repository with a complete new name. (Note that this maps to a Docker Remote API 'tag'\n     * operation, which IMO is badly named since it also can generate a complete alias to a given image)\n     *\n     * @param sourceImage full name (including tag) of the image to alias\n     * @param targetImage the alias name\n     * @param force forced tagging\n     * @throws DockerAccessException if the original image doesn't exist or another error occurs somehow.\n     */\n    void tag(String sourceImage, String targetImage, boolean force) throws DockerAccessException;\n\n    /**\n     * Remove an image from this docker installation\n     *\n     * @param image image to remove\n     * @param force if set to true remove containers as well (only the first vararg is evaluated)\n     * @return true if an image was removed, false if none was removed\n     * @throws DockerAccessException if an image cannot be removed\n     */\n    boolean removeImage(String image, boolean ... force) throws DockerAccessException;\n\n    /**\n     * Save an image to a tar file\n     *\n     * @param image image to save\n     * @param filename target filename\n     * @param compression compression to use for the archive\n     * @throws DockerAccessException if an image cannot be removed\n     */\n    void saveImage(String image, String filename, ArchiveCompression compression) throws DockerAccessException;\n\n    /**\n     * List all networks\n     *\n     * @return list of <code>Network<code> objects\n     * @throws DockerAccessException if the networks could not be listed\n     */\n    List<Network> listNetworks() throws DockerAccessException;\n\n    /**\n     * Create a custom network from the given configuration.\n     *\n     * @param configuration network configuration\n     * @throws DockerAccessException if the container could not be created.\n     */\n\n    String createNetwork(NetworkCreateConfig configuration) throws DockerAccessException;\n\n    /**\n     * Remove a custom network\n     *\n     * @param networkId network to remove\n     * @return true if an network was removed, false if none was removed\n     * @throws DockerAccessException if an image cannot be removed\n     */\n    boolean removeNetwork(String networkId) throws DockerAccessException;\n\n    /**\n     * Lifecycle method for this access class which must be called before any other method is called.\n     */\n    void start() throws DockerAccessException;\n\n    /**\n     * Lifecycle method which must be called when this object is not needed anymore. This hook might be used for\n     * cleaning up things.\n     */\n    void shutdown();\n\n   /**\n    *  Create a volume\n    *\n    *  @param configuration volume configuration\n    *  @return the name of the Volume\n    *  @throws DockerAccessException if the volume could not be created.\n    */\n   String createVolume(VolumeCreateConfig configuration) throws DockerAccessException;\n\n   /**\n    * Removes a volume. It is a no-op if the volume does not exist.\n    * @param name volume name to remove\n    * @throws DockerAccessException if the volume could not be removed\n    */\n   void removeVolume(String name) throws DockerAccessException;\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/DockerAccessException.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.IOException;\n\n/**\n * Exception thrown if access to the docker host fails\n *\n * @author roland\n * @since 20.10.14\n */\npublic class DockerAccessException extends IOException {\n\n    /**\n     * Constructor\n     *\n     * @param cause root cause\n     * @param message error message\n     */\n    public DockerAccessException(Throwable cause, String message) {\n        super(message, cause);\n    }\n\n    public DockerAccessException(String message) {\n        super(message);\n    }\n\n    public DockerAccessException(String format, Object...args) {\n        super(String.format(format, args));\n    }\n\n    public DockerAccessException(Throwable cause, String format, Object ... args) {\n        super(String.format(format, args),cause);\n    }\n\n    @Override\n    public String getMessage() {\n        if (getCause() != null) {\n            return String.format(\"%s : %s\", super.getMessage(), getCause().getMessage());\n        } else {\n            return super.getMessage();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/DockerConnectionDetector.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.*;\nimport java.util.*;\n\nimport io.fabric8.maven.docker.access.util.LocalSocketUtil;\nimport io.fabric8.maven.docker.util.*;\n\n/**\n * Detector for determining the Docker access mechanism\n */\npublic class DockerConnectionDetector {\n\n    final List<DockerHostProvider> dockerHostProviders;\n\n    public DockerConnectionDetector(List<DockerHostProvider> externalProviders) {\n        dockerHostProviders = new ArrayList<>();\n        dockerHostProviders.addAll(getDefaultEnvProviders());\n        if (externalProviders != null) {\n            dockerHostProviders.addAll(externalProviders);\n        }\n        Collections.sort(dockerHostProviders,new DockerHostProvider.Comparator());\n    }\n\n    private Collection<? extends DockerHostProvider> getDefaultEnvProviders() {\n        return Arrays.asList(new EnvDockerHostProvider(),\n                             new UnixSocketDockerHostProvider(),\n                             new WindowsPipeDockerHostProvider());\n    }\n\n\n    /**\n     * Provider of environment variables like 'DOCKER_HOST'\n     */\n    public interface DockerHostProvider {\n        /**\n         * Get value of the docker host as detected by this provider. Return null if it couldn't be detected.\n         *\n         * @return the docker host parameter or null\n         * @param certPath\n         */\n        ConnectionParameter getConnectionParameter(String certPath) throws IOException;\n\n        /**\n         * Get the priority of the env provider. A priority of -1 means, this is a 'fallback' called\n         * as last resort.\n         *\n         * @return priority, the higher, the earlier the provider is called.\n         * The highest priority of internal providers are not larger than 100.\n         */\n        int getPriority();\n\n        class Comparator implements java.util.Comparator<DockerHostProvider> {\n            @Override\n            public int compare(DockerHostProvider o1, DockerHostProvider o2) {\n                return o2.getPriority() - o1.getPriority();\n            }\n        }\n    }\n\n    /**\n     * Get the docker host url.\n     * <ol>\n     *   <li>From &lt;dockerHost&gt; configuration</li>\n     *   <li>From &lt;machine&gt; configuration</li>\n     *   <li>From DOCKER_HOST environment variable</li>\n     *   <li>Default to /var/run/docker.sock</li>\n     * </ol>\n     * @param dockerHost The dockerHost configuration setting\n     * @return The docker host url\n     * @throws IOException when URL handling fails\n     */\n    public ConnectionParameter detectConnectionParameter(String dockerHost, String certPath) throws IOException {\n        if (dockerHost != null) {\n            return new ConnectionParameter(dockerHost, certPath);\n        }\n        for (DockerHostProvider provider : dockerHostProviders) {\n            ConnectionParameter value = provider.getConnectionParameter(certPath);\n            if (value != null) {\n                return value;\n            }\n        }\n        throw new IllegalArgumentException(\"No <dockerHost> given, no DOCKER_HOST environment variable, \" +\n                                           \"no read/writable '/var/run/docker.sock' or '//./pipe/docker_engine' \" +\n                                           \"and no external provider like Docker machine configured\");\n    }\n\n    // ====================================================================================================\n\n    // Lookup from the enviroment\n    class EnvDockerHostProvider implements DockerHostProvider {\n        @Override\n        public ConnectionParameter getConnectionParameter(String certPath) throws IOException {\n            String connect = System.getenv(\"DOCKER_HOST\");\n            return connect != null ? new ConnectionParameter(connect, certPath) : null;\n        }\n\n        @Override\n        public int getPriority() {\n            return 100;\n        }\n    }\n\n    // Check for a unix socket\n    class UnixSocketDockerHostProvider implements DockerHostProvider {\n        @Override\n        public ConnectionParameter getConnectionParameter(String certPath) throws IOException {\n            File unixSocket = new File(\"/var/run/docker.sock\");\n            if (unixSocket.exists() && unixSocket.canRead() && unixSocket.canWrite() && LocalSocketUtil.canConnectUnixSocket(unixSocket)) {\n                return new ConnectionParameter(\"unix:///var/run/docker.sock\", certPath);\n            } else {\n                return null;\n            }\n        }\n\n        @Override\n        public int getPriority() {\n            return 55;\n        }\n    }\n\n    // Check for a windows pipe\n    class WindowsPipeDockerHostProvider implements DockerHostProvider {\n        @Override\n        public ConnectionParameter getConnectionParameter(String certPath) throws IOException {\n            File windowsPipe = new File(\"//./pipe/docker_engine\");\n            if (windowsPipe.exists()) {\n                return new ConnectionParameter(\"npipe:////./pipe/docker_engine\", certPath);\n            } else {\n                return null;\n            }\n        }\n\n        @Override\n        public int getPriority() {\n            return 50;\n        }\n    }\n\n    public static class ConnectionParameter {\n        private final String url;\n        private String certPath;\n\n        public ConnectionParameter(String url, String certPath) throws IOException {\n            this.url = url != null ? EnvUtil.convertTcpToHttpUrl(url) : null;\n            initCertPath(certPath);\n        }\n\n        public String getUrl() {\n            return url;\n        }\n\n        public String getCertPath() {\n            return certPath;\n        }\n\n        /**\n         * Get the docker certificate location\n         * <ol>\n         *   <li>From &lt;certPath&gt; a given cert config argument</li>\n         *   <li>From DOCKER_CERT_PATH environment variable</li>\n         *   <li>Default to ${user.home}/.docker</li>\n         * </ol>\n         * @param certPath the configured certification path which is used directly if set\n         */\n        private void initCertPath(String certPath) throws IOException {\n            this.certPath = certPath != null ? certPath : System.getenv(\"DOCKER_CERT_PATH\");\n            // Try default locations as last resort\n            if (this.certPath == null) {\n                File dockerHome = new File(System.getProperty(\"user.home\") + \"/.docker\");\n                if (dockerHome.isDirectory()) {\n                    String[] entries = dockerHome.list(SuffixFileFilter.PEM_FILTER);\n                    if (entries == null) {\n                        throw new IOException(\"Can not read directory \" + dockerHome + \". Please check file permissions.\");\n                    }\n                    if (entries.length > 0) {\n                        this.certPath = dockerHome.getAbsolutePath();\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/DockerMachine.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.*;\nimport java.util.*;\n\nimport io.fabric8.maven.docker.access.util.EnvCommand;\nimport io.fabric8.maven.docker.access.util.ExternalCommand;\nimport io.fabric8.maven.docker.config.DockerMachineConfiguration;\nimport io.fabric8.maven.docker.util.Logger;\n\n/**\n * launch docker-machine to obtain environment settings\n */\npublic class DockerMachine implements DockerConnectionDetector.DockerHostProvider {\n\n    private final Logger log;\n    private final DockerMachineConfiguration machine;\n    private boolean initialized = false;\n    private Map<String, String> envMap;\n\n    public DockerMachine(Logger log, DockerMachineConfiguration machine) {\n        this.log = log;\n        this.machine = machine;\n    }\n\n    enum Status {\n        DoesNotExist, Running, Stopped\n    }\n\n    public synchronized DockerConnectionDetector.ConnectionParameter getConnectionParameter(String certPath) throws IOException {\n        if (machine == null) {\n            return null;\n        }\n        if (envMap == null) {\n            envMap = getEnvironment();\n        }\n        String value = envMap.get(\"DOCKER_HOST\");\n        if (value == null) {\n            return null;\n        }\n        log.info(\"DOCKER_HOST from docker-machine \\\"%s\\\" : %s\", machine.getName(), value);\n        return new DockerConnectionDetector.ConnectionParameter(value, certPath != null ? certPath : envMap.get(\"DOCKER_CERT_PATH\"));\n    }\n\n    @Override\n    public int getPriority() {\n        // Just after environment variable priority-wise\n        return 90;\n    }\n\n    private Map<String, String> getEnvironment() throws IOException {\n        lazyInit();\n        return new MachineEnvCommand().getEnvironment();\n    }\n\n    private synchronized void lazyInit() throws IOException {\n        if (!initialized) {\n            Status status = new StatusCommand().getStatus();\n            switch (status) {\n                case DoesNotExist:\n                    if (Boolean.TRUE == machine.getAutoCreate()) {\n                        new CreateCommand().execute();\n                    } else {\n                        throw new IllegalStateException(machine.getName() + \" does not exist and docker.machine.autoCreate is false\");\n                    }\n                    break;\n                case Running:\n                    break;\n                case Stopped:\n                    new StartCommand().execute();\n                    if (Boolean.TRUE == machine.getRegenerateCertsAfterStart()) {\n                        new RegenerateCertsCommand().execute();\n                    }\n                    break;\n            }\n        }\n        initialized = true;\n    }\n\n    // docker-machine env <name>\n    private class MachineEnvCommand extends EnvCommand {\n\n        MachineEnvCommand() {\n            super(DockerMachine.this.log, \"SET \");\n        }\n\n        @Override\n        protected String[] getArgs() {\n            // use windows style with \"SET \"\n            return new String[]{\"docker-machine\", \"env\", machine.getName(), \"--shell\", \"cmd\"};\n        }\n    }\n\n    // docker-machine status <name>\n    private class StatusCommand extends ExternalCommand {\n\n        private Status status;\n        private String message;\n\n        StatusCommand() {\n            super(DockerMachine.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            return new String[]{\"docker-machine\", \"status\", machine.getName()};\n        }\n\n        @Override\n        protected void processLine(String line) {\n            log.info(\"Docker machine \\\"%s\\\" is %s\",machine.getName(),line.toLowerCase());\n            if (\"Running\".equals(line)) {\n                status = Status.Running;\n            } else if (\"Stopped\".equals(line)) {\n                status = Status.Stopped;\n            } else {\n                message = \"Unknown status - \" + line;\n            }\n        }\n\n        public Status getStatus() throws IOException {\n            try {\n                execute();\n            } catch (IOException ex) {\n                if (getStatusCode() == 1) {\n                    status = Status.DoesNotExist;\n                } else {\n                    throw ex;\n                }\n            }\n            if (message != null) {\n                throw new IOException(message);\n            }\n            return status;\n        }\n    }\n\n    // docker-machine create --driver virtualbox <name>\n    private class CreateCommand extends ExternalCommand {\n\n        private long start;\n\n        CreateCommand() {\n            super(DockerMachine.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            List<String> args = new ArrayList<>();\n            args.add(\"docker-machine\");\n            args.add(\"create\");\n            if (machine.getCreateOptions() != null) {\n                for (Map.Entry<String, String> entry : machine.getCreateOptions().entrySet()) {\n                    args.add(\"--\" + entry.getKey());\n                    String value = entry.getValue();\n                    if (value != null && !value.isEmpty()) {\n                        args.add(value);\n                    }\n                }\n            }\n            args.add(machine.getName());\n            return args.toArray(new String[0]);\n        }\n\n        @Override\n        protected void start() {\n            log.info(\"Creating docker machine \\\"%s\\\" with args %s\",\n                     machine.getName(),\n                     machine.getCreateOptions() != null ? machine.getCreateOptions().toString() : \"\");\n            log.info(\"This might take a while ...\");\n            start = System.currentTimeMillis();\n        }\n\n        @Override\n        protected void end() {\n            log.info(\"Created docker machine \\\"%s\\\" in %d seconds\",machine.getName(), (System.currentTimeMillis() - start) / 1000);\n        }\n    }\n\n    // docker-machine start <name>\n    private class StartCommand extends ExternalCommand {\n\n        private long start;\n\n        StartCommand() {\n            super(DockerMachine.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            return new String[]{\"docker-machine\", \"start\", machine.getName()};\n        }\n\n        @Override\n        protected void start() {\n            log.info(\"Starting docker machine \\\"%s\\\"\", machine.getName());\n            start = System.currentTimeMillis();\n        }\n\n        @Override\n        protected void end() {\n            log.info(\"Started docker machine \\\"%s\\\" in %d seconds\",machine.getName(), (System.currentTimeMillis() - start) / 1000);\n        }\n    }\n\n    // docker-machine regenerate-certs <name>\n    private class RegenerateCertsCommand extends ExternalCommand {\n\n        private long start;\n\n        RegenerateCertsCommand() {\n            super(DockerMachine.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            return new String[]{\"docker-machine\", \"regenerate-certs\", \"-f\", machine.getName()};\n        }\n\n        @Override\n        protected void start() {\n            log.info(\"Regenerating certificates for \\\"%s\\\"\", machine.getName());\n            start = System.currentTimeMillis();\n        }\n\n        @Override\n        protected void end() {\n            log.info(\"Regenerated certificates for \\\"%s\\\" in %d seconds\",machine.getName(), (System.currentTimeMillis() - start) / 1000);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ExecException.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.util.Arrays;\n\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.model.ExecDetails;\n\n/**\n * Exception thrown when the execution of an exec container failed\n *\n * @author roland\n * @since 18.01.18\n */\npublic class ExecException extends Exception {\n    public ExecException(ExecDetails details, ContainerDetails container) {\n        super(String.format(\n                \"Executing '%s' with args '%s' inside container '%s' [%s](%s) resulted in a non-zero exit code: %d\",\n                details.getEntryPoint(),\n                Arrays.toString(details.getArguments()),\n                container.getName(),\n                container.getImage(),\n                container.getId(),\n                details.getExitCode()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/KeyStoreUtil.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.*;\nimport java.security.*;\nimport java.security.cert.Certificate;\nimport java.security.cert.*;\nimport java.util.Collection;\n\nimport org.bouncycastle.asn1.pkcs.PrivateKeyInfo;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.openssl.PEMKeyPair;\nimport org.bouncycastle.openssl.PEMParser;\nimport org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;\n\n/**\n * Utility class for building up a keystore which can be used in\n * SSL communication.\n *\n * @author roland\n * @since 20.10.14\n */\npublic class KeyStoreUtil {\n\n    static {\n        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {\n            Security.addProvider(new BouncyCastleProvider());\n        }\n    }\n\n    /**\n     * Create a key stored holding certificates and secret keys from the given Docker key cert\n     *\n     * @param certPath directory holding the keys (key.pem) and certs (ca.pem, cert.pem)\n     * @return a keystore where the private key is secured with \"docker\"\n     *\n     * @throws IOException is reading of the the PEMs failed\n     * @throws GeneralSecurityException when the files in a wrong format\n     */\n    public static KeyStore createDockerKeyStore(String certPath) throws IOException, GeneralSecurityException {\n        PrivateKey privKey = loadPrivateKey(certPath + \"/key.pem\");\n        Certificate[] certs = loadCertificates(certPath + \"/cert.pem\");\n\n        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());\n        keyStore.load(null);\n\n        keyStore.setKeyEntry(\"docker\", privKey, \"docker\".toCharArray(), certs);\n        addCA(keyStore, certPath + \"/ca.pem\");\n        return keyStore;\n    }\n\n    static PrivateKey loadPrivateKey(String keyPath) throws IOException, GeneralSecurityException {\n        try (Reader reader = new FileReader(keyPath);\n            PEMParser parser = new PEMParser(reader)) {\n            Object readObject;\n            while ((readObject = parser.readObject()) != null) {\n                if (readObject instanceof PEMKeyPair) {\n                    PEMKeyPair keyPair = (PEMKeyPair) readObject;\n                    return generatePrivateKey(keyPair.getPrivateKeyInfo());\n                } else if (readObject instanceof PrivateKeyInfo) {\n                    return generatePrivateKey((PrivateKeyInfo) readObject);\n                }\n            }\n        }\n        throw new GeneralSecurityException(\"Cannot generate private key from file: \" + keyPath);\n    }\n\n    private static PrivateKey generatePrivateKey(PrivateKeyInfo keyInfo) throws IOException {\n        return new JcaPEMKeyConverter().getPrivateKey(keyInfo);\n    }\n\n    private static void addCA(KeyStore keyStore, String caPath) throws IOException, KeyStoreException,\n            CertificateException {\n        for (Certificate cert : loadCertificates(caPath)) {\n            X509Certificate crt = (X509Certificate) cert;\n            String alias = crt.getSubjectX500Principal().getName();\n            keyStore.setCertificateEntry(alias, crt);\n        }\n    }\n\n    private static Certificate[] loadCertificates(String certPath) throws IOException, CertificateException {\n        try (InputStream is = new FileInputStream(certPath)) {\n            CertificateFactory certificateFactory = CertificateFactory.getInstance(\"X509\");\n            Collection<? extends Certificate> certs = certificateFactory.generateCertificates(is);\n            return certs.toArray(new Certificate[certs.size()]);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/NetworkCreateConfig.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonObject;\n\npublic class NetworkCreateConfig {\n    final JsonObject createConfig = new JsonObject();\n    final String name;\n\n    public NetworkCreateConfig(String name) {\n        this.name = name;\n        createConfig.addProperty(\"Name\", name);\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * Get JSON which is used for <em>creating</em> a network\n     *\n     * @return string representation for JSON representing creating a network\n     */\n    public String toJson() {\n        return createConfig.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/PortMapping.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.util.EnvUtil;\n\n/**\n * Entity holding port mappings which can be set through the configuration.\n *\n * @author roland\n * @since 04.04.14\n */\npublic class PortMapping {\n\n    // Pattern for splitting of the protocol\n    private static final Pattern PROTOCOL_SPLIT_PATTERN = Pattern.compile(\"(.*?)(?:/(tcp|udp))?$\");\n\n    // Mapping between ports and the IP they should bind to\n    private final Map<String, String> bindToHostMap = new HashMap<>();\n\n    // ports map (container port -> host port)\n    private final Map<String, Integer> containerPortToHostPort = new HashMap<>();\n\n    // resolved dynamic properties\n    private final Properties dynamicProperties = new Properties();\n\n    // Mapping between property name and host ip (ip filled in after container creation)\n    private final Map<String, String> hostIpVariableMap = new HashMap<>();\n\n    // Mapping between property name and host port (port filled in after container creation)\n    private final Map<String, Integer> hostPortVariableMap = new HashMap<>();\n\n    // project properties\n    private final Properties projProperties;\n\n    // variables (container port spec -> host ip variable name)\n    private final Map<String, String> specToHostIpVariableMap = new HashMap<>();\n\n    // variables (container port spec -> host port variable name)\n    private final Map<String, String> specToHostPortVariableMap = new HashMap<>();\n\n    /**\n     * Create the mapping from a configuration. The configuration is list of port mapping specifications which has the\n     * format used by docker for port mapping (i.e. host_ip:host_port:container_port)\n     * <ul>\n     * <li>The \"host_ip\" part is optional. If not given, the all interfaces are used</li>\n     * <li>If \"host_port\" is non numeric it is taken as a variable name. If this variable is given as value in\n     * variables, this number is used as host port. If no numeric value is given, it is considered to be filled with the\n     * real, dynamically created port value when {@link #updateProperties(Map)} is called</li>\n     * </ul>\n     *\n     * @param portMappings a list of configuration strings where each string hast the format\n     *            <code>host_ip:host_port:container_port</code>. If the <code>host-port</code> is non-numeric it is\n     *            assumed to be a variable (which later might be filled in with the dynamically created port).\n     * @param projProperties project properties\n     * @throws IllegalArgumentException if the format doesn't fit\n     */\n    public PortMapping(List<String> portMappings, Properties projProperties) {\n        this.projProperties = projProperties;\n\n        for (String portMapping : portMappings) {\n            parsePortMapping(portMapping);\n        }\n    }\n\n    /**\n     * Check whether property needs updates for dynamically obtained host ports and ip adresses.\n     *\n     * @return true if any property are used which need to be filled in, false otherwise\n     */\n    public boolean needsPropertiesUpdate() {\n        return !specToHostPortVariableMap.isEmpty() || !specToHostIpVariableMap.isEmpty();\n    }\n\n    /**\n     * @return Set of all mapped container ports\n     */\n    public Set<String> getContainerPorts() {\n        return containerPortToHostPort.keySet();\n    }\n\n    /**\n     * Update variable-to-port mappings with dynamically obtained ports and host ips.\n     * This should only be called once after this dynamically allocated parts has been be obtained.\n     *\n     * @param dockerObtainedDynamicBindings keys are the container ports, values are the dynamically mapped host ports and host ips.\n     */\n    public void updateProperties(Map<String, Container.PortBinding> dockerObtainedDynamicBindings) {\n        for (Map.Entry<String, Container.PortBinding> entry : dockerObtainedDynamicBindings.entrySet()) {\n            String variable = entry.getKey();\n            Container.PortBinding portBinding = entry.getValue();\n\n            if (portBinding != null) {\n                update(hostPortVariableMap, specToHostPortVariableMap.get(variable), portBinding.getHostPort());\n\n                String hostIp = portBinding.getHostIp();\n\n                // Use the docker host if binding is on all interfaces\n                if (\"0.0.0.0\".equals(hostIp)) {\n                    hostIp = projProperties.getProperty(\"docker.host.address\");\n                }\n\n                update(hostIpVariableMap, specToHostIpVariableMap.get(variable), hostIp);\n            }\n        }\n\n        updateDynamicProperties(hostPortVariableMap);\n        updateDynamicProperties(hostIpVariableMap);\n    }\n\n    /**\n     * Create a JSON specification which can be used to in a Docker API request as the 'PortBindings' part\n     * for creating container.\n     *\n     * @return 'PortBindings' object or null if no port mappings are used.\n     */\n    JsonObject toDockerPortBindingsJson() {\n        Map<String, Integer> portMap = getContainerPortToHostPortMap();\n        if (!portMap.isEmpty()) {\n            JsonObject portBindings = new JsonObject();\n            Map<String, String> bindToMap = getBindToHostMap();\n\n            for (Map.Entry<String, Integer> entry : portMap.entrySet()) {\n                String containerPortSpec = entry.getKey();\n                Integer hostPort = entry.getValue();\n\n                JsonObject o = new JsonObject();\n                o.addProperty(\"HostPort\", hostPort != null ? hostPort.toString() : \"\");\n\n                if (bindToMap.containsKey(containerPortSpec)) {\n                    o.addProperty(\"HostIp\", bindToMap.get(containerPortSpec));\n                }\n\n                JsonArray array = new JsonArray();\n                array.add(o);\n\n                portBindings.add(containerPortSpec, array);\n            }\n            return portBindings;\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Return the content of the mapping as an array with all specifications as given\n     *\n     * @return port mappings as JSON array or null if no mappings exist\n     */\n    public JsonArray toJson() {\n        Map<String, Integer> portMap = getContainerPortToHostPortMap();\n        if (portMap.isEmpty()) {\n            return null;\n        }\n\n        JsonArray ret = new JsonArray();\n        Map<String, String> bindToMap = getBindToHostMap();\n\n        for (Map.Entry<String, Integer> entry : portMap.entrySet()) {\n            JsonObject mapping = new JsonObject();\n            String containerPortSpec = entry.getKey();\n            Matcher matcher = PROTOCOL_SPLIT_PATTERN.matcher(entry.getKey());\n            if (!matcher.matches()) {\n                throw new IllegalStateException(\"Internal error: \" + entry.getKey() +\n                                                \" doesn't contain protocol part and doesn't match \"\n                                                + PROTOCOL_SPLIT_PATTERN);\n            }\n\n            mapping.addProperty(\"containerPort\", Integer.parseInt(matcher.group(1)));\n            if (matcher.group(2) != null) {\n                mapping.addProperty(\"protocol\", matcher.group(2));\n            }\n\n            Integer hostPort = entry.getValue();\n            if (hostPort != null) {\n                mapping.addProperty(\"hostPort\", hostPort);\n            }\n\n            if (bindToMap.containsKey(containerPortSpec)) {\n                mapping.addProperty(\"hostIP\", bindToMap.get(containerPortSpec));\n            }\n\n            ret.add(mapping);\n        }\n\n        return ret;\n    }\n\n    // ==========================================================================================================\n\n    // visible for testing\n    Map<String, String> getBindToHostMap() {\n        return bindToHostMap;\n    }\n\n    // visible for testing\n    Map<String, Integer> getContainerPortToHostPortMap() {\n        return containerPortToHostPort;\n    }\n\n    // visible for testing\n    Map<String, String> getHostIpVariableMap() {\n        return hostIpVariableMap;\n    }\n\n    // visible for testing\n    Map<String, Integer> getHostPortVariableMap() {\n        return hostPortVariableMap;\n    }\n\n    // visible for testing\n    Map<String, Integer> getPortsMap() {\n        return containerPortToHostPort;\n    }\n\n    private IllegalArgumentException createInvalidMappingError(String mapping, Exception exp) {\n        return new IllegalArgumentException(\"\\nInvalid port mapping '\" + mapping + \"'\\n\" +\n                \"Required format: '<hostIP>:<hostPort>:<containerPort>(/tcp|udp)'\\n\" +\n                \"See the reference manual for more details\");\n    }\n\n    private void createMapping(String[] parts, String protocol) {\n        if (parts.length == 3) {\n            mapBindToAndHostPortSpec(parts[0], parts[1], createPortSpec(parts[2], protocol));\n        } else if (parts.length == 2) {\n            mapHostPortToSpec(parts[0], createPortSpec(parts[1], protocol));\n        } else {\n            mapHostPortToSpec(null, createPortSpec(parts[0], protocol));\n        }\n    }\n\n    private String createPortSpec(String port, String protocol) throws NumberFormatException {\n        return Integer.parseInt(port) + \"/\" + protocol;\n    }\n\n    private Integer getAsIntOrNull(String val) {\n        try {\n            return Integer.parseInt(val);\n        } catch (@SuppressWarnings(\"unused\") NumberFormatException exp) {\n            return null;\n        }\n    }\n\n    // Check for a variable containing a port, return it as integer or <code>null</code> is not found or not a number\n    // First check system properties, then the variables given\n    private Integer getPortFromProjectOrSystemProperty(String var) {\n        String sysProp = System.getProperty(var);\n        if (sysProp != null) {\n            return getAsIntOrNull(sysProp);\n        }\n        if (projProperties.containsKey(var)) {\n            return getAsIntOrNull(projProperties.getProperty(var));\n        }\n        return null;\n    }\n\n    private String extractPortPropertyName(String name) {\n        String mavenPropName = EnvUtil.extractMavenPropertyName(name);\n        return mavenPropName != null ? mavenPropName : name;\n    }\n\n    private void mapBindToAndHostPortSpec(String bindTo, String hPort, String portSpec) {\n        mapHostPortToSpec(hPort, portSpec);\n\n        String hostPropName = extractHostPropertyName(bindTo);\n        if (hostPropName != null) {\n            String host = projProperties.getProperty(hostPropName);\n            if (host != null) {\n                // the container portSpec can never be null, so use that as the key\n                bindToHostMap.put(portSpec, resolveHostname(host));\n            }\n\n            specToHostIpVariableMap.put(portSpec, hostPropName);\n        } else {\n            // the container portSpec can never be null, so use that as the key\n            bindToHostMap.put(portSpec, resolveHostname(bindTo));\n        }\n    }\n\n    private String extractHostPropertyName(String name) {\n        if (name.startsWith(\"+\")) {\n            return name.substring(1);\n        } else {\n            return EnvUtil.extractMavenPropertyName(name);\n        }\n    }\n\n    private void mapHostPortToSpec(String hPort, String portSpec) {\n        Integer hostPort;\n        if (hPort == null) {\n            hostPort = null;\n        } else {\n            try {\n                hostPort = Integer.parseInt(hPort);\n            } catch (@SuppressWarnings(\"unused\") NumberFormatException exp) {\n                // Port should be dynamically assigned and set to the variable give in hPort\n                String portPropertyName = extractPortPropertyName(hPort);\n\n                hostPort = getPortFromProjectOrSystemProperty(portPropertyName);\n                if (hostPort != null) {\n                    // portPropertyName: Prop name, hostPort: Port from a property value (prefilled)\n                    hostPortVariableMap.put(portPropertyName, hostPort);\n                } else {\n                    // portSpec: Port from container, portPropertyName: Variable name to be filled in later\n                    specToHostPortVariableMap.put(portSpec, portPropertyName);\n                }\n            }\n        }\n        containerPortToHostPort.put(portSpec, hostPort);\n    }\n\n    private void parsePortMapping(String input) throws IllegalArgumentException {\n        try {\n            Matcher matcher = PROTOCOL_SPLIT_PATTERN.matcher(input);\n            // Matches always\n            matcher.matches();\n            String mapping = matcher.group(1);\n            String protocol = matcher.group(2);\n            if (protocol == null) {\n                protocol = \"tcp\";\n            }\n\n            createMapping(mapping.split(\":\", 3), protocol);\n        } catch (NullPointerException | NumberFormatException exp) {\n            throw createInvalidMappingError(input, exp);\n        }\n    }\n\n    private String resolveHostname(String bindToHost) {\n        try {\n            return InetAddress.getByName(bindToHost).getHostAddress();\n        } catch (@SuppressWarnings(\"unused\") UnknownHostException e) {\n            throw new IllegalArgumentException(\"Host '\" + bindToHost + \"' to bind to cannot be resolved\");\n        }\n    }\n\n    private <T> void update(Map<String, T> map, String key, T value) {\n        if (key != null) {\n            map.put(key, value);\n        }\n    }\n\n    private void updateDynamicProperties(Map<String, ?> dynamicPorts) {\n        for (Map.Entry<String, ?> entry : dynamicPorts.entrySet()) {\n            String var = entry.getKey();\n            String val = entry.getValue().toString();\n\n            projProperties.setProperty(var, val);\n            dynamicProperties.setProperty(var, val);\n        }\n    }\n\n\n    public static class PropertyWriteHelper {\n\n        private final Properties globalExport;\n\n        private final String globalFile;\n        private final Map<String, Properties> toExport;\n\n        public PropertyWriteHelper(String globalFile) {\n            this.globalFile = globalFile;\n\n            this.toExport = new HashMap<>();\n            this.globalExport = new Properties();\n        }\n\n        public void add(PortMapping portMapping, String portPropertyFile) {\n            if (portPropertyFile != null) {\n                toExport.put(portPropertyFile, portMapping.dynamicProperties);\n            } else if (globalFile != null) {\n                globalExport.putAll(portMapping.dynamicProperties);\n            }\n        }\n\n        public void write() throws IOException {\n            for (Map.Entry<String, Properties> entry : toExport.entrySet()) {\n                Properties props = entry.getValue();\n                writeProperties(props, entry.getKey());\n                globalExport.putAll(props);\n            }\n\n            if (globalFile != null && !globalExport.isEmpty()) {\n                writeProperties(globalExport, globalFile);\n            }\n        }\n\n        private void writeProperties(Properties props, String file) throws IOException {\n            File propFile = new File(file);\n            try (OutputStream os = new FileOutputStream(propFile)) {\n                props.store(os, \"Docker ports\");\n            } catch (IOException e) {\n                throw new IOException(\"Cannot write properties to \" + file + \": \" + e, e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.TreeSet;\n\nimport io.fabric8.maven.docker.util.ImageName;\n\npublic final class UrlBuilder {\n\n    private final String apiVersion;\n\n    // Base Docker URL\n    private final String baseUrl;\n\n    public UrlBuilder(String baseUrl, String apiVersion) {\n        this.apiVersion = apiVersion;\n        this.baseUrl = stripSlash(baseUrl);\n    }\n\n    public String buildImage(String image, BuildOptions options) {\n        Builder urlBuilder = u(\"build\")\n            .p(\"t\", image);\n        if (options != null) {\n            urlBuilder.p(options.getOptions());\n        }\n        return urlBuilder.build();\n    }\n\n    public String copyArchive(String containerId, String targetPath) {\n        return u(\"containers/%s/archive\",containerId)\n                .p(\"path\",targetPath)\n                .build();\n    }\n\n    public String inspectImage(String name) {\n        return u(\"images/%s/json\", name)\n                .build();\n    }\n\n    public String listImages(boolean all) {\n        return u(\"images/json\")\n                .p(\"all\", all)\n                .build();\n    }\n\n    public String containerLogs(String containerId, boolean follow) {\n        return u(\"containers/%s/logs\", containerId)\n                .p(\"stdout\",true)\n                .p(\"stderr\",true)\n                .p(\"timestamps\", true)\n                .p(\"follow\", follow)\n                .build();\n    }\n\n    public String createContainer(String name) {\n        return u(\"containers/create\")\n                .p(\"name\", name)\n                .build();\n    }\n\n    public String version() {\n        return String.format(\"%s/version\", baseUrl);\n    }\n\n    public String deleteImage(String name, boolean force) {\n        return u(\"images/%s\", name)\n                .p(\"force\", force)\n                .build();\n    }\n\n    public String getImage(ImageName name) {\n        return u(\"images/%s/get\", name.getFullName())\n            .build();\n    }\n\n    public String inspectContainer(String containerId) {\n        return u(\"containers/%s/json\", containerId)\n                .build();\n    }\n\n    public String inspectExecContainer(String containerId) {\n        return u(\"exec/%s/json\", containerId)\n                .build();\n    }\n\n    public String listContainers(boolean all, String ... filter) {\n        Builder builder = u(\"containers/json\").p(\"all\", all);\n        addFilters(builder, filter);\n        return builder.build();\n    }\n\n    public String loadImage() {\n        return u(\"images/load\")\n            .build();\n    }\n\n    public String pullImage(ImageName name, String registry) {\n        return u(\"images/create\")\n                .p(\"fromImage\", name.getNameWithoutTag(registry))\n                .p(\"tag\", name.getDigest() != null ? name.getDigest() : name.getTag())\n                .build();\n    }\n\n    public String pushImage(ImageName name, String registry) {\n        return u(\"images/%s/push\", name.getNameWithoutTag(registry))\n                .p(\"tag\", name.getTag())\n                // \"force=1\" helps Fedora/CentOs Docker variants to push to public registries\n                .p(\"force\", true)\n                .build();\n    }\n\n    public String removeContainer(String containerId, boolean removeVolumes) {\n        return u(\"containers/%s\", containerId)\n                .p(\"v\", removeVolumes)\n                .build();\n    }\n\n    public String startContainer(String containerId) {\n        return u(\"containers/%s/start\", containerId)\n                .build();\n    }\n\n    public String createExecContainer(String containerId) {\n        return u(\"containers/%s/exec\", containerId)\n                .build();\n    }\n\n    public String startExecContainer(String containerId) {\n        return u(\"exec/%s/start\", containerId)\n                .build();\n    }\n\n    public String stopContainer(String containerId, int killWait) {\n        Builder b = u(\"containers/%s/stop\", containerId);\n        if (killWait > 0) {\n            b.p(\"t\", killWait);\n        }\n        return b.build();\n    }\n\n    public String killContainer(String containerId) {\n        Builder b = u(\"containers/%s/kill\", containerId);\n        return b.build();\n    }\n\n    public String tagContainer(ImageName source, ImageName target, boolean force) {\n        return u(\"images/%s/tag\", source.getFullName())\n                .p(\"repo\",target.getNameWithoutTag())\n                .p(\"tag\",target.getTag())\n                .p(\"force\",force)\n                .build();\n    }\n\n    public String listNetworks() {\n        return u(\"networks\")\n                .build();\n    }\n\n    public String createNetwork() {\n        return u(\"networks/create\")\n                .build();\n    }\n\n    public String removeNetwork(String id) {\n        return u(\"networks/%s\", id)\n                .build();\n    }\n\n    public String createVolume() {\n       return u(\"volumes/create\").build();\n    }\n\n    public String removeVolume(String name) {\n       return u(\"volumes/%s\", name).build();\n    }\n\n    public String getBaseUrl() {\n        return baseUrl;\n    }\n\n    // ============================================================================\n\n    @SuppressWarnings(\"deprecation\")\n    private static String encode(String param) {\n        try {\n            return URLEncoder.encode(param, \"UTF-8\");\n        }\n        catch (@SuppressWarnings(\"unused\") UnsupportedEncodingException e) {\n            // wont happen\n            return URLEncoder.encode(param);\n        }\n    }\n\n    private String stripSlash(String url) {\n        String ret = url;\n        while (ret.endsWith(\"/\")) {\n            ret = ret.substring(0, ret.length() - 1);\n        }\n        return ret;\n    }\n\n    // Entry point for builder\n    private Builder u(String format, String ... args) {\n        return new Builder(createUrl(String.format(format, (Object[]) encodeArgs(args))));\n    }\n\n    private String[] encodeArgs(String[] args) {\n        String ret[] = new String[args.length];\n        int i=0;\n        for (String arg : args) {\n            ret[i++] = encode(arg);\n        }\n        return ret;\n    }\n\n    private String createUrl(String path) {\n        return String.format(\"%s/%s/%s\", baseUrl, apiVersion, path);\n    }\n\n    private void addFilters(Builder builder, String... filter) {\n       if (filter.length > 0) {\n           if (filter.length % 2 != 0) {\n               throw new IllegalArgumentException(\"Filters must be given as key value pairs and not \" + Arrays.asList(filter));\n           }\n           JsonObject filters = new JsonObject();\n           for (int i = 0; i < filter.length; i +=2) {\n               JsonArray value = new JsonArray();\n               value.add(filter[i+1]);\n               filters.add(filter[i],value);\n           }\n           builder.p(\"filters\",filters.toString());\n       }\n    }\n\n    private static class Builder {\n\n        private Map<String,String> queryParams = new HashMap<>();\n        private String url;\n\n        public Builder(String url) {\n            this.url = url;\n        }\n\n        private Builder p(Map<String, String> params) {\n            queryParams.putAll(params);\n            return this;\n        }\n\n        private Builder p(String key, String value) {\n             if (value != null) {\n                queryParams.put(key, value);\n            }\n            return this;\n        }\n\n        private Builder p(String key, boolean value) {\n            return p(key,value ? \"1\" : \"0\");\n        }\n\n        private Builder p(String key, int value) {\n            return p(key,Integer.toString(value));\n        }\n\n        public String build() {\n            if (queryParams.size() > 0) {\n                StringBuilder ret = new StringBuilder(url);\n                ret.append(\"?\");\n                // Sort to make order predictable e.g. for unit testing\n                for (String key : new TreeSet<>(queryParams.keySet())) {\n                    ret.append(key)\n                       .append(\"=\")\n                       .append(encode(queryParams.get(key)))\n                       .append(\"&\");\n                }\n                return ret.substring(0,ret.length() - 1);\n            } else {\n                return url;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/VolumeCreateConfig.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonObject;\n\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.util.JsonFactory;\n\npublic class VolumeCreateConfig\n{\n    private final JsonObject createConfig = new JsonObject();\n\n    public VolumeCreateConfig(String name) {\n        add(\"Name\", name);\n    }\n\n    public VolumeCreateConfig driver(String driver) {\n       return add(\"Driver\", driver);\n    }\n\n    public VolumeCreateConfig opts(Map<String, String> opts) {\n       if (opts != null && opts.size() > 0) {\n          add(\"DriverOpts\", JsonFactory.newJsonObject(opts));\n       }\n       return this;\n    }\n\n    public VolumeCreateConfig labels(Map<String,String> labels) {\n        if (labels != null && labels.size() > 0) {\n           add(\"Labels\", JsonFactory.newJsonObject(labels));\n        }\n        return this;\n    }\n\n    public String getName() {\n        return createConfig.get(\"Name\").getAsString();\n    }\n\n    /**\n     * Get JSON which is used for <em>creating</em> a volume\n     *\n     * @return string representation for JSON representing creating a volume\n     */\n    public String toJson() {\n        return createConfig.toString();\n    }\n\n    // =======================================================================\n\n    private VolumeCreateConfig add(String name, JsonObject value) {\n        if (value != null) {\n            createConfig.add(name, value);\n        }\n        return this;\n    }\n\n    private VolumeCreateConfig add(String name, String value) {\n        if (value != null) {\n            createConfig.addProperty(name, value);\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/chunked/BuildJsonResponseHandler.java",
    "content": "package io.fabric8.maven.docker.access.chunked;\n\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.util.Logger;\n\npublic class BuildJsonResponseHandler implements EntityStreamReaderUtil.JsonEntityResponseHandler {\n\n    private final Logger log;\n\n    public BuildJsonResponseHandler(Logger log) {\n        this.log = log;\n    }\n    \n    @Override\n    public void process(JsonObject json) throws DockerAccessException {\n        if (json.has(\"error\")) {\n            String msg = json.get(\"error\").getAsString();\n            String detailMsg = \"\";\n            if (json.has(\"errorDetail\")) {\n                JsonObject details = json.getAsJsonObject(\"errorDetail\");\n                detailMsg = details.get(\"message\").getAsString();\n            }\n            throw new DockerAccessException(\"%s %s\", json.get(\"error\"),\n                    (msg.equals(detailMsg) || \"\".equals(detailMsg) ? \"\" : \"(\" + detailMsg + \")\"));\n        } else if (json.has(\"stream\")) {\n            String message = json.get(\"stream\").getAsString();\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"%s\", message.trim());\n        } else if (json.has(\"status\")) {\n            String status = json.get(\"status\").getAsString().trim();\n            String id = json.has(\"id\") ? json.get(\"id\").getAsString() : null;\n            if (status.matches(\"^.*(Download|Pulling).*\")) {\n                log.info(\"  %s%s\",id != null ? id + \" \" : \"\",status);\n            }\n        }\n    }\n\n    // Lifecycle methods not needed ...\n    @Override\n    public void start() {}\n\n    @Override\n    public void stop() {}\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/chunked/EntityStreamReaderUtil.java",
    "content": "package io.fabric8.maven.docker.access.chunked;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\n\npublic class EntityStreamReaderUtil {\n\n    private EntityStreamReaderUtil() {}\n\n    public static void processJsonStream(JsonEntityResponseHandler handler, InputStream stream) throws IOException {\n        handler.start();\n        try(JsonReader json = new JsonReader(new InputStreamReader(stream))) {\n            JsonParser parser = new JsonParser();\n\n            json.setLenient(true);\n            while (json.peek() != JsonToken.END_DOCUMENT) {\n                JsonElement element = parser.parse(json);\n                handler.process(element.getAsJsonObject());\n            }\n        } finally {\n            handler.stop();\n        }\n    }\n\n    public interface JsonEntityResponseHandler {\n        void process(JsonObject toProcess) throws DockerAccessException;\n        void start();\n        void stop();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/chunked/PullOrPushResponseJsonHandler.java",
    "content": "package io.fabric8.maven.docker.access.chunked;\n\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.util.Logger;\n\npublic class PullOrPushResponseJsonHandler implements EntityStreamReaderUtil.JsonEntityResponseHandler {\n\n    private final Logger log;\n    \n    public PullOrPushResponseJsonHandler(Logger log) {\n        this.log = log;\n    }\n    \n    @Override\n    public void process(JsonObject json) throws DockerAccessException {\n        if (json.has(\"progressDetail\")) {\n            log.progressUpdate(getStringOrEmpty(json, \"id\"),\n                               getStringOrEmpty(json, \"status\"),\n                               getStringOrEmpty(json, \"progress\"));\n        } else if (json.has(\"error\")) {\n            throwDockerAccessException(json);\n        } else {\n            log.progressFinished();\n            logInfoMessage(json);\n            log.progressStart();\n        }\n    }\n\n    private void logInfoMessage(JsonObject json) {\n        String value;\n        if (json.has(\"stream\")) {\n            value = json.get(\"stream\").getAsString().replaceFirst(\"\\n$\", \"\");\n        } else if (json.has(\"status\")) {\n            value = json.get(\"status\").getAsString();\n        } else {\n            value = json.toString();\n        }\n        log.info(\"%s\", value);\n    }\n\n    private void throwDockerAccessException(JsonObject json) throws DockerAccessException {\n        String msg = json.get(\"error\").getAsString().trim();\n        String details = json.getAsJsonObject(\"errorDetail\").get(\"message\").getAsString().trim();\n        throw new DockerAccessException(\"%s %s\", msg, (msg.equals(details) ? \"\" : \"(\" + details + \")\"));\n    }\n\n    private String getStringOrEmpty(JsonObject json, String what) {\n        return json.has(what) ? json.get(what).getAsString() : \"\";\n    }\n\n    @Override\n    public void start() {\n        log.progressStart();\n    }\n\n    @Override\n    public void stop() {\n        log.progressFinished();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ecr/AwsSigner4.java",
    "content": "package io.fabric8.maven.docker.access.ecr;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.apache.http.HttpRequest;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.utils.URLEncodedUtils;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * AwsSigner4 implementation that signs requests with the AWS4 signing protocol. Refer to the AWS docs for mor details.\n *\n * @author chas\n * @since 2016-12-9\n */\nclass AwsSigner4 {\n\n    // a-f must be lower case\n    final private static char[] HEXITS = \"0123456789abcdef\".toCharArray();\n\n    private final String service;\n    private final String region;\n\n    /**\n     * A signer for a particular region and service.\n     *\n     * @param region The aws region.\n     * @param service The aws service.\n     */\n    AwsSigner4(String region, String service) {\n        this.region = region;\n        this.service = service;\n    }\n\n    /**\n     * Sign a request.  Add the headers that authenticate the request.\n     *\n     * @param request The request to sign.\n     * @param credentials The credentials to use when signing.\n     * @param signingTime The invocation time to use;\n     */\n    void sign(HttpRequest request, AuthConfig credentials, Date signingTime) {\n        AwsSigner4Request sr = new AwsSigner4Request(region, service, request, signingTime);\n        if(!request.containsHeader(\"X-Amz-Date\")) {\n            request.addHeader(\"X-Amz-Date\", sr.getSigningDateTime());\n        }\n        request.addHeader(\"Authorization\", task4(sr, credentials));\n        final String securityToken = credentials.getAuth();\n        if (StringUtils.isNotEmpty(securityToken)) {\n            request.addHeader(\"X-Amz-Security-Token\", securityToken);\n        }\n    }\n\n    /**\n     * Task 1.\n     * <a href=\"https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html\">Create a Canonical Request</a>\n     */\n    String task1(AwsSigner4Request sr) {\n        StringBuilder sb = new StringBuilder(sr.getMethod()).append('\\n')\n                .append(sr.getUri().getRawPath()).append('\\n')\n                .append(getCanonicalQuery(sr.getUri())).append('\\n')\n                .append(sr.getCanonicalHeaders()).append('\\n')\n                .append(sr.getSignedHeaders()).append('\\n');\n\n        hexEncode(sb, sha256(sr.getBytes()));\n        return sb.toString();\n    }\n\n    /**\n     * Task 2.\n     * <a href=\"https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html\">Create a String to Sign for Signature Version 4</a>\n     */\n    String task2(AwsSigner4Request sr) {\n        StringBuilder sb = new StringBuilder(\"AWS4-HMAC-SHA256\\n\")\n                .append(sr.getSigningDateTime()).append('\\n')\n                .append(sr.getScope()).append('\\n');\n        hexEncode(sb, sha256(task1(sr)));\n        return sb.toString();\n    }\n\n    /**\n     * Task 3.\n     * <a href=\"https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html\">Calculate the Signature for AWS Signature Version 4</a>\n     */\n    final byte[] task3(AwsSigner4Request sr, AuthConfig credentials) {\n        return hmacSha256(getSigningKey(sr, credentials), task2(sr));\n    }\n\n    private static byte[] getSigningKey(AwsSigner4Request sr, AuthConfig credentials) {\n        byte[] kSecret = (\"AWS4\" + credentials.getPassword()).getBytes(StandardCharsets.UTF_8);\n        byte[] kDate = hmacSha256(kSecret, sr.getSigningDate());\n        byte[] kRegion = hmacSha256(kDate, sr.getRegion());\n        byte[] kService = hmacSha256(kRegion, sr.getService());\n        return hmacSha256(kService, \"aws4_request\");\n    }\n\n    /**\n     * Task 4.\n     * <a href=\"https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html\">Add the Signing Information to the Request</a>\n     */\n    String task4(AwsSigner4Request sr, AuthConfig credentials) {\n        StringBuilder sb = new StringBuilder(\"AWS4-HMAC-SHA256 Credential=\")\n                .append(credentials.getUsername() ).append( '/' ).append( sr.getScope() )\n                .append(\", SignedHeaders=\").append(sr.getSignedHeaders())\n                .append(\", Signature=\" );\n        hexEncode(sb, task3(sr, credentials));\n        return sb.toString();\n    }\n\n    private String getCanonicalQuery(URI uri) {\n        String query = uri.getQuery();\n        if(query == null || query.isEmpty()) {\n            return \"\";\n        }\n        List<NameValuePair> params = URLEncodedUtils.parse(query, StandardCharsets.UTF_8);\n        Collections.sort(params, new Comparator<NameValuePair>() {\n            @Override\n            public int compare(NameValuePair l, NameValuePair r) {\n                return l.getName().compareToIgnoreCase(r.getName());\n            }\n        });\n        return URLEncodedUtils.format(params, StandardCharsets.UTF_8);\n    }\n\n    static void hexEncode(StringBuilder dst, byte[] src) {\n        for (byte aSrc : src) {\n            int v = aSrc & 0xFF;\n            dst.append(HEXITS[v >>> 4]);\n            dst.append(HEXITS[v & 0x0F]);\n        }\n    }\n\n    private static byte[] hmacSha256(byte[] key, String value) {\n        try {\n            Mac mac = Mac.getInstance(\"HmacSHA256\");\n            mac.init(new SecretKeySpec(key, \"HmacSHA256\"));\n            return mac.doFinal(value.getBytes(StandardCharsets.UTF_8));\n        }\n        catch (NoSuchAlgorithmException | InvalidKeyException e) {\n            throw new UnsupportedOperationException(e.getMessage(), e);\n        }\n    }\n\n    private static byte[] sha256(String string) {\n        return sha256(string.getBytes(StandardCharsets.UTF_8));\n    }\n\n     private static byte[] sha256(byte[] bytes) {\n         try {\n            MessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n            md.update(bytes);\n            return md.digest();\n         }\n         catch (NoSuchAlgorithmException e) {\n             throw new UnsupportedOperationException(e.getMessage(), e);\n         }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ecr/AwsSigner4Request.java",
    "content": "package io.fabric8.maven.docker.access.ecr;\n\nimport java.io.IOException;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.TimeZone;\nimport java.util.TreeMap;\n\nimport org.apache.http.Header;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpRequest;\nimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\nimport org.apache.http.util.EntityUtils;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * The state of an aws sigV4 request.\n *\n * @author chas\n * @since 2016-12-9\n */\npublic class AwsSigner4Request {\n\n    static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat(\"yyyyMMdd'T'HHmmss'Z'\");\n\n    static {\n        TimeZone utc = TimeZone.getTimeZone(\"GMT\");\n        TIME_FORMAT.setTimeZone(utc);\n    }\n\n    private static final byte[] EMPTY_BYTES = new byte[0];\n\n    private final String region;\n    private final String service;\n    private final HttpRequest request;\n\n    private final String signingDate;\n    private final String signingDateTime;\n    private final String scope;\n\n    private final String method;\n    private final URI uri;\n    private final String canonicalHeaders;\n    private final String signedHeaders;\n\n    AwsSigner4Request(String region, String service, HttpRequest request, Date signingTime) {\n        this.region = region;\n        this.service = service;\n        this.request = request;\n\n        signingDateTime = getSigningDateTime(request, signingTime);\n        signingDate = signingDateTime.substring(0, 8);\n        scope = signingDate + '/' + region + '/' + service + \"/aws4_request\";\n        method = request.getRequestLine().getMethod();\n        uri = getUri(request);\n\n        Map<String, String> headers = getOrderedHeadersToSign(request.getAllHeaders());\n        signedHeaders = StringUtils.join(headers.keySet(), ';');\n        canonicalHeaders = canonicalHeaders(headers);\n    }\n\n    public String getRegion() {\n        return region;\n    }\n\n    public String getService() {\n        return service;\n    }\n\n    public String getSigningDate() {\n        return signingDate;\n    }\n\n    public String getSigningDateTime() {\n        return signingDateTime;\n    }\n\n    public String getScope() {\n        return scope;\n    }\n\n    public String getMethod() {\n        return method;\n    }\n\n    public URI getUri() {\n        return uri;\n    }\n\n    public String getCanonicalHeaders() {\n        return canonicalHeaders;\n    }\n\n    public String getSignedHeaders() {\n        return signedHeaders;\n    }\n\n    private static String getSigningDateTime(HttpRequest request, Date signingTime) {\n        Header dateHeader = request.getFirstHeader(\"X-Amz-Date\");\n        if (dateHeader != null) {\n            return dateHeader.getValue();\n        }\n        synchronized (TIME_FORMAT) {\n            return TIME_FORMAT.format(signingTime);\n        }\n    }\n\n    private static URI getUri(HttpRequest request) {\n        String hostName = request.getFirstHeader(\"host\").getValue();\n        String requestTarget = request.getRequestLine().getUri();\n        URI requestUri = createUri(hostName, requestTarget);\n        return requestUri.normalize();\n    }\n\n    private static URI createUri(String authority, String uri) {\n        String scheme = \"https\";\n        int schemeEnd = uri.indexOf(':');\n        int pathStart = uri.indexOf('/');\n        if (schemeEnd >= 0 && schemeEnd < pathStart) {\n            scheme = uri.substring(0, schemeEnd);\n            if (uri.charAt(pathStart + 1) == '/') {\n                authority = uri.substring(pathStart);\n                pathStart = uri.indexOf('/', pathStart + 2);\n            }\n        }\n\n        String path;\n        String query;\n        int queryIdx = uri.indexOf('?', pathStart);\n        if (queryIdx < 0) {\n            query = null;\n            path = uri.substring(pathStart);\n        } else {\n            query = uri.substring(queryIdx + 1);\n            path = uri.substring(pathStart, queryIdx);\n        }\n        try {\n            return new URI(scheme, authority, path, query, null);\n        } catch (URISyntaxException e) {\n            throw new UndeclaredThrowableException(e);\n        }\n    }\n\n    /**\n     * Get the ordered map of headers to sign.\n     *\n     * @param headers the possible headers to sign\n     * @return A &lt;String, StringBuilder&gt; map of headers to sign. Key is the name of the\n     *         header, Value is the comma separated values with minimized space\n     */\n    private static Map<String, String> getOrderedHeadersToSign(Header[] headers) {\n        Map<String, String> unique = new TreeMap<>();\n        for (Header header : headers) {\n            String key = header.getName().toLowerCase(Locale.US).trim();\n            if (key.equals(\"connection\")) {\n                // do not sign 'connection' header, it is very likely to be changed en-route.\n                continue;\n            }\n            String value = header.getValue();\n            if (value == null) {\n                value = \"\";\n            } else {\n                // minimize white space\n                value = value.trim().replaceAll(\"\\\\s+\", \" \");\n            }\n            // merge all values with same header name\n            String prior = unique.get(key);\n            if (prior != null) {\n                if (prior.length() > 0) {\n                    value = prior + ',' + value;\n                }\n                unique.put(key, value);\n            } else {\n                unique.put(key, value);\n            }\n        }\n        return unique;\n    }\n\n    /**\n     * Create canonical header set. The headers are ordered by name.\n     *\n     * @param headers The set of headers to sign\n     * @return The signing value from headers. Headers are separated with newline. Each header is\n     *         formatted name:value with each header value whitespace trimmed and minimized\n     */\n    private static String canonicalHeaders(Map<String, String> headers) {\n        StringBuilder canonical = new StringBuilder();\n        for (Map.Entry<String, String> header : headers.entrySet()) {\n            canonical.append(header.getKey()).append(':').append(header.getValue()).append('\\n');\n        }\n        return canonical.toString();\n    }\n\n    byte[] getBytes() {\n        if (request instanceof HttpEntityEnclosingRequestBase) {\n            try {\n                HttpEntity entity = ((HttpEntityEnclosingRequestBase) request).getEntity();\n                return EntityUtils.toByteArray(entity);\n            } catch (IOException e) {\n                throw new UndeclaredThrowableException(e);\n            }\n        }\n        return EMPTY_BYTES;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/ecr/EcrExtendedAuth.java",
    "content": "package io.fabric8.maven.docker.access.ecr;\n\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Date;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.maven.plugin.MojoExecutionException;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.util.Logger;\n\n/**\n * Exchange local stored credentials for temporary ecr credentials\n *\n * @author chas\n * @since 2016-12-9\n */\npublic class EcrExtendedAuth {\n\n    private static final Pattern AWS_REGISTRY =\n            Pattern.compile(\"^(\\\\d{12})\\\\.dkr\\\\.ecr\\\\.([a-z\\\\-0-9]+)\\\\.amazonaws\\\\.com$\");\n\n    private final Logger logger;\n    private final boolean isAwsRegistry;\n    private final String accountId;\n    private final String region;\n\n    /**\n     * Is given the registry an ecr registry?\n     * \n     * @param registry the registry name\n     * @return true, if the registry matches the ecr pattern\n     */\n    public static boolean isAwsRegistry(String registry) {\n        return (registry != null) && AWS_REGISTRY.matcher(registry).matches();\n    }\n    \n    /**\n     * Initialize an extended authentication for ecr registry.\n     *\n     * @param registry The registry, we may or may not be an ecr registry.\n     */\n    public EcrExtendedAuth(Logger logger, String registry) {\n        this.logger = logger;\n        Matcher matcher = AWS_REGISTRY.matcher(registry);\n        isAwsRegistry = matcher.matches();\n        if (isAwsRegistry) {\n            accountId = matcher.group(1);\n            region = matcher.group(2);\n        } else {\n            accountId = null;\n            region = null;\n        }\n        logger.debug(\"registry = %s, isValid= %b\", registry, isAwsRegistry);\n    }\n\n    /**\n     * Is the registry an ecr registry?\n     * @return true, if the registry matches the ecr pattern\n     */\n    public boolean isAwsRegistry() {\n        return isAwsRegistry;\n    }\n\n    /**\n     * Perform extended authentication.  Use the provided credentials as IAM credentials and\n     * get a temporary ECR token.\n     *\n     * @param localCredentials IAM id/secret\n     * @return ECR base64 encoded username:password\n     * @throws IOException\n     * @throws MojoExecutionException\n     */\n    public AuthConfig extendedAuth(AuthConfig localCredentials) throws IOException, MojoExecutionException {\n        JsonObject jo = getAuthorizationToken(localCredentials);\n\n        JsonArray authorizationDatas = jo.getAsJsonArray(\"authorizationData\");\n        JsonObject authorizationData = authorizationDatas.get(0).getAsJsonObject();\n        String authorizationToken = authorizationData.get(\"authorizationToken\").getAsString();\n\n        return new AuthConfig(authorizationToken, \"none\");\n    }\n\n    private JsonObject getAuthorizationToken(AuthConfig localCredentials) throws IOException, MojoExecutionException {\n        HttpPost request = createSignedRequest(localCredentials, new Date());\n        return executeRequest(createClient(), request);\n    }\n\n    CloseableHttpClient createClient() {\n        return HttpClients.custom().useSystemProperties().build();\n    }\n\n    private JsonObject executeRequest(CloseableHttpClient client, HttpPost request) throws IOException, MojoExecutionException {\n        try {\n            CloseableHttpResponse response = client.execute(request);\n            int statusCode = response.getStatusLine().getStatusCode();\n            logger.debug(\"Response status %d\", statusCode);\n            if (statusCode != HttpStatus.SC_OK) {\n                throw new MojoExecutionException(\"AWS authentication failure\");\n            }\n\n            HttpEntity entity = response.getEntity();\n            Reader jr = new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8);\n            return new Gson().fromJson(jr, JsonObject.class);\n        }\n        finally {\n            client.close();\n        }\n    }\n\n    HttpPost createSignedRequest(AuthConfig localCredentials, Date time) {\n        String host = \"api.ecr.\" + region + \".amazonaws.com\";\n\n        logger.debug(\"Get ECR AuthorizationToken from %s\", host);\n\n        HttpPost request = new HttpPost(\"https://\" + host + '/');\n        request.setHeader(\"host\", host);\n        request.setHeader(\"Content-Type\", \"application/x-amz-json-1.1\");\n        request.setHeader(\"X-Amz-Target\", \"AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken\");\n        request.setEntity(new StringEntity(\"{\\\"registryIds\\\":[\\\"\"+ accountId + \"\\\"]}\", StandardCharsets.UTF_8));\n\n        AwsSigner4 signer = new AwsSigner4(region, \"ecr\");\n        signer.sign(request, localCredentials, time);\n        return request;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/ApacheHttpClientDelegate.java",
    "content": "package io.fabric8.maven.docker.access.hc;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.apache.http.HttpHeaders;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.ResponseHandler;\nimport org.apache.http.client.methods.HttpDelete;\nimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpPut;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.entity.FileEntity;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.util.EntityUtils;\n\nimport io.fabric8.maven.docker.access.hc.http.HttpRequestException;\nimport io.fabric8.maven.docker.access.hc.util.ClientBuilder;\n\npublic class ApacheHttpClientDelegate {\n\n    private final ClientBuilder clientBuilder;\n    private final CloseableHttpClient httpClient;\n\n    public ApacheHttpClientDelegate(ClientBuilder clientBuilder, boolean pooled) throws IOException {\n        this.clientBuilder = clientBuilder;\n        this.httpClient = pooled ? clientBuilder.buildPooledClient() : clientBuilder.buildBasicClient();\n    }\n\n    public CloseableHttpClient createBasicClient()  {\n        try {\n            return clientBuilder.buildBasicClient();\n        } catch (IOException exp) {\n            throw new IllegalStateException(\"Cannot create single HTTP client: \" + exp,exp);\n        }\n    }\n\n    public CloseableHttpClient getHttpClient() {\n        return httpClient;\n    }\n\n    public void close() throws IOException {\n        httpClient.close();\n    }\n\n    public int delete(String url, int... statusCodes) throws IOException {\n        return delete(url, new StatusCodeResponseHandler(), statusCodes);\n    }\n\n    public static class StatusCodeResponseHandler implements ResponseHandler<Integer> {\n        @Override\n        public Integer handleResponse(HttpResponse response)\n            throws IOException {\n            return response.getStatusLine().getStatusCode();\n        }\n\n    }\n\n    public <T> T delete(String url, ResponseHandler<T> responseHandler, int... statusCodes)\n        throws IOException {\n        return httpClient.execute(newDelete(url),\n                                  new StatusCodeCheckerResponseHandler<>(responseHandler,\n                                                                         statusCodes));\n    }\n\n    public String get(String url, int... statusCodes) throws IOException {\n        return httpClient.execute(newGet(url), new StatusCodeCheckerResponseHandler<>(\n            new BodyResponseHandler(), statusCodes));\n    }\n\n    public <T> T get(String url, ResponseHandler<T> responseHandler, int... statusCodes)\n        throws IOException {\n        return httpClient\n            .execute(newGet(url), new StatusCodeCheckerResponseHandler<>(responseHandler, statusCodes));\n    }\n    public static class BodyResponseHandler implements ResponseHandler<String> {\n        @Override\n        public String handleResponse(HttpResponse response)\n            throws IOException {\n            return getResponseMessage(response);\n        }\n\n    }\n\n    private static String getResponseMessage(HttpResponse response) throws IOException {\n        return (response.getEntity() == null) ? null\n            : EntityUtils.toString(response.getEntity()).trim();\n    }\n\n    public <T> T post(String url, Object body, Map<String, String> headers,\n                      ResponseHandler<T> responseHandler, int... statusCodes) throws IOException {\n        HttpUriRequest request = newPost(url, body);\n        for (Entry<String, String> entry : headers.entrySet()) {\n            request.addHeader(entry.getKey(), entry.getValue());\n        }\n\n        return httpClient.execute(request, new StatusCodeCheckerResponseHandler<>(responseHandler, statusCodes));\n    }\n\n    public <T> T post(String url, Object body, ResponseHandler<T> responseHandler,\n                      int... statusCodes) throws IOException {\n        return httpClient.execute(newPost(url, body),\n                                  new StatusCodeCheckerResponseHandler<>(responseHandler,\n                                                                         statusCodes));\n    }\n\n    public int post(String url, int... statusCodes) throws IOException {\n        return post(url, null, new StatusCodeResponseHandler(), statusCodes);\n    }\n\n    public int put(String url, Object body, int... statusCodes) throws IOException {\n        return httpClient.execute(newPut(url, body),\n                                  new StatusCodeCheckerResponseHandler<>(new StatusCodeResponseHandler(), statusCodes));\n    }\n\n    // =========================================================================================\n\n    private HttpUriRequest addDefaultHeaders(HttpUriRequest req) {\n        req.addHeader(HttpHeaders.ACCEPT, \"*/*\");\n        req.addHeader(HttpHeaders.CONTENT_TYPE, \"application/json\");\n        return req;\n    }\n\n\n    private HttpUriRequest newDelete(String url) {\n        return addDefaultHeaders(new HttpDelete(url));\n    }\n\n    private HttpUriRequest newGet(String url) {\n        return addDefaultHeaders(new HttpGet(url));\n    }\n\n    private HttpUriRequest newPut(String url, Object body) {\n        HttpPut put = new HttpPut(url);\n        setEntityIfGiven(put, body);\n        return addDefaultHeaders(put);\n    }\n\n    private HttpUriRequest newPost(String url, Object body) {\n        HttpPost post = new HttpPost(url);\n        setEntityIfGiven(post, body);\n        return addDefaultHeaders(post);\n    }\n\n\n    private void setEntityIfGiven(HttpEntityEnclosingRequestBase request, Object entity) {\n        if (entity != null) {\n            if (entity instanceof File) {\n                request.setEntity(new FileEntity((File) entity));\n            } else {\n                request.setEntity(new StringEntity((String) entity, Charset.defaultCharset()));\n            }\n        }\n    }\n\n    private static class StatusCodeCheckerResponseHandler<T> implements ResponseHandler<T> {\n\n        private int[] statusCodes;\n        private ResponseHandler<T> delegate;\n\n        StatusCodeCheckerResponseHandler(ResponseHandler<T> delegate, int... statusCodes) {\n            this.statusCodes = statusCodes;\n            this.delegate = delegate;\n        }\n\n        @Override\n        public T handleResponse(HttpResponse response) throws IOException {\n            StatusLine statusLine = response.getStatusLine();\n            int statusCode = statusLine.getStatusCode();\n            for (int code : statusCodes) {\n                if (statusCode == code) {\n                    return delegate.handleResponse(response);\n                }\n            }\n\n            String reason = statusLine.getReasonPhrase().trim();\n            throw new HttpRequestException(String.format(\"%s (%s: %d)\", getResponseMessage(response),\n                                                         reason, statusCode));\n        }\n\n    }\n\n    public static class BodyAndStatusResponseHandler implements ResponseHandler<HttpBodyAndStatus> {\n\n        @Override\n        public HttpBodyAndStatus handleResponse(HttpResponse response)\n            throws IOException {\n            return new HttpBodyAndStatus(response.getStatusLine().getStatusCode(),\n                                         getResponseMessage(response));\n        }\n    }\n\n    public static class HttpBodyAndStatus {\n\n        private final int statusCode;\n        private final String body;\n\n        public HttpBodyAndStatus(int statusCode, String body) {\n            this.statusCode = statusCode;\n            this.body = body;\n        }\n\n        public int getStatusCode() {\n            return statusCode;\n        }\n\n        public String getBody() {\n            return body;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClient.java",
    "content": "package io.fabric8.maven.docker.access.hc;\n\nimport static java.net.HttpURLConnection.*;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.LineNumberReader;\nimport java.io.OutputStream;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.HttpHeaders;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpResponseException;\nimport org.apache.http.client.ResponseHandler;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.BuildOptions;\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.NetworkCreateConfig;\nimport io.fabric8.maven.docker.access.UrlBuilder;\nimport io.fabric8.maven.docker.access.VolumeCreateConfig;\nimport io.fabric8.maven.docker.access.chunked.BuildJsonResponseHandler;\nimport io.fabric8.maven.docker.access.chunked.EntityStreamReaderUtil;\nimport io.fabric8.maven.docker.access.chunked.PullOrPushResponseJsonHandler;\nimport io.fabric8.maven.docker.access.hc.ApacheHttpClientDelegate.BodyAndStatusResponseHandler;\nimport io.fabric8.maven.docker.access.hc.ApacheHttpClientDelegate.HttpBodyAndStatus;\nimport io.fabric8.maven.docker.access.hc.http.HttpClientBuilder;\nimport io.fabric8.maven.docker.access.hc.unix.UnixSocketClientBuilder;\nimport io.fabric8.maven.docker.access.hc.util.ClientBuilder;\nimport io.fabric8.maven.docker.access.hc.win.NamedPipeClientBuilder;\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.access.log.LogGetHandle;\nimport io.fabric8.maven.docker.access.log.LogRequestor;\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.log.DefaultLogCallback;\nimport io.fabric8.maven.docker.log.LogOutputSpec;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.model.ContainersListElement;\nimport io.fabric8.maven.docker.model.ExecDetails;\nimport io.fabric8.maven.docker.model.Image;\nimport io.fabric8.maven.docker.model.ImageDetails;\nimport io.fabric8.maven.docker.model.Network;\nimport io.fabric8.maven.docker.model.NetworksListElement;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.JsonFactory;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.TimestampFactory;\n\n/**\n * Implementation using <a href=\"http://hc.apache.org/\">Apache HttpComponents</a>\n * for remotely accessing the docker host.\n * <p/>\n * The design goal here is to provide only the functionality required for this plugin in order to\n * make it as robust as possible against docker API changes (which happen quite frequently). That's\n * also the reason, why no framework like JAX-RS or docker-java is used so that the dependencies are\n * kept low.\n * <p/>\n * Of course, it's a bit more manual work, but it's worth the effort\n * (as long as the Docker API functionality required is not too much).\n *\n * @author roland\n * @since 26.03.14\n */\npublic class DockerAccessWithHcClient implements DockerAccess {\n\n    // Base URL which is given through when using UnixSocket communication but is not really used\n    private static final String UNIX_URL = \"unix://127.0.0.1:1/\";\n\n    // Base URL which is given through when using NamedPipe communication but is not really used\n    private static final String NPIPE_URL = \"npipe://127.0.0.1:1/\";\n\n    // Minimal API version, independent of any feature used\n    public static final String API_VERSION = \"1.18\";\n\n    // Copy buffer size when saving images\n    private static final int COPY_BUFFER_SIZE = 65536;\n\n    // Logging\n    private final Logger log;\n\n    private final ApacheHttpClientDelegate delegate;\n    private final UrlBuilder urlBuilder;\n\n    /**\n     * Create a new access for the given URL\n     *\n     * @param baseUrl  base URL for accessing the docker Daemon\n     * @param certPath used to build up a keystore with the given keys and certificates found in this\n     *                 directory\n     * @param maxConnections maximum parallel connections allowed to docker daemon (if a pool is used)\n     * @param log      a log handler for printing out logging information\n     * @paran usePool  whether to use a connection bool or not\n     */\n    public DockerAccessWithHcClient(String baseUrl,\n                                    String certPath,\n                                    int maxConnections,\n                                    Logger log) throws IOException {\n        URI uri = URI.create(baseUrl);\n        if (uri.getScheme() == null) {\n            throw new IllegalArgumentException(\"The docker access url '\" + baseUrl + \"' must contain a schema tcp://, unix:// or npipe://\");\n        }\n        if (uri.getScheme().equalsIgnoreCase(\"unix\")) {\n            this.delegate = createHttpClient(new UnixSocketClientBuilder(uri.getPath(), maxConnections, log));\n            baseUrl = UNIX_URL;\n        } else if (uri.getScheme().equalsIgnoreCase(\"npipe\")) {\n            this.delegate = createHttpClient(new NamedPipeClientBuilder(uri.getPath(), maxConnections, log), false);\n            baseUrl = NPIPE_URL;\n        } else {\n            this.delegate = createHttpClient(new HttpClientBuilder(isSSL(baseUrl) ? certPath : null, maxConnections));\n        }\n\n        // Strip trailing slashes if any\n        while(baseUrl.endsWith(\"/\")) {\n            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);\n        }\n        this.urlBuilder = new UrlBuilder(baseUrl, \"v\" + fetchApiVersionFromServer(baseUrl, this.delegate));\n        this.log = log;\n    }\n\n    /** {@inheritDoc} */\n    @Override\n    public String getServerApiVersion() throws DockerAccessException {\n        try {\n            String url = urlBuilder.version();\n            log.verbose(Logger.LogVerboseCategory.API,\"GET %s\", url);\n            String response = delegate.get(url, 200);\n            JsonObject info = JsonFactory.newJsonObject(response);\n            return info.get(\"ApiVersion\").getAsString();\n        } catch (Exception e) {\n            throw new DockerAccessException(e, \"Cannot extract API version from server %s\", urlBuilder.getBaseUrl());\n        }\n    }\n\n    @Override\n    public void startExecContainer(String containerId, LogOutputSpec outputSpec) throws DockerAccessException {\n        try {\n            String url = urlBuilder.startExecContainer(containerId);\n            JsonObject request = new JsonObject();\n            request.addProperty(\"Detach\", false);\n            request.addProperty(\"Tty\", true);\n\n            log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with %s\", url, request);\n            delegate.post(url, request.toString(), createExecResponseHandler(outputSpec), HTTP_OK);\n        } catch (Exception e) {\n            throw new DockerAccessException(e, \"Unable to start container id [%s]\", containerId);\n        }\n    }\n\n    private ResponseHandler<Object> createExecResponseHandler(LogOutputSpec outputSpec) throws FileNotFoundException {\n        final LogCallback callback = new DefaultLogCallback(outputSpec);\n        return new ResponseHandler<Object>() {\n            @Override\n            public Object handleResponse(HttpResponse response) throws IOException {\n                try (InputStream stream = response.getEntity().getContent()) {\n                    LineNumberReader reader = new LineNumberReader(new InputStreamReader(stream));\n                    String line;\n                    try {\n                        callback.open();\n                        while ( (line = reader.readLine()) != null) {\n                            callback.log(1, TimestampFactory.createTimestamp(), line);\n                        }\n                    } catch (LogCallback.DoneException e) {\n                        // Ok, we stop here ...\n                    } finally {\n                        callback.close();\n                    }\n                }\n                return null;\n            }\n        };\n    }\n\n    @Override\n    public String createExecContainer(String containerId, Arguments arguments) throws DockerAccessException {\n        String url = urlBuilder.createExecContainer(containerId);\n        JsonObject request = new JsonObject();\n        request.addProperty(\"Tty\", true);\n        request.addProperty(\"AttachStdin\", false);\n        request.addProperty(\"AttachStdout\", true);\n        request.addProperty(\"AttachStderr\", true);\n        request.add(\"Cmd\", JsonFactory.newJsonArray(arguments.getExec()));\n\n        String execJsonRequest = request.toString();\n        log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with %s\", url, execJsonRequest);\n        try {\n            String response = delegate.post(url, execJsonRequest, new ApacheHttpClientDelegate.BodyResponseHandler(), HTTP_CREATED);\n            JsonObject json = JsonFactory.newJsonObject(response);\n            if (json.has(\"Warnings\")) {\n                logWarnings(json);\n            }\n\n            return json.get(\"Id\").getAsString();\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to exec [%s] on container [%s]\", request.toString(),\n                                            containerId);\n        }\n\n    }\n\n    @Override\n    public String createContainer(ContainerCreateConfig containerConfig, String containerName)\n            throws DockerAccessException {\n        String createJson = containerConfig.toJson();\n        log.debug(\"Container create config: %s\", createJson);\n\n        try {\n            String url = urlBuilder.createContainer(containerName);\n            log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with %s\", url, createJson);\n            String response =\n                    delegate.post(url, createJson, new ApacheHttpClientDelegate.BodyResponseHandler(), HTTP_CREATED);\n            JsonObject json = JsonFactory.newJsonObject(response);\n            logWarnings(json);\n\n            // only need first 12 to id a container\n            return json.get(\"Id\").getAsString().substring(0, 12);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to create container for [%s]\",\n                                            containerConfig.getImageName());\n        }\n    }\n\n    @Override\n    public void startContainer(String containerId) throws DockerAccessException {\n        try {\n            String url = urlBuilder.startContainer(containerId);\n            log.verbose(Logger.LogVerboseCategory.API,\"POST %s\", url);\n            delegate.post(url, HTTP_NO_CONTENT, HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to start container id [%s]\", containerId);\n        }\n    }\n\n    @Override\n    public void stopContainer(String containerId, int killWait) throws DockerAccessException {\n        try {\n            String url = urlBuilder.stopContainer(containerId, killWait);\n            log.verbose(Logger.LogVerboseCategory.API,\"POST %s\", url);\n            delegate.post(url, HTTP_NO_CONTENT, HTTP_NOT_MODIFIED);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to stop container id [%s]\", containerId);\n        }\n    }\n\n    @Override\n    public void killContainer(String containerId) throws DockerAccessException {\n        try {\n            String url = urlBuilder.killContainer(containerId);\n            log.verbose(Logger.LogVerboseCategory.API,\"POST %s\", url);\n            delegate.post(url, HTTP_NO_CONTENT, HTTP_NOT_MODIFIED);\n        } catch (IOException ie) {\n            throw new DockerAccessException(ie, \"Unable to kill container id [%s]\", containerId);\n        }\n    }\n\n    @Override\n    public void buildImage(String image, File dockerArchive, BuildOptions options) throws DockerAccessException {\n        try {\n            String url = urlBuilder.buildImage(image, options);\n            log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with contents of file %s\", url, dockerArchive);\n            delegate.post(url, dockerArchive, createBuildResponseHandler(), HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to build image [%s]\", image);\n        }\n    }\n\n    @Override\n    public void copyArchive(String containerId, File archive, String targetPath)\n            throws DockerAccessException {\n        try {\n            String url = urlBuilder.copyArchive(containerId, targetPath);\n            log.verbose(Logger.LogVerboseCategory.API,\"PUT to %s with contents of file %s\", url, archive);\n            delegate.put(url, archive, HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to copy archive %s to container [%s] with path %s\",\n                                            archive.toPath(), containerId, targetPath);\n        }\n    }\n\n    @Override\n    public void getLogSync(String containerId, LogCallback callback) {\n        LogRequestor extractor = new LogRequestor(delegate.getHttpClient(), urlBuilder, containerId, callback);\n        extractor.fetchLogs();\n    }\n\n    @Override\n    public LogGetHandle getLogAsync(String containerId, LogCallback callback) {\n        LogRequestor extractor = new LogRequestor(delegate.createBasicClient(), urlBuilder, containerId, callback);\n        extractor.start();\n        return extractor;\n    }\n\n    @Override\n    public List<Container> getContainersForImage(String image, boolean all) throws DockerAccessException {\n        String url;\n        String serverApiVersion = getServerApiVersion();\n        if (EnvUtil.greaterOrEqualsVersion(serverApiVersion, \"1.23\")) {\n            // For Docker >= 1.11 we can use a new filter when listing containers\n            url = urlBuilder.listContainers(all, \"ancestor\",image);\n        } else {\n            // For older versions (< Docker 1.11) we need to iterate over the containers.\n            url = urlBuilder.listContainers(all);\n        }\n\n        try {\n            log.verbose(Logger.LogVerboseCategory.API,\"GET %s\", url);\n            String response = delegate.get(url, HTTP_OK);\n            JsonArray array = JsonFactory.newJsonArray(response);\n            List<Container> containers = new ArrayList<>();\n\n            for (int i = 0; i < array.size(); i++) {\n                JsonObject element = array.get(i).getAsJsonObject();\n                if (image.equals(element.get(\"Image\").getAsString())) {\n                    containers.add(new ContainersListElement(element));\n                }\n            }\n            return containers;\n        } catch (IOException e) {\n            throw new DockerAccessException(e.getMessage());\n        }\n    }\n\n    @Override\n    public List<Container> listContainers(boolean all) throws DockerAccessException {\n        String url = urlBuilder.listContainers(all);\n\n        try {\n            String response = delegate.get(url, HTTP_OK);\n            JsonArray array = JsonFactory.newJsonArray(response);\n            List<Container> containers = new ArrayList<>();\n\n            for (JsonElement element : array) {\n                containers.add(new ContainersListElement(element.getAsJsonObject()));\n            }\n\n            return containers;\n        } catch (IOException e) {\n            throw new DockerAccessException(e.getMessage());\n        }\n    }\n\n    @Override\n    public ContainerDetails getContainer(String containerIdOrName) throws DockerAccessException {\n        HttpBodyAndStatus response = inspectContainer(containerIdOrName);\n        if (response.getStatusCode() == HTTP_NOT_FOUND) {\n            return null;\n        } else {\n            return new ContainerDetails(JsonFactory.newJsonObject(response.getBody()));\n        }\n    }\n\n    @Override\n    public ExecDetails getExecContainer(String containerIdOrName) throws DockerAccessException {\n        HttpBodyAndStatus response = inspectExecContainer(containerIdOrName);\n        if (response.getStatusCode() == HTTP_NOT_FOUND) {\n            return null;\n        } else {\n            return new ExecDetails(JsonFactory.newJsonObject(response.getBody()));\n        }\n    }\n\n    private HttpBodyAndStatus inspectContainer(String containerIdOrName) throws DockerAccessException {\n        try {\n            String url = urlBuilder.inspectContainer(containerIdOrName);\n            return delegate.get(url, new BodyAndStatusResponseHandler(), HTTP_OK, HTTP_NOT_FOUND);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to retrieve container name for [%s]\", containerIdOrName);\n        }\n    }\n\n    private HttpBodyAndStatus inspectExecContainer(String containerIdOrName) throws DockerAccessException {\n        try {\n            String url = urlBuilder.inspectExecContainer(containerIdOrName);\n            return delegate.get(url, new BodyAndStatusResponseHandler(), HTTP_OK, HTTP_NOT_FOUND);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to retrieve container name for [%s]\", containerIdOrName);\n        }\n    }\n\n    @Override\n    public List<Image> listImages(boolean all) throws DockerAccessException {\n        String url = urlBuilder.listImages(all);\n\n        try {\n            String response = delegate.get(url, HTTP_OK);\n            JsonArray array = JsonFactory.newJsonArray(response);\n            List<Image> images = new ArrayList<>(array.size());\n\n            for (int i = 0; i < array.size(); i++) {\n                images.add(new ImageDetails(array.get(i).getAsJsonObject()));\n            }\n\n            return images;\n        } catch(IOException e) {\n            throw new DockerAccessException(e.getMessage());\n        }\n    }\n\n    @Override\n    public boolean hasImage(String name) throws DockerAccessException {\n        String url = urlBuilder.inspectImage(name);\n        try {\n            return delegate.get(url, new ApacheHttpClientDelegate.StatusCodeResponseHandler(), HTTP_OK, HTTP_NOT_FOUND) == HTTP_OK;\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to check image [%s]\", name);\n        }\n    }\n\n    @Override\n    public String getImageId(String name) throws DockerAccessException {\n        HttpBodyAndStatus response = inspectImage(name);\n        if (response.getStatusCode() == HTTP_NOT_FOUND) {\n            return null;\n        }\n        JsonObject imageDetails = JsonFactory.newJsonObject(response.getBody());\n        return imageDetails.get(\"Id\").getAsString().substring(0, 12);\n    }\n\n    private HttpBodyAndStatus inspectImage(String name) throws DockerAccessException {\n        String url = urlBuilder.inspectImage(name);\n        try {\n            return delegate.get(url, new BodyAndStatusResponseHandler(), HTTP_OK, HTTP_NOT_FOUND);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to inspect image [%s]\", name);\n        }\n    }\n\n    @Override\n    public void removeContainer(String containerId, boolean removeVolumes)\n            throws DockerAccessException {\n        try {\n            String url = urlBuilder.removeContainer(containerId, removeVolumes);\n            log.verbose(Logger.LogVerboseCategory.API,\"DELETE %s\", url);\n            delegate.delete(url, HTTP_NO_CONTENT);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to remove container [%s]\", containerId);\n        }\n    }\n\n    @Override\n    public void loadImage(String image, File tarArchive) throws DockerAccessException {\n        String url = urlBuilder.loadImage();\n\n        log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with contents of file %s\", url, tarArchive);\n        try {\n            delegate.post(url, tarArchive, new BodyAndStatusResponseHandler(), HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to load %s\", tarArchive);\n        }\n    }\n\n    @Override\n    public void pullImage(String image, AuthConfig authConfig, String registry)\n            throws DockerAccessException {\n        ImageName name = new ImageName(image);\n        String pullUrl = urlBuilder.pullImage(name, registry);\n\n        try {\n            delegate.post(pullUrl, null, createAuthHeader(authConfig),\n                    createPullOrPushResponseHandler(), HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to pull '%s'%s\", image, (registry != null) ? \" from registry '\" + registry + \"'\" : \"\");\n        }\n    }\n\n    @Override\n    public void pushImage(String image, AuthConfig authConfig, String registry, int retries)\n            throws DockerAccessException {\n        ImageName name = new ImageName(image);\n        String pushUrl = urlBuilder.pushImage(name, registry);\n        TemporaryImageHandler temporaryImageHandler = tagTemporaryImage(name, registry);\n        DockerAccessException dae = null;\n        try {\n            doPushImage(pushUrl, createAuthHeader(authConfig), createPullOrPushResponseHandler(), HTTP_OK, retries);\n        } catch (IOException e) {\n            dae = new DockerAccessException(e, \"Unable to push '%s'%s\", image, (registry != null) ? \" to registry '\" + registry + \"'\" : \"\");\n            throw dae;\n        } finally {\n            temporaryImageHandler.handle(dae);\n        }\n    }\n\n    @Override\n    public void saveImage(String image, String filename, ArchiveCompression compression) throws DockerAccessException {\n        ImageName name = new ImageName(image);\n        String url = urlBuilder.getImage(name);\n        try {\n            delegate.get(url, getImageResponseHandler(filename, compression), HTTP_OK);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to save '%s' to '%s'\", image, filename);\n        }\n\n    }\n\n    private ResponseHandler<Object> getImageResponseHandler(final String filename, final ArchiveCompression compression) throws FileNotFoundException {\n        return new ResponseHandler<Object>() {\n            @Override\n            public Object handleResponse(HttpResponse response) throws IOException {\n                try (InputStream stream = response.getEntity().getContent();\n                     OutputStream out = compression.wrapOutputStream(new FileOutputStream(filename))) {\n                    IOUtils.copy(stream, out, COPY_BUFFER_SIZE);\n                }\n                return null;\n            }\n        };\n    }\n\n    @Override\n    public void tag(String sourceImage, String targetImage, boolean force)\n            throws DockerAccessException {\n        ImageName source = new ImageName(sourceImage);\n        ImageName target = new ImageName(targetImage);\n        try {\n            String url = urlBuilder.tagContainer(source, target, force);\n            delegate.post(url, HTTP_CREATED);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to add tag [%s] to image [%s]\", targetImage,\n                    sourceImage, e);\n        }\n    }\n\n    @Override\n    public boolean removeImage(String image, boolean... forceOpt) throws DockerAccessException {\n        boolean force = forceOpt != null && forceOpt.length > 0 && forceOpt[0];\n        try {\n            String url = urlBuilder.deleteImage(image, force);\n            HttpBodyAndStatus response = delegate.delete(url, new BodyAndStatusResponseHandler(), HTTP_OK, HTTP_NOT_FOUND);\n            if (log.isDebugEnabled()) {\n                logRemoveResponse(JsonFactory.newJsonArray(response.getBody()));\n            }\n\n            return response.getStatusCode() == HTTP_OK;\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to remove image [%s]\", image);\n        }\n    }\n\n    @Override\n    public List<Network> listNetworks() throws DockerAccessException {\n        String url = urlBuilder.listNetworks();\n\n        try {\n            String response = delegate.get(url, HTTP_OK);\n            JsonArray array = JsonFactory.newJsonArray(response);\n            List<Network> networks = new ArrayList<>(array.size());\n\n            for (int i = 0; i < array.size(); i++) {\n                networks.add(new NetworksListElement(array.get(i).getAsJsonObject()));\n            }\n\n            return networks;\n        } catch (IOException e) {\n            throw new DockerAccessException(e.getMessage());\n        }\n    }\n\n    @Override\n    public String createNetwork(NetworkCreateConfig networkConfig)\n            throws DockerAccessException {\n        String createJson = networkConfig.toJson();\n        log.debug(\"Network create config: \" + createJson);\n        try {\n            String url = urlBuilder.createNetwork();\n            log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with %s\", url, createJson);\n            String response =\n                    delegate.post(url, createJson, new ApacheHttpClientDelegate.BodyResponseHandler(), HTTP_CREATED);\n            log.debug(response);\n            JsonObject json = JsonFactory.newJsonObject(response);\n            if (json.has(\"Warnings\")) {\n                logWarnings(json);\n            }\n\n            // only need first 12 to id a container\n            return json.get(\"Id\").getAsString().substring(0, 12);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to create network for [%s]\",\n                    networkConfig.getName());\n        }\n    }\n\n    @Override\n    public boolean removeNetwork(String networkId)\n            throws DockerAccessException {\n        try {\n            String url = urlBuilder.removeNetwork(networkId);\n            log.verbose(Logger.LogVerboseCategory.API,\"DELETE %s\", url);\n            int status = delegate.delete(url, HTTP_OK, HTTP_NO_CONTENT, HTTP_NOT_FOUND);\n            return status == HTTP_OK || status == HTTP_NO_CONTENT;\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to remove network [%s]\", networkId);\n        }\n    }\n\n    @Override\n    public String createVolume(VolumeCreateConfig containerConfig)\n           throws DockerAccessException\n    {\n        String createJson = containerConfig.toJson();\n        log.debug(\"Volume create config: %s\", createJson);\n\n        try\n        {\n            String url = urlBuilder.createVolume();\n            log.verbose(Logger.LogVerboseCategory.API,\"POST to %s with %s\", url, createJson);\n            String response =\n                    delegate.post(url,\n                                  createJson,\n                                  new ApacheHttpClientDelegate.BodyResponseHandler(),\n                                  HTTP_CREATED);\n            JsonObject json = JsonFactory.newJsonObject(response);\n            logWarnings(json);\n\n            return json.get(\"Name\").getAsString();\n        }\n        catch (IOException e)\n        {\n           throw new DockerAccessException(e, \"Unable to create volume for [%s]\",\n                                           containerConfig.getName());\n        }\n    }\n\n    @Override\n    public void removeVolume(String name) throws DockerAccessException {\n        try {\n            String url = urlBuilder.removeVolume(name);\n            log.verbose(Logger.LogVerboseCategory.API,\"DELETE %s\", url);\n            delegate.delete(url, HTTP_NO_CONTENT, HTTP_NOT_FOUND);\n        } catch (IOException e) {\n            throw new DockerAccessException(e, \"Unable to remove volume [%s]\", name);\n        }\n    }\n\n\n    // ---------------\n    // Lifecycle methods not needed here\n    @Override\n    public void start() {\n    }\n\n    @Override\n    public void shutdown() {\n        try {\n            delegate.close();\n        } catch (IOException exp) {\n            log.error(\"Error while closing HTTP client: \" + exp,exp);\n        }\n    }\n\n    ApacheHttpClientDelegate createHttpClient(ClientBuilder builder) throws IOException {\n    \treturn createHttpClient(builder, true);\n    }\n\n    ApacheHttpClientDelegate createHttpClient(ClientBuilder builder, boolean pooled) throws IOException {\n        return new ApacheHttpClientDelegate(builder, pooled);\n    }\n\n    // visible for testing?\n    private HcChunkedResponseHandlerWrapper createBuildResponseHandler() {\n        return new HcChunkedResponseHandlerWrapper(new BuildJsonResponseHandler(log));\n    }\n\n    // visible for testing?\n    private HcChunkedResponseHandlerWrapper createPullOrPushResponseHandler() {\n        return new HcChunkedResponseHandlerWrapper(new PullOrPushResponseJsonHandler(log));\n    }\n\n    private Map<String, String> createAuthHeader(AuthConfig authConfig) {\n        if (authConfig == null) {\n            authConfig = AuthConfig.EMPTY_AUTH_CONFIG;\n        }\n        return Collections.singletonMap(\"X-Registry-Auth\", authConfig.toHeaderValue());\n    }\n\n    private boolean isRetryableErrorCode(int errorCode) {\n        // there eventually could be more then one of this\n        return errorCode == HTTP_INTERNAL_ERROR;\n    }\n\n    private void doPushImage(String url, Map<String, String> header, HcChunkedResponseHandlerWrapper handler, int status,\n                             int retries) throws IOException {\n        // 0: The original attemp, 1..retry: possible retries.\n        for (int i = 0; i <= retries; i++) {\n            try {\n                delegate.post(url, null, header, handler, HTTP_OK);\n                return;\n            } catch (HttpResponseException e) {\n                if (isRetryableErrorCode(e.getStatusCode()) && i != retries) {\n                    log.warn(\"failed to push image to [{}], retrying...\", url);\n                } else {\n                    throw e;\n                }\n            }\n        }\n    }\n\n    private TemporaryImageHandler tagTemporaryImage(ImageName name, String registry) throws DockerAccessException {\n        String targetImage = name.getFullName(registry);\n        if (name.hasRegistry() || registry == null) {\n            return () ->\n                log.info(\"Temporary image tag skipped. Target image '%s' already has registry set or no registry is available\",\n                    targetImage);\n        }\n\n        String fullName = name.getFullName();\n        boolean alreadyHasImage = hasImage(targetImage);\n\n        if (alreadyHasImage) {\n            log.warn(\"Target image '%s' already exists. Tagging of '%s' will replace existing image\",\n                targetImage, fullName);\n        }\n\n        tag(fullName, targetImage, false);\n        return alreadyHasImage ?\n            () -> log.info(\"Tagged image '%s' won't be removed after tagging as it already existed\", targetImage) :\n            new RemovingTemporaryImageHandler(targetImage);\n    }\n\n    // ===========================================================================================================\n\n    private void logWarnings(JsonObject body) {\n        if (body.has(\"Warnings\")) {\n            JsonElement warningsObj = body.get(\"Warnings\");\n            if (!warningsObj.isJsonNull()) {\n                JsonArray warnings = (JsonArray) warningsObj;\n                for (int i = 0; i < warnings.size(); i++) {\n                    log.warn(warnings.get(i).getAsString());\n                }\n            }\n        }\n    }\n\n    // Callback for processing response chunks\n    private void logRemoveResponse(JsonArray logElements) {\n        for (int i = 0; i < logElements.size(); i++) {\n            JsonObject entry = logElements.get(i).getAsJsonObject();\n            for (Object key : entry.keySet()) {\n                log.debug(\"%s: %s\", key, entry.get(key.toString()));\n            }\n        }\n    }\n\n    private static boolean isSSL(String url) {\n        return url != null && url.toLowerCase().startsWith(\"https\");\n    }\n\n    // Preparation for performing requests\n    private static class HcChunkedResponseHandlerWrapper implements ResponseHandler<Object> {\n\n        private EntityStreamReaderUtil.JsonEntityResponseHandler handler;\n\n        HcChunkedResponseHandlerWrapper(EntityStreamReaderUtil.JsonEntityResponseHandler handler) {\n            this.handler = handler;\n        }\n\n        @Override\n        public Object handleResponse(HttpResponse response) throws IOException {\n            try (InputStream stream = response.getEntity().getContent()) {\n                // Parse text as json\n                EntityStreamReaderUtil.processJsonStream(handler, stream);\n            }\n            return null;\n        }\n    }\n\n    public String fetchApiVersionFromServer(String baseUrl, ApacheHttpClientDelegate delegate) throws IOException {\n        HttpGet get = new HttpGet(baseUrl + (baseUrl.endsWith(\"/\") ? \"\" : \"/\") + \"version\");\n        get.addHeader(HttpHeaders.ACCEPT, \"*/*\");\n        get.addHeader(HttpHeaders.CONTENT_TYPE, \"application/json\");\n        try (CloseableHttpResponse response = delegate.getHttpClient().execute(get)) {\n\n            return response.getFirstHeader(\"Api-Version\") != null ? response.getFirstHeader(\"Api-Version\").getValue() : API_VERSION;\n        }\n    }\n\n    @FunctionalInterface\n    private interface TemporaryImageHandler {\n        void handle() throws DockerAccessException;\n\n        default void handle(@Nullable DockerAccessException interruptingError) throws DockerAccessException {\n            handle();\n\n            if (interruptingError == null) {\n                return;\n            }\n            throw interruptingError;\n        }\n    }\n\n    private final class RemovingTemporaryImageHandler implements TemporaryImageHandler {\n\t\tprivate final String targetImage;\n\n\t\tprivate RemovingTemporaryImageHandler(String targetImage) {\n            this.targetImage = targetImage;\n        }\n\n\t\t@Override\n\t\tpublic void handle() throws DockerAccessException {\n\t\t\tboolean imageRemoved = removeImage(targetImage, true);\n\t\t\tif (imageRemoved) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new DockerAccessException(\n\t\t\t\t\"Image %s could be pushed, but the temporary tag could not be removed\",\n\t\t\t\ttargetImage\n\t\t\t);\n\t\t}\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/http/HttpClientBuilder.java",
    "content": "package io.fabric8.maven.docker.access.hc.http;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyStore;\n\nimport javax.net.ssl.SSLContext;\n\nimport io.fabric8.maven.docker.access.hc.util.ClientBuilder;\nimport org.apache.http.config.Registry;\nimport org.apache.http.config.RegistryBuilder;\nimport org.apache.http.conn.HttpClientConnectionManager;\nimport org.apache.http.conn.socket.ConnectionSocketFactory;\nimport org.apache.http.conn.ssl.NoopHostnameVerifier;\nimport org.apache.http.conn.ssl.SSLConnectionSocketFactory;\nimport org.apache.http.impl.client.*;\nimport org.apache.http.impl.conn.BasicHttpClientConnectionManager;\nimport org.apache.http.impl.conn.PoolingHttpClientConnectionManager;\nimport io.fabric8.maven.docker.access.KeyStoreUtil;\nimport org.apache.http.ssl.SSLContexts;\n\n/**\n * @author roland\n * @since 05/06/15\n */\npublic class HttpClientBuilder implements ClientBuilder {\n\n    private final String certPath;\n    private final int maxConnections;\n\n    public HttpClientBuilder(String certPath, int maxConnections) {\n        this.certPath = certPath;\n        this.maxConnections = maxConnections;\n    }\n\n    public CloseableHttpClient buildPooledClient() throws IOException {\n        org.apache.http.impl.client.HttpClientBuilder builder = HttpClients.custom();\n        HttpClientConnectionManager manager = getPooledConnectionFactory(certPath, maxConnections);\n        builder.setConnectionManager(manager);\n        // TODO: For push-redirects working for 301, the redirect strategy should be relaxed (see #351)\n        // However not sure whether we should do it right now and whether this is correct, since normally\n        // a 301 should only occur when the image name is invalid (e.g. containing \"//\" in which case a redirect\n        // happens to the URL with a single \"/\")\n        // builder.setRedirectStrategy(new LaxRedirectStrategy());\n\n        // TODO: Tune client if needed (e.g. add pooling factoring .....\n        // But I think, that's not really required.\n\n        return builder.build();\n    }\n\n    public CloseableHttpClient buildBasicClient() throws IOException {\n        return HttpClients.custom().setConnectionManager(getBasicConnectionFactory(certPath)).build();\n    }\n\n    private static HttpClientConnectionManager getPooledConnectionFactory(String certPath, int maxConnections) throws IOException {\n        PoolingHttpClientConnectionManager ret =  certPath != null ?\n                new PoolingHttpClientConnectionManager(getSslFactoryRegistry(certPath)) :\n                new PoolingHttpClientConnectionManager();\n        ret.setDefaultMaxPerRoute(maxConnections);\n        ret.setMaxTotal(maxConnections);\n        return ret;\n    }\n\n    private static HttpClientConnectionManager getBasicConnectionFactory(String certPath) throws IOException {\n        return certPath != null ?\n            new BasicHttpClientConnectionManager(getSslFactoryRegistry(certPath)) :\n            new BasicHttpClientConnectionManager();\n    }\n\n    private static Registry<ConnectionSocketFactory> getSslFactoryRegistry(String certPath) throws IOException {\n        try\n        {\n            KeyStore keyStore = KeyStoreUtil.createDockerKeyStore(certPath);\n\n            SSLContext sslContext =\n                    SSLContexts.custom()\n                               .setProtocol(SSLConnectionSocketFactory.TLS)\n                               .loadKeyMaterial(keyStore, \"docker\".toCharArray())\n                               .loadTrustMaterial(keyStore, null)\n                               .build();\n            String tlsVerify = System.getenv(\"DOCKER_TLS_VERIFY\");\n            SSLConnectionSocketFactory sslsf =\n                    tlsVerify != null && !tlsVerify.equals(\"0\") && !tlsVerify.equals(\"false\") ?\n                            new SSLConnectionSocketFactory(sslContext) :\n                            new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);\n\n            return RegistryBuilder.<ConnectionSocketFactory> create().register(\"https\", sslsf).build();\n        }\n        catch (GeneralSecurityException e) {\n            // this isn't ideal but the net effect is the same\n            throw new IOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/http/HttpRequestException.java",
    "content": "package io.fabric8.maven.docker.access.hc.http;\n\nimport java.io.IOException;\n\npublic class HttpRequestException extends IOException {\n\n    public HttpRequestException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/unix/UnixConnectionSocketFactory.java",
    "content": "package io.fabric8.maven.docker.access.hc.unix;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.Socket;\nimport java.net.SocketAddress;\n\nimport io.fabric8.maven.docker.access.hc.util.AbstractNativeSocketFactory;\nimport jnr.unixsocket.UnixSocketAddress;\nimport org.apache.http.protocol.HttpContext;\n\nfinal class UnixConnectionSocketFactory extends AbstractNativeSocketFactory {\n\n    UnixConnectionSocketFactory(String unixSocketPath) {\n        super(unixSocketPath);\n    }\n\n    @Override\n    public Socket createSocket(HttpContext context) throws IOException {\n        return new UnixSocket();\n    }\n\n    @Override\n    protected SocketAddress createSocketAddress(String path) {\n        return new UnixSocketAddress(new File(path));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/unix/UnixSocket.java",
    "content": "package io.fabric8.maven.docker.access.hc.unix;\n\nimport java.io.FilterInputStream;\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport java.net.SocketAddress;\nimport java.net.SocketException;\n\nimport java.nio.channels.Channels;\nimport java.nio.channels.SocketChannel;\n\nimport jnr.unixsocket.UnixSocketAddress;\nimport jnr.unixsocket.UnixSocketChannel;\nimport jnr.unixsocket.UnixSocketOptions;\n\nfinal class UnixSocket extends Socket {\n\n    private final Object connectLock = new Object();\n    private volatile boolean inputShutdown, outputShutdown;\n\n    private final UnixSocketChannel channel;\n\n    UnixSocket() throws IOException {\n        channel = UnixSocketChannel.open();\n    }\n\n    @Override\n    public void connect(SocketAddress endpoint) throws IOException {\n        connect(endpoint, 0);\n    }\n\n    @Override\n    public void connect(SocketAddress endpoint, int timeout) throws IOException {\n        if (timeout < 0) {\n            throw new IllegalArgumentException(\"Timeout may not be negative: \" + timeout);\n        }\n\n        if (!(endpoint instanceof UnixSocketAddress)) {\n            throw new IllegalArgumentException(\"Unsupported address type: \" + endpoint.getClass().getName());\n        }\n\n        synchronized (connectLock) {\n            channel.connect((UnixSocketAddress) endpoint);\n        }\n    }\n\n    @Override\n    public void bind(SocketAddress bindpoint) throws IOException {\n        throw new SocketException(\"Bind is not supported\");\n    }\n\n    @Override\n    public InetAddress getInetAddress() {\n        return null;\n    }\n\n    @Override\n    public InetAddress getLocalAddress() {\n        return null;\n    }\n\n    @Override\n    public int getPort() {\n        return -1;\n    }\n\n    @Override\n    public int getLocalPort() {\n        return -1;\n    }\n\n    @Override\n    public SocketAddress getRemoteSocketAddress() {\n        synchronized (connectLock) {\n            return channel.getRemoteSocketAddress();\n        }\n    }\n\n    @Override\n    public SocketAddress getLocalSocketAddress() {\n        synchronized (connectLock) {\n            return channel.getLocalSocketAddress();\n        }\n    }\n\n    @Override\n    public SocketChannel getChannel() {\n        return null;\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        if (!channel.isConnected()) {\n            throw new SocketException(\"Socket is not connected\");\n        }\n\n        if (inputShutdown) {\n            throw new SocketException(\"Socket input is shutdown\");\n        }\n\n        return new FilterInputStream(Channels.newInputStream(channel)) {\n            @Override\n            public void close() throws IOException {\n                shutdownInput();\n            }\n        };\n    }\n\n    @Override\n    public OutputStream getOutputStream() throws IOException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        if (!channel.isConnected()) {\n            throw new SocketException(\"Socket is not connected\");\n        }\n\n        if (outputShutdown) {\n            throw new SocketException(\"Socket output is shutdown\");\n        }\n\n        return new FilterOutputStream(Channels.newOutputStream(channel)) {\n            @Override\n            public void write(byte[] b, int off, int len) throws IOException {\n                out.write(b, off, len);\n            }\n\n            @Override\n            public void close() throws IOException {\n                shutdownOutput();\n            }\n        };\n    }\n\n    @Override\n    public void sendUrgentData(int data) throws IOException {\n        throw new SocketException(\"Urgent data not supported\");\n    }\n    @Override\n    public void setSoTimeout(int timeout) throws SocketException {\n        try {\n            channel.setOption(UnixSocketOptions.SO_RCVTIMEO, Integer.valueOf(timeout));\n        } catch (IOException e) {\n            throw (SocketException)new SocketException().initCause(e);\n        }\n    }\n\n    @Override\n    public int getSoTimeout() throws SocketException {\n        try {\n            return channel.getOption(UnixSocketOptions.SO_RCVTIMEO).intValue();\n        } catch (IOException e) {\n            throw (SocketException)new SocketException().initCause(e);\n        }\n    }\n\n    @Override\n    public void setSendBufferSize(int size) throws SocketException {\n        if (size <= 0) {\n            throw new IllegalArgumentException(\"Send buffer size must be positive: \" + size);\n        }\n\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public synchronized int getSendBufferSize() throws SocketException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        throw new UnsupportedOperationException(\"Getting the send buffer size is not supported\");\n    }\n\n    @Override\n    public synchronized void setReceiveBufferSize(int size) throws SocketException {\n        if (size <= 0) {\n            throw new IllegalArgumentException(\"Receive buffer size must be positive: \" + size);\n        }\n\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public synchronized int getReceiveBufferSize() throws SocketException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        throw new UnsupportedOperationException(\"Getting the receive buffer size is not supported\");\n    }\n\n    @Override\n    public void setKeepAlive(boolean on) throws SocketException {\n        try {\n            channel.setOption(UnixSocketOptions.SO_KEEPALIVE, Boolean.valueOf(on));\n        } catch (IOException e) {\n            throw (SocketException)new SocketException().initCause(e);\n        }\n    }\n\n    @Override\n    public boolean getKeepAlive() throws SocketException {\n        try {\n            return channel.getOption(UnixSocketOptions.SO_KEEPALIVE).booleanValue();\n        } catch (IOException e) {\n            throw (SocketException)new SocketException().initCause(e);\n        }\n    }\n\n    @Override\n    public void setTrafficClass(int tc) throws SocketException {\n        if (tc < 0 || tc > 255) {\n            throw new IllegalArgumentException(\"Traffic class is not in range 0 -- 255: \" + tc);\n        }\n\n        if (isClosed()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public int getTrafficClass() throws SocketException {\n        throw new UnsupportedOperationException(\"Getting the traffic class is not supported\");\n    }\n\n    @Override\n    public void setReuseAddress(boolean on) throws SocketException {\n        if (isClosed()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public boolean getReuseAddress() throws SocketException {\n        throw new UnsupportedOperationException(\"Getting the SO_REUSEADDR option is not supported\");\n    }\n\n    @Override\n    public void close() throws IOException {\n        channel.close();\n        inputShutdown = true;\n        outputShutdown = true;\n    }\n\n    @Override\n    public void shutdownInput() throws IOException {\n        channel.shutdownInput();\n        inputShutdown = true;\n    }\n\n    @Override\n    public void shutdownOutput() throws IOException {\n        channel.shutdownOutput();\n        outputShutdown = true;\n    }\n\n    @Override\n    public String toString() {\n        if (isConnected()) {\n            return \"UnixSocket[addr=\" + channel.getRemoteSocketAddress() + ']';\n        }\n\n        return \"UnixSocket[unconnected]\";\n    }\n\n    @Override\n    public boolean isConnected() {\n        return channel.isConnected();\n    }\n\n    @Override\n    public boolean isBound() {\n        return false;\n    }\n\n    @Override\n    public boolean isClosed() {\n        return !channel.isOpen();\n    }\n\n    @Override\n    public boolean isInputShutdown() {\n        return inputShutdown;\n    }\n\n    @Override\n    public boolean isOutputShutdown() {\n        return outputShutdown;\n    }\n\n    @Override\n    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {\n        // no-op\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/unix/UnixSocketClientBuilder.java",
    "content": "package io.fabric8.maven.docker.access.hc.unix;\n\nimport io.fabric8.maven.docker.access.hc.util.AbstractNativeClientBuilder;\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.http.conn.socket.ConnectionSocketFactory;\n\npublic class UnixSocketClientBuilder extends AbstractNativeClientBuilder {\n\n    public UnixSocketClientBuilder(String unixSocketPath, int maxConnections, Logger log) {\n        super(unixSocketPath, maxConnections, log);\n    }\n\n    @Override\n    protected ConnectionSocketFactory getConnectionSocketFactory() {\n        return new UnixConnectionSocketFactory(path);\n    }\n\n    @Override\n    protected String getProtocol() {\n        return \"unix\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/util/AbstractNativeClientBuilder.java",
    "content": "package io.fabric8.maven.docker.access.hc.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\n\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.http.config.Registry;\nimport org.apache.http.config.RegistryBuilder;\nimport org.apache.http.conn.DnsResolver;\nimport org.apache.http.conn.socket.ConnectionSocketFactory;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.impl.conn.BasicHttpClientConnectionManager;\nimport org.apache.http.impl.conn.PoolingHttpClientConnectionManager;\n\n/**\n * Base class for all clients which access Docker natively\n *\n * @author roland\n * @since 08/08/16\n */\nabstract public class AbstractNativeClientBuilder implements ClientBuilder {\n\n    protected final Registry<ConnectionSocketFactory> registry;\n    protected final String path;\n    protected final Logger log;\n\n    private final DnsResolver dnsResolver;\n    private final int maxConnections;\n\n    public AbstractNativeClientBuilder(String path, int maxConnections, Logger logger) {\n        this.maxConnections = maxConnections;\n        this.log = logger;\n        this.path = path;\n        dnsResolver = nullDnsResolver();\n        registry = buildRegistry(path);\n    }\n\n    protected abstract ConnectionSocketFactory getConnectionSocketFactory();\n    protected abstract String getProtocol();\n\n    @Override\n    public CloseableHttpClient buildPooledClient() {\n        final HttpClientBuilder httpBuilder = HttpClients.custom();\n        final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry, dnsResolver);\n        manager.setDefaultMaxPerRoute(maxConnections);\n        httpBuilder.setConnectionManager(manager);\n        return httpBuilder.build();\n    }\n\n    @Override\n    public CloseableHttpClient buildBasicClient() throws IOException {\n        BasicHttpClientConnectionManager manager = new BasicHttpClientConnectionManager(registry, null, null, dnsResolver);\n        return HttpClients.custom().setConnectionManager(manager).build();\n    }\n\n    // =========================================================================================================\n\n    private Registry<ConnectionSocketFactory> buildRegistry(String path) {\n        final RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();\n        registryBuilder.register(getProtocol(), getConnectionSocketFactory());\n        return registryBuilder.build();\n    }\n\n    private  DnsResolver nullDnsResolver() {\n        return new DnsResolver() {\n            @Override\n            public InetAddress[] resolve(final String host) throws UnknownHostException {\n                return new InetAddress[] {null};\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/util/AbstractNativeSocketFactory.java",
    "content": "package io.fabric8.maven.docker.access.hc.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.net.SocketAddress;\n\nimport org.apache.http.HttpHost;\nimport org.apache.http.conn.socket.ConnectionSocketFactory;\nimport org.apache.http.protocol.HttpContext;\n\n/**\n * @author roland\n * @since 08/08/16\n */\nabstract public class AbstractNativeSocketFactory implements ConnectionSocketFactory {\n\n    final private String path;\n\n    public AbstractNativeSocketFactory(String path) {\n        this.path = path;\n    }\n\n    @Override\n    public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {\n        sock.connect(createSocketAddress(path), connectTimeout);\n        return sock;\n    }\n\n    protected abstract SocketAddress createSocketAddress(String path);\n    public abstract Socket createSocket(HttpContext context) throws IOException;\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/util/ClientBuilder.java",
    "content": "package io.fabric8.maven.docker.access.hc.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.IOException;\n\nimport org.apache.http.impl.client.CloseableHttpClient;\n\n/**\n * A client builder know how to build HTTP clients\n *\n * @author roland\n * @since 03/05/16\n */\npublic interface ClientBuilder {\n\n    /**\n     * Create a pooled client\n     *\n     * @return an HTTP client\n     * @throws IOException\n     */\n    CloseableHttpClient buildPooledClient() throws IOException;\n\n    /**\n     * Create a basic client with a single connection. This is the client which should be used\n     * in long running threads\n     *\n     * @return an HTTP client\n     * @throws IOException\n     */\n    CloseableHttpClient buildBasicClient() throws IOException;\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/win/NamedPipe.java",
    "content": "package io.fabric8.maven.docker.access.hc.win;\n\nimport java.io.FilterInputStream;\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.RandomAccessFile;\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport java.net.SocketAddress;\nimport java.net.SocketException;\nimport java.nio.channels.Channels;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.SocketChannel;\nimport java.nio.charset.Charset;\n\nimport io.fabric8.maven.docker.util.Logger;\n\nimport static com.google.common.base.CharMatcher.ascii;\n\nfinal class NamedPipe extends Socket {\n\n\t// Logging\n    private final Logger log;\n\t//for development purposes\n\tprivate final static boolean DEBUG = false;\n\n    private final Object connectLock = new Object();\n    private volatile boolean inputShutdown, outputShutdown;\n\n    private String socketPath;\n    private volatile SocketAddress socketAddress;\n    private RandomAccessFile namedPipe;\n\n    private FileChannel channel;\n\n    NamedPipe(Logger log) throws IOException {\n    \tthis.log = log;\n    }\n\n    @Override\n    public void connect(SocketAddress endpoint) throws IOException {\n        connect(endpoint, 0);\n    }\n\n    @Override\n    public void connect(SocketAddress endpoint, int timeout) throws IOException {\n        if (timeout < 0) {\n            throw new IllegalArgumentException(\"Timeout may not be negative: \" + timeout);\n        }\n\n        if (!(endpoint instanceof NpipeSocketAddress)) {\n            throw new IllegalArgumentException(\"Unsupported address type: \" + endpoint.getClass().getName());\n        }\n\n        this.socketAddress = endpoint;\n        this.socketPath = ((NpipeSocketAddress) endpoint).path();\n\n        synchronized (connectLock) {\n        \tthis.namedPipe = new RandomAccessFile(socketPath, \"rw\");\n        \tthis.channel = this.namedPipe.getChannel();\n        }\n    }\n\n    @Override\n    public void bind(SocketAddress bindpoint) throws IOException {\n        throw new SocketException(\"Bind is not supported\");\n    }\n\n    @Override\n    public InetAddress getInetAddress() {\n        return null;\n    }\n\n    @Override\n    public InetAddress getLocalAddress() {\n        return null;\n    }\n\n    @Override\n    public int getPort() {\n        return -1;\n    }\n\n    @Override\n    public int getLocalPort() {\n        return -1;\n    }\n\n    @Override\n    public SocketAddress getRemoteSocketAddress() {\n        return socketAddress;\n    }\n\n    @Override\n    public SocketAddress getLocalSocketAddress() {\n        return null;\n    }\n\n    @Override\n    public SocketChannel getChannel() {\n        return null;\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        if (inputShutdown) {\n            throw new SocketException(\"Socket input is shutdown\");\n        }\n\n        return new FilterInputStream(Channels.newInputStream(channel)) {\n\n        \t@Override\n        \tpublic int read(byte[] b, int off, int len) throws IOException {\n        \t\tint readed = super.read(b, off, len);\n                log.debug(\"RESPONSE %s\", new String(b, off, len, Charset.forName(\"UTF-8\")));\n                return readed;\n        \t}\n\n            @Override\n            public void close() throws IOException {\n                shutdownInput();\n            }\n        };\n    }\n\n    @Override\n    public OutputStream getOutputStream() throws IOException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        if (outputShutdown) {\n            throw new SocketException(\"Socket output is shutdown\");\n        }\n\n        return new FilterOutputStream(Channels.newOutputStream(channel)) {\n            @Override\n            public void write(byte[] b, int off, int len) throws IOException {\n                if(log.isDebugEnabled()){\n                    String request = new String(b, off, len, Charset.forName(\"UTF-8\"));\n                    String logValue = ascii().matchesAllOf(request) ? request : \"not logged due to non-ASCII characters. \";\n                    log.debug(\"REQUEST %s\", logValue);\n                }\n                out.write(b, off, len);\n            }\n\n            @Override\n            public void close() throws IOException {\n                shutdownOutput();\n            }\n        };\n    }\n\n    @Override\n    public void sendUrgentData(int data) throws IOException {\n        throw new SocketException(\"Urgent data not supported\");\n    }\n\n    @Override\n    public void setSoTimeout(int timeout) {\n    }\n\n    @Override\n    public int getSoTimeout() throws SocketException {\n    \treturn 0;\n    }\n\n    @Override\n    public void setSendBufferSize(int size) throws SocketException {\n        if (size <= 0) {\n            throw new IllegalArgumentException(\"Send buffer size must be positive: \" + size);\n        }\n\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public synchronized int getSendBufferSize() throws SocketException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        throw new UnsupportedOperationException(\"Getting the send buffer size is not supported\");\n    }\n\n    @Override\n    public synchronized void setReceiveBufferSize(int size) throws SocketException {\n        if (size <= 0) {\n            throw new IllegalArgumentException(\"Receive buffer size must be positive: \" + size);\n        }\n\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public synchronized int getReceiveBufferSize() throws SocketException {\n        if (!channel.isOpen()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        throw new UnsupportedOperationException(\"Getting the receive buffer size is not supported\");\n    }\n\n    @Override\n    public void setKeepAlive(boolean on) throws SocketException {\n    }\n\n    @Override\n    public boolean getKeepAlive() throws SocketException {\n    \treturn true;\n    }\n\n    @Override\n    public void setTrafficClass(int tc) throws SocketException {\n        if (tc < 0 || tc > 255) {\n            throw new IllegalArgumentException(\"Traffic class is not in range 0 -- 255: \" + tc);\n        }\n\n        if (isClosed()) {\n            throw new SocketException(\"Socket is closed\");\n        }\n\n        // just ignore\n    }\n\n    @Override\n    public int getTrafficClass() throws SocketException {\n        throw new UnsupportedOperationException(\"Getting the traffic class is not supported\");\n    }\n\n    @Override\n    public void setReuseAddress(boolean on) throws SocketException {\n        // just ignore\n    }\n\n    @Override\n    public boolean getReuseAddress() throws SocketException {\n        throw new UnsupportedOperationException(\"Getting the SO_REUSEADDR option is not supported\");\n    }\n\n    @Override\n    public void close() throws IOException {\n        if (isClosed()) {\n            return;\n        }\n        if (channel != null) {\n            channel.close();\n        }\n        inputShutdown = true;\n        outputShutdown = true;\n    }\n\n    @Override\n    public void shutdownInput() throws IOException {\n        inputShutdown = true;\n    }\n\n    @Override\n    public void shutdownOutput() throws IOException {\n        outputShutdown = true;\n    }\n\n    @Override\n    public String toString() {\n        if (isConnected()) {\n            return \"WindowsPipe[addr=\" + this.socketPath + ']';\n        }\n\n        return \"WindowsPipe[unconnected]\";\n    }\n\n    @Override\n    public boolean isConnected() {\n        return !isClosed();\n    }\n\n    @Override\n    public boolean isBound() {\n        return false;\n    }\n\n    @Override\n    public boolean isClosed() {\n        return channel != null && !channel.isOpen();\n    }\n\n    @Override\n    public boolean isInputShutdown() {\n        return inputShutdown;\n    }\n\n    @Override\n    public boolean isOutputShutdown() {\n        return outputShutdown;\n    }\n\n    @Override\n    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {\n        // no-op\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/win/NamedPipeClientBuilder.java",
    "content": "package io.fabric8.maven.docker.access.hc.win;\n\nimport io.fabric8.maven.docker.access.hc.util.AbstractNativeClientBuilder;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport org.apache.http.conn.socket.ConnectionSocketFactory;\n\npublic class NamedPipeClientBuilder extends AbstractNativeClientBuilder {\n    public NamedPipeClientBuilder(String namedPipePath, int maxConnections, Logger log) {\n        super(namedPipePath, maxConnections, log);\n    }\n\n    @Override\n    protected ConnectionSocketFactory getConnectionSocketFactory() {\n        return new NpipeConnectionSocketFactory(path, log);\n    }\n\n    @Override\n    protected String getProtocol() {\n        return \"npipe\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/win/NpipeConnectionSocketFactory.java",
    "content": "package io.fabric8.maven.docker.access.hc.win;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.Socket;\nimport java.net.SocketAddress;\n\nimport io.fabric8.maven.docker.access.hc.util.AbstractNativeSocketFactory;\nimport org.apache.http.protocol.HttpContext;\n\nimport io.fabric8.maven.docker.util.Logger;\n\nfinal class NpipeConnectionSocketFactory extends AbstractNativeSocketFactory {\n\n\t// Logging\n    private final Logger log;\n\n    NpipeConnectionSocketFactory(String npipePath, Logger log) {\n        super(npipePath);\n        this.log = log;\n    }\n\n    @Override\n    public Socket createSocket(HttpContext context) throws IOException {\n        return new NamedPipe(log);\n    }\n\n    @Override\n    protected SocketAddress createSocketAddress(String path) {\n        return new NpipeSocketAddress(new File(path));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/hc/win/NpipeSocketAddress.java",
    "content": "package io.fabric8.maven.docker.access.hc.win;\n\nimport java.io.File;\n\nclass NpipeSocketAddress extends java.net.SocketAddress {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate String path;\n\n    NpipeSocketAddress(File path) {\n        this.path = path.getPath();\n    }\n\n    public String path() {\n        return path;\n    }\n\n    @Override\n    public String toString() {\n        return \"NpipeSocketAddress{path=\" + path + \"}\";\n    }\n\n    @Override\n    public boolean equals(Object _other) {\n        return _other instanceof NpipeSocketAddress && path.equals(((NpipeSocketAddress) _other).path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/log/LogCallback.java",
    "content": "package io.fabric8.maven.docker.access.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.IOException;\nimport java.time.ZonedDateTime;\nimport java.util.concurrent.CancellationException;\n\n/**\n * Interface called for each log line received from the docker host when asynchronous\n * log fetching is used.\n *\n * @author roland\n * @since 21/11/14\n */\npublic interface LogCallback {\n\n    /**\n     * Receive a log entry\n     * @param type 1 for log on standard output, 2 for standard error\n     * @param timestamp timestampp on the server side when this entry happened\n     * @param txt log output\n     * @throws CancellationException if thrown will stop the logging.\n     */\n    void log(int type, ZonedDateTime timestamp, String txt) throws DoneException;\n\n    /**\n     * Method called in case on an error when reading the logs\n     * @param error error description\n     */\n    void error(String error);\n\n    /**\n     * To be called by a client to start the callback and allocate the underlying output stream.\n     * In case of a shared stream it might be that the stream is reused\n     */\n    void open() throws IOException;\n\n    /**\n     * Invoked by a user when callback is no longer used.\n     * Closing the underlying stream may be delayed by the implementation\n     * when this stream is shared between multiple clients.\n     */\n    void close();\n\n    /**\n     * Exception indicating that logging is done and should be finished\n     */\n    class DoneException extends Exception {}\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/log/LogGetHandle.java",
    "content": "package io.fabric8.maven.docker.access.log;/*\n * \n * Copyright 2014 Roland Huss\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\nimport io.fabric8.maven.docker.access.DockerAccessException;\n\n/**\n * @author roland\n * @since 29/11/14\n */\npublic interface LogGetHandle {\n\n    void finish();\n\n    boolean isError();\n\n    DockerAccessException getException();\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/log/LogRequestor.java",
    "content": "package io.fabric8.maven.docker.access.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.time.ZonedDateTime;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.base.Charsets;\nimport com.google.common.io.ByteStreams;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.UrlBuilder;\nimport io.fabric8.maven.docker.access.util.RequestUtil;\nimport io.fabric8.maven.docker.util.TimestampFactory;\nimport org.apache.commons.codec.binary.Hex;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.impl.client.CloseableHttpClient;\n\n/**\n * Extractor for parsing the response of a log request\n *\n * @author roland\n * @since 28/11/14\n */\npublic class LogRequestor extends Thread implements LogGetHandle {\n    // Patter for matching log entries\n    static final Pattern LOG_LINE = Pattern.compile(\"^\\\\[?(?<timestamp>[^\\\\s\\\\]]*)]? (?<entry>.*?)\\\\s*$\", Pattern.DOTALL);\n    private final CloseableHttpClient client;\n\n    private final String containerId;\n\n    // callback called for each line extracted\n    private LogCallback callback;\n\n    private DockerAccessException exception;\n\n    // Remember for asynchronous handling so that the request can be aborted from the outside\n    private HttpUriRequest request;\n\n    private final UrlBuilder urlBuilder;\n\n    /**\n     * Create a helper object for requesting log entries synchronously ({@link #fetchLogs()}) or asynchronously ({@link #start()}.\n     *\n     * @param client HTTP client to use for requesting the docker host\n     * @param urlBuilder builder that creates docker urls\n     * @param containerId container for which to fetch the host\n     * @param callback callback to call for each line received\n     */\n    public LogRequestor(CloseableHttpClient client, UrlBuilder urlBuilder, String containerId, LogCallback callback) {\n        this.client = client;\n        this.containerId = containerId;\n\n        this.urlBuilder = urlBuilder;\n\n        this.callback = callback;\n        this.exception = null;\n    }\n\n    /**\n     * Get logs and feed a callback with the content\n     */\n    public void fetchLogs() {\n        try {\n            callback.open();\n            this.request = getLogRequest(false);\n            final HttpResponse response = client.execute(request);\n            parseResponse(response);\n        } catch (LogCallback.DoneException e) {\n            // Signifies we're finished with the log stream.\n        } catch (IOException exp) {\n            callback.error(exp.getMessage());\n        } finally {\n            callback.close();\n        }\n    }\n\n    // Fetch log asynchronously as stream and follow stream\n    public void run() {\n        try {\n            callback.open();\n            this.request = getLogRequest(true);\n            final HttpResponse response = client.execute(request);\n            parseResponse(response);\n        } catch (LogCallback.DoneException e) {\n            // Signifies we're finished with the log stream.\n        } catch (IOException e) {\n            callback.error(\"IO Error while requesting logs: \" + e + \" \" + Thread.currentThread().getName());\n        } finally {\n            callback.close();\n        }\n    }\n\n    private static class NoBytesReadException extends IOException {\n        public NoBytesReadException() {\n        }\n    }\n\n    /**\n     * This is a copy of ByteStreams.readFully(), with the slight change that it throws\n     * NoBytesReadException if zero bytes are read. Otherwise it is identical.\n     *\n     * @param in\n     * @param bytes\n     * @throws IOException\n     */\n    private void readFully(InputStream in, byte[] bytes) throws IOException {\n        int read = ByteStreams.read(in, bytes, 0, bytes.length);\n        if (read == 0) {\n            throw new NoBytesReadException();\n        } else if (read != bytes.length) {\n            throw new EOFException(\"reached end of stream after reading \"\n                    + read + \" bytes; \" + bytes.length + \" bytes expected\");\n        }\n    }\n\n    private boolean readStreamFrame(InputStream is) throws IOException, LogCallback.DoneException {\n        // Read the header, which is composed of eight bytes. The first byte is an integer\n        // indicating the stream type (0 = stdin, 1 = stdout, 2 = stderr), the next three are thrown\n        // out, and the final four are the size of the remaining stream as an integer.\n        ByteBuffer headerBuffer = ByteBuffer.allocate(8);\n        headerBuffer.order(ByteOrder.BIG_ENDIAN);\n        try {\n            this.readFully(is, headerBuffer.array());\n        } catch (NoBytesReadException e) {\n            // Not bytes read for stream. Return false to stop consuming stream.\n            return false;\n        } catch (EOFException e) {\n            throw new IOException(\"Failed to read log header. Could not read all 8 bytes. \" + e.getMessage(), e);\n        }\n\n        // Grab the stream type (stdout, stderr, stdin) from first byte and throw away other 3 bytes.\n        int type = headerBuffer.get();\n\n        // Skip three bytes, then read size from remaining four bytes.\n        int size = headerBuffer.getInt(4);\n\n        // Ignore empty messages and keep reading.\n        if (size <= 0) {\n            return true;\n        }\n\n        // Read the actual message\n        ByteBuffer payload = ByteBuffer.allocate(size);\n        try {\n            ByteStreams.readFully(is, payload.array());\n        } catch (EOFException e) {\n            throw new IOException(\"Failed to read log message. Could not read all \" + size + \" bytes. \" + e.getMessage() +\n                                  \" [ Header: \" + Hex.encodeHexString(headerBuffer.array()) + \"]\", e);\n        }\n\n        String message = Charsets.UTF_8.newDecoder().decode(payload).toString();\n        callLogCallback(type, message);\n        return true;\n    }\n\n    private void parseResponse(HttpResponse response) throws LogCallback.DoneException, IOException {\n        final StatusLine status = response.getStatusLine();\n        if (status.getStatusCode() != 200) {\n            exception = new DockerAccessException(\"Error while reading logs (\" + status + \")\");\n            throw new LogCallback.DoneException();\n        }\n\n        try (InputStream is = response.getEntity().getContent()) {\n            while (true) {\n                if (!readStreamFrame(is)) {\n                    return;\n                }\n            }\n        }\n    }\n\n    private void callLogCallback(int type, String txt) throws LogCallback.DoneException {\n        Matcher matcher = LOG_LINE.matcher(txt);\n        if (!matcher.matches()) {\n            callback.error(String.format(\"Invalid log format for '%s' (expected: \\\"<timestamp> <txt>\\\") [%04x %04x]\",\n                                         txt,(int) (txt.toCharArray())[0],(int) (txt.toCharArray())[1]));\n            throw new LogCallback.DoneException();\n        }\n        ZonedDateTime ts = TimestampFactory.createTimestamp(matcher.group(\"timestamp\"));\n        String logTxt = matcher.group(\"entry\");\n        callback.log(type, ts, logTxt);\n    }\n\n    private HttpUriRequest getLogRequest(boolean follow) {\n        return RequestUtil.newGet(urlBuilder.containerLogs(containerId, follow));\n    }\n\n    @Override\n    public void finish() {\n        if (request != null) {\n            request.abort();\n            request = null;\n        }\n    }\n\n    @Override\n    public boolean isError() {\n        return exception != null;\n    }\n\n    @Override\n    public DockerAccessException getException() {\n        return exception;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/util/EnvCommand.java",
    "content": "package io.fabric8.maven.docker.access.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport io.fabric8.maven.docker.util.Logger;\n\n/**\n * Command for extracting the environment information emitted by e.g. 'docker-machine env' as\n * a map.\n *\n * @since 14/09/16\n */\nabstract public class EnvCommand extends ExternalCommand {\n\n    private final Map<String, String> env = new HashMap<>();\n\n    private final String prefix;\n\n    public EnvCommand(Logger log, String prefix) {\n        super(log);\n        this.prefix = prefix;\n    }\n\n    @Override\n    protected void processLine(String line) {\n        if (log.isDebugEnabled()) {\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"%s\", line);\n        }\n        if (line.startsWith(prefix)) {\n            setEnvironmentVariable(line.substring(prefix.length()));\n        }\n    }\n\n    private final Pattern ENV_VAR_PATTERN = Pattern.compile(\"^\\\\s*(?<key>[^=]+)=\\\"?(?<value>.*?)\\\"?\\\\s*$\");\n\n    // parse line like SET DOCKER_HOST=tcp://192.168.99.100:2376\n    private void setEnvironmentVariable(String line) {\n        Matcher matcher = ENV_VAR_PATTERN.matcher(line);\n        if (matcher.matches()) {\n            String key = matcher.group(\"key\");\n            String value = matcher.group(\"value\");\n            log.debug(\"Env: %s=%s\",key,value);\n            env.put(key, value);\n        }\n    }\n\n    public Map<String, String> getEnvironment() throws IOException {\n        execute();\n        return env;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/util/ExternalCommand.java",
    "content": "package io.fabric8.maven.docker.access.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.*;\nimport java.util.concurrent.*;\n\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.maven.shared.utils.StringUtils;\n\n/**\n * @author roland\n * @since 14/09/16\n */\npublic abstract class ExternalCommand {\n    protected final Logger log;\n\n    private final ExecutorService executor = Executors.newFixedThreadPool(2);\n\n    private int statusCode;\n\n    public ExternalCommand(Logger log) {\n        this.log = log;\n    }\n\n    public void execute() throws IOException {\n        execute(null);\n    }\n\n    public void execute(String processInput) throws IOException {\n        final Process process = startProcess();\n        start();\n        try {\n            inputStreamPump(process.getOutputStream(),processInput);\n\n            Future<IOException> stderrFuture = startStreamPump(process.getErrorStream());\n            outputStreamPump(process.getInputStream());\n\n            stopStreamPump(stderrFuture);\n            checkProcessExit(process);\n        } catch (IOException e) {\n            process.destroy();\n            throw e;\n        } finally {\n            end();\n        }\n        if (statusCode != 0) {\n            throw new IOException(String.format(\"Process '%s' exited with status %d\",\n                                                getCommandAsString(), statusCode));\n        }\n\n    }\n\n    // Hooks for logging ...\n    protected void start() {}\n\n    protected void end() {}\n\n    protected int getStatusCode() {\n        return statusCode;\n    }\n\n    private void checkProcessExit(Process process) {\n        try {\n            statusCode = process.waitFor();\n            executor.shutdown();\n            executor.awaitTermination(10, TimeUnit.SECONDS);\n        } catch (IllegalThreadStateException | InterruptedException e) {\n            process.destroy();\n            statusCode = -1;\n        }\n    }\n\n    private void inputStreamPump(OutputStream outputStream,String processInput) throws IOException {\n        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {\n            if (processInput != null) {\n                writer.write(processInput);\n                writer.flush();\n            }\n        } catch (IOException e) {\n            log.info(\"Failed to close process output stream: %s\", e.getMessage());\n        }\n    }\n\n    private Process startProcess() throws IOException {\n        try {\n            return Runtime.getRuntime().exec(getArgs());\n        } catch (IOException e) {\n            throw new IOException(String.format(\"Failed to start '%s' : %s\",\n                                                getCommandAsString(),\n                                                e.getMessage()), e);\n        }\n    }\n\n    protected String getCommandAsString() {\n        return StringUtils.join(getArgs(), \" \");\n    }\n\n    protected abstract String[] getArgs();\n\n    private void outputStreamPump(final InputStream inputStream) throws IOException {\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {\n            for (; ; ) {\n                String line = reader.readLine();\n                if (line == null) {\n                    break;\n                }\n                processLine(line);\n            }\n        } catch (IOException e) {\n            throw new IOException(String.format(\"Failed to read process '%s' output: %s\",\n                                                getCommandAsString(),\n                                                e.getMessage()), e);\n        }\n    }\n\n    protected void processLine(String line) {\n        log.verbose(Logger.LogVerboseCategory.BUILD,line);\n    }\n\n    private Future<IOException> startStreamPump(final InputStream errorStream) {\n        return executor.submit(new Callable<IOException>() {\n            @Override\n            public IOException call() {\n                try (BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));) {\n                    for (; ; ) {\n                        String line = reader.readLine();\n                        if (line == null) {\n                            break;\n                        }\n                        synchronized (log) {\n                            log.warn(line);\n                        }\n                    }\n                    return null;\n                } catch (IOException e) {\n                    return e;\n                }\n            }\n        });\n    }\n\n    private void stopStreamPump(Future<IOException> future) throws IOException {\n        try {\n            IOException e = future.get(2, TimeUnit.SECONDS);\n            if (e != null) {\n                throw new IOException(String.format(\"Failed to read process '%s' error stream\",\n                                                    getCommandAsString()), e);\n            }\n        } catch (InterruptedException ignore) {\n            Thread.currentThread().interrupt();\n        } catch (ExecutionException | TimeoutException e) {\n            throw new IOException(String.format(\"Failed to stop process '%s' error stream\",\n                                                getCommandAsString()), e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/util/LocalSocketUtil.java",
    "content": "package io.fabric8.maven.docker.access.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.File;\nimport java.io.IOException;\n\nimport jnr.unixsocket.UnixSocketAddress;\nimport jnr.unixsocket.UnixSocketChannel;\n\n/**\n * Utilities around socket connections\n *\n * @author roland\n * @since 22/09/16\n */\npublic class LocalSocketUtil {\n\n    /**\n     * Check whether we can connect to a local Unix socket\n     *\n     */\n    public static boolean canConnectUnixSocket(File path) {\n        try (UnixSocketChannel channel = UnixSocketChannel.open()) {\n            return channel.connect(new UnixSocketAddress(path));\n        } catch (IOException e) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/access/util/RequestUtil.java",
    "content": "package io.fabric8.maven.docker.access.util;/*\n * \n * Copyright 2014 Roland Huss\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\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.Charset;\n\nimport org.apache.http.client.methods.*;\nimport org.apache.http.entity.StringEntity;\n\n/**\n * @author roland\n * @since 30/11/14\n */\npublic class RequestUtil {\n\n    public static final String HEADER_ACCEPT = \"Accept\";\n    public static final String HEADER_ACCEPT_ALL = \"*/*\";\n\n    // -----------------------\n    // Request related methods\n    public static HttpUriRequest newGet(String url) {\n        return addDefaultHeaders(new HttpGet(url));\n    }\n\n    public static HttpUriRequest newPost(String url, String body) {\n        HttpPost post = new HttpPost(url);\n        if (body != null) {\n            post.setEntity(new StringEntity(body, Charset.defaultCharset()));\n        }\n        return addDefaultHeaders(post);\n    }\n\n    public static HttpUriRequest newDelete(String url) {\n        return addDefaultHeaders(new HttpDelete(url));\n    }\n\n    public static HttpUriRequest addDefaultHeaders(HttpUriRequest req) {\n        req.addHeader(HEADER_ACCEPT, HEADER_ACCEPT_ALL);\n        req.addHeader(\"Content-Type\", \"application/json\");\n        return req;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static String encode(String param) {\n        try {\n            return URLEncoder.encode(param, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            // wont happen\n            return URLEncoder.encode(param);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/AllFilesExecCustomizer.java",
    "content": "package io.fabric8.maven.docker.assembly;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.File;\nimport java.io.IOException;\n\nimport io.fabric8.maven.docker.util.Logger;\nimport org.codehaus.plexus.archiver.ArchiveEntry;\nimport org.codehaus.plexus.archiver.ResourceIterator;\nimport org.codehaus.plexus.archiver.tar.TarArchiver;\nimport org.codehaus.plexus.archiver.tar.TarLongFileMode;\nimport org.codehaus.plexus.components.io.resources.PlexusIoResource;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * @author roland\n * @since 26/06/16\n */\nclass AllFilesExecCustomizer implements ArchiverCustomizer {\n    private Logger log;\n\n    AllFilesExecCustomizer(Logger logger) {\n        this.log = logger;\n    }\n\n    @Override\n    public TarArchiver customize(TarArchiver archiver) throws IOException {\n        log.warn(\"/--------------------- SECURITY WARNING ---------------------\\\\\");\n        log.warn(\"|You are building a Docker image with normalized permissions.|\");\n        log.warn(\"|All files and directories added to build context will have  |\");\n        log.warn(\"|'-rwxr-xr-x' permissions. It is recommended to double check |\");\n        log.warn(\"|and reset permissions for sensitive files and directories.  |\");\n        log.warn(\"\\\\------------------------------------------------------------/\");\n\n        TarArchiver newArchiver = new TarArchiver();\n        newArchiver.setDestFile(archiver.getDestFile());\n        newArchiver.setLongfile(TarLongFileMode.posix);\n\n        ResourceIterator resources = archiver.getResources();\n        while (resources.hasNext()) {\n            ArchiveEntry ae = resources.next();\n            String fileName = ae.getName();\n            PlexusIoResource resource = ae.getResource();\n            String name = StringUtils.replace(fileName, File.separatorChar, '/');\n\n            // See docker source:\n            // https://github.com/docker/docker/blob/3d13fddd2bc4d679f0eaa68b0be877e5a816ad53/pkg/archive/archive_windows.go#L45\n            int mode = ae.getMode() & 0777;\n            int newMode = mode;\n            newMode &= 0755;\n            newMode |= 0111;\n\n            if (newMode != mode) {\n                log.debug(\"Changing permissions of '%s' from %o to %o.\", name, mode, newMode);\n            }\n\n            newArchiver.addResource(resource, name, newMode);\n        }\n\n        archiver = newArchiver;\n\n        return archiver;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/ArchiverCustomizer.java",
    "content": "/*\n * Copyright 2016 Red Hat, Inc.\n *\n * Red Hat licenses this file to you under the Apache License, version\n * 2.0 (the \"License\"); you may not use this file except in compliance\n * with the License.  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\n * implied.  See the License for the specific language governing\n * permissions and limitations under the License.\n */\npackage io.fabric8.maven.docker.assembly;\n\nimport java.io.IOException;\n\nimport org.codehaus.plexus.archiver.tar.TarArchiver;\n\n/**\n * Archiver used to adapt for customizations.\n *\n * @author nicola\n * @since 04/08/2017\n */\npublic interface ArchiverCustomizer {\n    TarArchiver customize(TarArchiver archiver) throws IOException;\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/AssemblyFiles.java",
    "content": "package io.fabric8.maven.docker.assembly;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Collection of assembly files which need to be monitored for checking when\n * to rebuild an image.\n *\n * @author roland\n * @since 15/06/15\n */\npublic class AssemblyFiles {\n\n    private final File assemblyDirectory;\n    private List<Entry> entries = new ArrayList<>();\n\n    /**\n     * Create a collection of assembly files\n     *\n     * @param assemblyDirectory directory into which the files are copied\n     */\n    public AssemblyFiles(File assemblyDirectory) {\n        this.assemblyDirectory = assemblyDirectory;\n    }\n\n    /**\n     * Add a entry to the list of assembly files which possible should be monitored\n     *\n     * @param srcFile source file to monitor. The source file must exist.\n     * @param destFile the destination to which it is eventually copied. The destination file must be relative.\n     */\n    public void addEntry(File srcFile, File destFile) {\n        entries.add(new Entry(srcFile,destFile));\n    }\n\n    /**\n     * Get the list of all updated entries i.e. all entries which have modification date\n     * which is newer than the last time check. ATTENTION: As a side effect this method also\n     * updates the timestamp of entries.\n     *\n     * @return list of all entries which has been updated since the last call to this method or an empty list\n     */\n    public List<Entry> getUpdatedEntriesAndRefresh() {\n        List<Entry> ret = new ArrayList<>();\n        for (Entry entry : entries) {\n            if (entry.isUpdated()) {\n                ret.add(entry);\n            }\n        }\n        return ret;\n    }\n\n    /**\n     * Returns true if there are no entries\n     */\n    public boolean isEmpty() {\n        return entries.isEmpty();\n    }\n\n    /**\n     * Return destination directory where the files are copied into\n     *\n     * @return top-level directory holding the assembled files\n     */\n    public File getAssemblyDirectory() {\n        return assemblyDirectory;\n    }\n\n    // ===============================================================================\n    // Inner class remembering the modification date of a source file and its destination\n\n    public static class Entry {\n\n        private long lastModified;\n        private File srcFile;\n        private File destFile;\n\n        private Entry(File srcFile, File destFile) {\n            this.srcFile = srcFile;\n            this.destFile = destFile;\n            if (!srcFile.exists()) {\n                throw new IllegalArgumentException(\"Source \" + srcFile + \" does not exist\");\n            }\n            if (!destFile.exists()) {\n                throw new IllegalArgumentException(\"Destination \" + destFile + \" does not exist\");\n            }\n            if (srcFile.isDirectory()) {\n                throw new IllegalArgumentException(\"Can only watch files, not directories: \" + srcFile);\n            }\n            this.lastModified = this.srcFile.lastModified();\n        }\n\n        public File getSrcFile() {\n            return srcFile;\n        }\n\n        /**\n         * @return destination file which is absolute (and withing AssemblyFiles.assemblyDirectory)\n         */\n        public File getDestFile() {\n            return destFile;\n        }\n\n        boolean isUpdated() {\n            if (srcFile.lastModified() > lastModified) {\n                // Update last modified as a side effect\n                lastModified = srcFile.lastModified();\n                return true;\n            } else {\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/BuildDirs.java",
    "content": "package io.fabric8.maven.docker.assembly;/*\n * \n * Copyright 2014 Roland Huss\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\nimport java.io.File;\n\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport io.fabric8.maven.docker.util.EnvUtil;\n\n/**\n * Helper object grouping together all working and output\n * directories.\n *\n * @author roland\n * @since 27/02/15\n */\npublic class BuildDirs {\n\n    private final String buildTopDir;\n    private final MojoParameters params;\n\n    /**\n     * Constructor building up the the output directories\n     *\n     * @param imageName image name for the image to build\n     * @param params mojo params holding base and global outptput dir\n     */\n    public BuildDirs(String imageName, MojoParameters params) {\n        this.params = params;\n        // Replace tag separator with a slash to avoid problems\n        // with OSs which gets confused by colons.\n        this.buildTopDir = imageName != null ? imageName.replace(':', '/') : null;\n    }\n\n    public File getOutputDirectory() {\n        return getDir(\"build\");\n    }\n\n    public File getWorkingDirectory() {\n        return getDir(\"work\");\n    }\n\n    public File getTemporaryRootDirectory() {\n        return getDir(\"tmp\");\n    }\n\n    void createDirs() {\n        for (String workDir : new String[] { \"build\", \"work\", \"tmp\" }) {\n            File dir = getDir(workDir);\n            if (!dir.exists()) {\n                if(!dir.mkdirs()) {\n                    throw new IllegalArgumentException(\"Cannot create directory \" + dir.getAbsolutePath());\n                }\n            }\n        }\n    }\n\n    private File getDir(String dir) {\n        return EnvUtil.prepareAbsoluteOutputDirPath(params, buildTopDir, dir);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/DockerAssemblyConfigurationSource.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Properties;\n\nimport javax.annotation.Nonnull;\n\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport org.apache.maven.archiver.MavenArchiveConfiguration;\nimport org.apache.maven.artifact.repository.ArtifactRepository;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugins.assembly.AssemblerConfigurationSource;\nimport org.apache.maven.plugins.assembly.utils.InterpolationConstants;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.filtering.MavenFileFilter;\nimport org.apache.maven.shared.filtering.MavenReaderFilter;\nimport org.apache.maven.shared.utils.cli.CommandLineUtils;\nimport org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;\nimport org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource;\nimport org.codehaus.plexus.interpolation.fixed.PropertiesBasedValueSource;\n\n/**\n * @author roland\n * @since 07.05.14\n */\npublic class DockerAssemblyConfigurationSource implements AssemblerConfigurationSource {\n\n    private final AssemblyConfiguration assemblyConfig;\n    private final MojoParameters params;\n    private final BuildDirs buildDirs;\n\n    // Required by configuration source and duplicated from AbstractAssemblyMojo (which is unfortunately\n    // not extracted to be usab;e\n    private FixedStringSearchInterpolator commandLinePropertiesInterpolator;\n    private FixedStringSearchInterpolator envInterpolator;\n    private FixedStringSearchInterpolator rootInterpolator;\n    private FixedStringSearchInterpolator mainProjectInterpolator;\n\n    public DockerAssemblyConfigurationSource(MojoParameters params, BuildDirs buildDirs, AssemblyConfiguration assemblyConfig) {\n        this.params = params;\n        this.assemblyConfig = assemblyConfig;\n        this.buildDirs = buildDirs;\n    }\n\n    @Override\n    public String[] getDescriptors() {\n        if (assemblyConfig != null) {\n          String descriptor = assemblyConfig.getDescriptor();\n\n          if (descriptor != null) {\n            return new String[] {EnvUtil.prepareAbsoluteSourceDirPath(params, descriptor).getAbsolutePath() };\n          }\n        }\n        return new String[0];\n    }\n\n    @Override\n    public String[] getDescriptorReferences() {\n        if (assemblyConfig != null) {\n            String descriptorRef = assemblyConfig.getDescriptorRef();\n            if (descriptorRef != null) {\n                return new String[]{descriptorRef};\n            }\n        }\n        return null;\n    }\n\n    // ============================================================================================\n\n    @Override\n    public File getOutputDirectory() {\n        return buildDirs.getOutputDirectory();\n    }\n\n    @Override\n    public File getWorkingDirectory() {\n        return buildDirs.getWorkingDirectory();\n    }\n\n    // X\n    @Override\n    public File getTemporaryRootDirectory() {\n        return buildDirs.getTemporaryRootDirectory();\n    }\n\n    @Override\n    public String getFinalName() {\n        //return params.getProject().getBuild().getFinalName();\n        return \".\";\n    }\n\n    @Override\n    public ArtifactRepository getLocalRepository() {\n        return params.getSession().getLocalRepository();\n    }\n    \n    public MavenFileFilter getMavenFileFilter() {\n        return params.getMavenFileFilter();\n    }\n\n    // Maybe use injection\n    @Override\n    public List<MavenProject> getReactorProjects() {\n        return params.getReactorProjects();\n    }\n\n    // Maybe use injection\n    @Override\n    public List<ArtifactRepository> getRemoteRepositories() {\n        return params.getProject().getRemoteArtifactRepositories();\n    }\n\n    @Override\n    public MavenSession getMavenSession() {\n        return params.getSession();\n    }\n\n    @Override\n    public MavenArchiveConfiguration getJarArchiveConfiguration() {\n        return params.getArchiveConfiguration();\n    }\n\n    // X\n    @Override\n    public String getEncoding() {\n        return params.getProject().getProperties().getProperty(\"project.build.sourceEncoding\");\n    }\n\n    // X\n    @Override\n    public String getEscapeString() {\n        return null;\n    }\n\n    @Override\n    public List<String> getDelimiters() {\n        return null;\n    }\n\n\n    @Nonnull public FixedStringSearchInterpolator getCommandLinePropsInterpolator()\n    {\n        if (commandLinePropertiesInterpolator == null) {\n            this.commandLinePropertiesInterpolator = createCommandLinePropertiesInterpolator();\n        }\n        return commandLinePropertiesInterpolator;\n    }\n\n    @Nonnull\n    public FixedStringSearchInterpolator getEnvInterpolator()\n    {\n        if (envInterpolator == null) {\n            this.envInterpolator = createEnvInterpolator();\n        }\n        return envInterpolator;\n    }\n\n    @Nonnull public FixedStringSearchInterpolator getRepositoryInterpolator()\n    {\n        if (rootInterpolator == null) {\n            this.rootInterpolator = createRepositoryInterpolator();\n        }\n        return rootInterpolator;\n    }\n\n\n    @Nonnull\n    public FixedStringSearchInterpolator getMainProjectInterpolator()\n    {\n        if (mainProjectInterpolator == null) {\n            this.mainProjectInterpolator = mainProjectInterpolator(getProject());\n        }\n        return mainProjectInterpolator;\n    }\n\n    // X\n    @Override\n    public MavenProject getProject() {\n        return params.getProject();\n    }\n\n    // X\n    @Override\n    public File getBasedir() {\n        return params.getProject().getBasedir();\n    }\n\n    // X\n    @Override\n    public boolean isIgnoreDirFormatExtensions() {\n        return true;\n    }\n\n    // X\n    @Override\n    public boolean isDryRun() {\n        return false;\n    }\n\n    // X\n    @Override\n    public List<String> getFilters() {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public boolean isIncludeProjectBuildFilters() {\n        return true;\n    }\n\n    // X\n    @Override\n    public File getDescriptorSourceDirectory() {\n        return null;\n    }\n\n    // X\n    @Override\n    public File getArchiveBaseDirectory() {\n        return null;\n    }\n\n    // X\n    @Override\n    public String getTarLongFileMode() {\n        return assemblyConfig.getTarLongFileMode() == null ? \"warn\" : assemblyConfig.getTarLongFileMode();\n    }\n\n    // X\n    @Override\n    public File getSiteDirectory() {\n        return null;\n    }\n\n    // X\n    @Override\n    public boolean isAssemblyIdAppended() {\n        return false;\n    }\n\n    // X\n    @Override\n    public boolean isIgnoreMissingDescriptor() {\n        return false;\n    }\n\n    // X: (maybe inject MavenArchiveConfiguration)\n    @Override\n    public String getArchiverConfig() {\n        return null;\n    }\n\n    @Override\n    public MavenReaderFilter getMavenReaderFilter() {\n        return params.getMavenFilterReader();\n    }\n\n    @Override\n    public boolean isUpdateOnly() {\n        return false;\n    }\n\n    @Override\n    public boolean isUseJvmChmod() {\n        return false;\n    }\n\n    @Override\n    public boolean isIgnorePermissions() {\n        return assemblyConfig != null ? assemblyConfig.isIgnorePermissions() : false;\n    }\n\n    // =======================================================================\n    // Taken from AbstractAssemblyMojo\n\n    private FixedStringSearchInterpolator mainProjectInterpolator(MavenProject mainProject)\n    {\n        if (mainProject != null) {\n            // 5\n            return FixedStringSearchInterpolator.create(\n                new org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource(\n                    InterpolationConstants.PROJECT_PREFIXES, mainProject, true ),\n\n                // 6\n                new org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource(\n                    InterpolationConstants.PROJECT_PROPERTIES_PREFIXES, mainProject.getProperties(), true ) );\n        }\n        else {\n            return FixedStringSearchInterpolator.empty();\n        }\n    }\n\n    private FixedStringSearchInterpolator createRepositoryInterpolator()\n    {\n        final Properties settingsProperties = new Properties();\n        final MavenSession session = getMavenSession();\n\n        if (getLocalRepository() != null) {\n            settingsProperties.setProperty(\"localRepository\", getLocalRepository().getBasedir());\n            settingsProperties.setProperty(\"settings.localRepository\", getLocalRepository().getBasedir());\n        }\n        else if (session != null && session.getSettings() != null) {\n            settingsProperties.setProperty(\"localRepository\", session.getSettings().getLocalRepository() );\n            settingsProperties.setProperty(\"settings.localRepository\", getLocalRepository().getBasedir() );\n        }\n        return FixedStringSearchInterpolator.create(new PropertiesBasedValueSource(settingsProperties));\n    }\n\n    private FixedStringSearchInterpolator createCommandLinePropertiesInterpolator()\n    {\n        Properties commandLineProperties = System.getProperties();\n        final MavenSession session = getMavenSession();\n\n        if (session != null) {\n            commandLineProperties = new Properties();\n            if (session.getSystemProperties() != null) {\n                commandLineProperties.putAll(session.getSystemProperties());\n            }\n            if (session.getUserProperties() != null) {\n                commandLineProperties.putAll(session.getUserProperties());\n            }\n        }\n        PropertiesBasedValueSource cliProps = new PropertiesBasedValueSource( commandLineProperties );\n        return FixedStringSearchInterpolator.create( cliProps );\n    }\n\n    private FixedStringSearchInterpolator createEnvInterpolator() {\n        PrefixedPropertiesValueSource envProps = new PrefixedPropertiesValueSource(Collections.singletonList(\"env.\"),\n                                                                                   CommandLineUtils.getSystemEnvVars(false), true );\n        return FixedStringSearchInterpolator.create( envProps );\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/DockerAssemblyManager.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.AssemblyMode;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.util.DockerFileUtil;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.model.Build;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.assembly.AssemblerConfigurationSource;\nimport org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;\nimport org.apache.maven.plugins.assembly.archive.ArchiveCreationException;\nimport org.apache.maven.plugins.assembly.archive.AssemblyArchiver;\nimport org.apache.maven.plugins.assembly.format.AssemblyFormattingException;\nimport org.apache.maven.plugins.assembly.io.AssemblyReadException;\nimport org.apache.maven.plugins.assembly.io.AssemblyReader;\nimport org.apache.maven.plugins.assembly.model.Assembly;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.utils.PathTool;\nimport org.apache.maven.shared.utils.io.FileUtils;\nimport org.codehaus.plexus.archiver.Archiver;\nimport org.codehaus.plexus.archiver.manager.ArchiverManager;\nimport org.codehaus.plexus.archiver.manager.NoSuchArchiverException;\nimport org.codehaus.plexus.archiver.tar.TarArchiver;\nimport org.codehaus.plexus.archiver.tar.TarLongFileMode;\nimport org.codehaus.plexus.archiver.util.DefaultArchivedFileSet;\nimport org.codehaus.plexus.archiver.util.DefaultFileSet;\nimport org.codehaus.plexus.component.annotations.Component;\nimport org.codehaus.plexus.component.annotations.Requirement;\nimport org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;\n\n/**\n * Tool for creating a docker image tar ball including a Dockerfile for building\n * a docker image.\n *\n * @author roland\n * @since 08.05.14\n */\n@Component(role = DockerAssemblyManager.class, instantiationStrategy = \"per-lookup\")\npublic class DockerAssemblyManager {\n\n    public static final String DEFAULT_DATA_BASE_IMAGE = \"busybox:latest\";\n    public static final String SCRATCH_IMAGE = \"scratch\";\n\n    // Assembly name used also as build directory within outputBuildDir\n    public static final String DOCKER_IGNORE = \".maven-dockerignore\";\n    public static final String DOCKER_EXCLUDE = \".maven-dockerexclude\";\n    public static final String DOCKER_INCLUDE = \".maven-dockerinclude\";\n    public static final String DOCKERFILE_NAME = \"Dockerfile\";\n\n    @Requirement\n    private AssemblyArchiver assemblyArchiver;\n\n    @Requirement\n    private AssemblyReader assemblyReader;\n\n    @Requirement\n    private ArchiverManager archiverManager;\n\n    @Requirement(hint = \"track\")\n    private Archiver trackArchiver;\n\n    /**\n     * Create an docker tar archive from the given configuration which can be send to the Docker host for\n     * creating the image.\n     *\n     * @param imageName Name of the image to create (used for creating build directories)\n     * @param params Mojos parameters (used for finding the directories)\n     * @param buildConfig configuration for how to build the image\n     * @param log Logger used to display warning if permissions are to be normalized\n     * @return file holding the path to the created assembly tar file\n     * @throws MojoExecutionException\n     */\n    public File createDockerTarArchive(String imageName, MojoParameters params, BuildImageConfiguration buildConfig, Logger log)\n            throws MojoExecutionException {\n        return createDockerTarArchive(imageName, params, buildConfig, log, null);\n    }\n\n    /**\n     * Create an docker tar archive from the given configuration which can be send to the Docker host for\n     * creating the image.\n     *\n     * @param imageName Name of the image to create (used for creating build directories)\n     * @param params Mojos parameters (used for finding the directories)\n     * @param buildConfig configuration for how to build the image\n     * @param log Logger used to display warning if permissions are to be normalized\n     * @param finalCustomizer finalCustomizer to be applied to the tar archive\n     * @return file holding the path to the created assembly tar file\n     * @throws MojoExecutionException\n     */\n    public File createDockerTarArchive(String imageName, final MojoParameters params, final BuildImageConfiguration buildConfig, Logger log, ArchiverCustomizer finalCustomizer)\n            throws MojoExecutionException {\n\n        final BuildDirs buildDirs = createBuildDirs(imageName, params);\n        final AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n\n        final List<ArchiverCustomizer> archiveCustomizers = new ArrayList<>();\n\n        // Build up assembly. In dockerfile mode this must be added explicitly in the Dockerfile with an ADD\n        if (hasAssemblyConfiguration(assemblyConfig)) {\n            createAssemblyArchive(assemblyConfig, params, buildDirs);\n        }\n        try {\n            if (buildConfig.isDockerFileMode()) {\n                // Use specified docker directory which must include a Dockerfile.\n                final File dockerFile = buildConfig.getAbsoluteDockerFilePath(params);\n                if (!dockerFile.exists()) {\n                    throw new MojoExecutionException(\"Configured Dockerfile \\\"\" +\n                                                     buildConfig.getDockerFile() + \"\\\" (resolved to \\\"\" + dockerFile + \"\\\") doesn't exist\");\n                }\n\n                FixedStringSearchInterpolator interpolator = DockerFileUtil.createInterpolator(params, buildConfig.getFilter());\n                verifyGivenDockerfile(dockerFile, buildConfig, interpolator, log);\n                interpolateDockerfile(dockerFile, buildDirs, interpolator);\n                // User dedicated Dockerfile from extra directory\n                archiveCustomizers.add(new ArchiverCustomizer() {\n                    @Override\n                    public TarArchiver customize(TarArchiver archiver) throws IOException {\n                        DefaultFileSet fileSet = DefaultFileSet.fileSet(buildConfig.getAbsoluteContextDirPath(params));\n                        addDockerIncludesExcludesIfPresent(fileSet, params);\n                        // Exclude non-interpolated dockerfile from source tree\n                        // Interpolated Dockerfile is already added as it was created into the output directory when\n                        // using dir dir mode\n                        excludeDockerfile(fileSet, dockerFile);\n\n                        // If the content is added as archive, then we need to add the Dockerfile from the builddir\n                        // directly to docker.tar (as the output builddir is not picked up in archive mode)\n                        if (isArchive(assemblyConfig)) {\n                            String name = dockerFile.getName();\n                            archiver.addFile(new File(buildDirs.getOutputDirectory(), name), name);\n                        }\n\n                        archiver.addFileSet(fileSet);\n                        return archiver;\n                    }\n                });\n            } else {\n                // Create custom docker file in output dir\n                DockerFileBuilder builder = createDockerFileBuilder(buildConfig, assemblyConfig);\n                builder.write(buildDirs.getOutputDirectory());\n                // Add own Dockerfile\n                final File dockerFile = new File(buildDirs.getOutputDirectory(), DOCKERFILE_NAME);\n                archiveCustomizers.add(new ArchiverCustomizer() {\n                    @Override\n                    public TarArchiver customize(TarArchiver archiver) throws IOException {\n                        archiver.addFile(dockerFile, DOCKERFILE_NAME);\n                        return archiver;\n                    }\n                });\n            }\n\n            // If required make all files in the assembly executable\n            if (assemblyConfig != null) {\n                AssemblyConfiguration.PermissionMode mode = assemblyConfig.getPermissions();\n                if (mode == AssemblyConfiguration.PermissionMode.exec ||\n                    mode == AssemblyConfiguration.PermissionMode.auto && EnvUtil.isWindows()) {\n                    archiveCustomizers.add(new AllFilesExecCustomizer(log));\n                }\n            }\n\n            if (finalCustomizer != null) {\n                archiveCustomizers.add(finalCustomizer);\n            }\n\n            return createBuildTarBall(buildDirs, archiveCustomizers, assemblyConfig, buildConfig.getCompression());\n\n        } catch (IOException e) {\n            throw new MojoExecutionException(String.format(\"Cannot create %s in %s\", DOCKERFILE_NAME, buildDirs.getOutputDirectory()), e);\n        }\n    }\n\n\n    private void excludeDockerfile(DefaultFileSet fileSet, File dockerFile) {\n        ArrayList<String> excludes =\n            fileSet.getExcludes() != null ?\n                new ArrayList<>(Arrays.asList(fileSet.getExcludes())) :\n                new ArrayList<String>();\n        excludes.add(dockerFile.getName());\n        fileSet.setExcludes(excludes.toArray(new String[0]));\n    }\n\n    private void interpolateDockerfile(File dockerFile, BuildDirs params, FixedStringSearchInterpolator interpolator) throws IOException {\n        File targetDockerfile = new File(params.getOutputDirectory(), dockerFile.getName());\n        String dockerFileInterpolated = DockerFileUtil.interpolate(dockerFile, interpolator);\n        try (Writer writer = new FileWriter(targetDockerfile)) {\n            IOUtils.write(dockerFileInterpolated, writer);\n        }\n    }\n\n    // visible for testing\n    void verifyGivenDockerfile(File dockerFile, BuildImageConfiguration buildConfig, FixedStringSearchInterpolator interpolator, Logger log) throws IOException {\n        AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n        if (assemblyConfig == null) {\n            return;\n        }\n\n        String name = assemblyConfig.getName();\n            for (String keyword : new String[] { \"ADD\", \"COPY\" }) {\n                List<String[]> lines = DockerFileUtil.extractLines(dockerFile, keyword, interpolator);\n                for (String[] line : lines) {\n                    if (!line[0].startsWith(\"#\")) {\n                        // Skip command flags like --chown\n                        int i;\n                        for (i = 1; i < line.length; i++) {\n                            String component = line[i];\n                            if (!component.startsWith(\"--\")) {\n                                break;\n                            }\n                        }\n\n                        // contains an ADD/COPY ... targetDir .... All good.\n                        if (i < line.length && line[i].contains(name)) {\n                            return;\n                        }\n                    }\n                }\n            }\n        log.warn(\"Dockerfile %s does not contain an ADD or COPY directive to include assembly created at %s. Ignoring assembly.\",\n                 dockerFile.getPath(), name);\n    }\n\n    /**\n     * Extract all files with a tracking archiver. These can be used to track changes in the filesystem and triggering\n     * a rebuild of the image if needed ('docker:watch')\n     */\n    public AssemblyFiles getAssemblyFiles(String name, BuildImageConfiguration buildConfig, MojoParameters mojoParams, Logger log)\n            throws InvalidAssemblerConfigurationException, ArchiveCreationException, AssemblyFormattingException, MojoExecutionException {\n\n        BuildDirs buildDirs = createBuildDirs(name, mojoParams);\n\n        AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n        String assemblyName = assemblyConfig.getName();\n        DockerAssemblyConfigurationSource source =\n                        new DockerAssemblyConfigurationSource(mojoParams, buildDirs, assemblyConfig);\n        Assembly assembly = getAssemblyConfig(assemblyConfig, source);\n\n\n        synchronized (trackArchiver) {\n            MappingTrackArchiver ta = (MappingTrackArchiver) trackArchiver;\n            ta.init(log, assemblyName);\n            assembly.setId(\"tracker\");\n            assemblyArchiver.createArchive(assembly, assemblyName, \"track\", source, false, null);\n            return ta.getAssemblyFiles(mojoParams.getSession());\n        }\n    }\n\n    private BuildDirs createBuildDirs(String imageName, MojoParameters params) {\n        BuildDirs buildDirs = new BuildDirs(imageName, params);\n        buildDirs.createDirs();\n        return buildDirs;\n    }\n\n    private boolean hasAssemblyConfiguration(AssemblyConfiguration assemblyConfig) {\n        return assemblyConfig != null &&\n                (assemblyConfig.getInline() != null ||\n                 assemblyConfig.getDescriptor() != null ||\n                 assemblyConfig.getDescriptorRef() != null);\n    }\n\n    private boolean isArchive(AssemblyConfiguration assemblyConfig) {\n        return hasAssemblyConfiguration(assemblyConfig) &&\n               assemblyConfig.getMode() != null &&\n               assemblyConfig.getMode().isArchive();\n    }\n\n    public File createChangedFilesArchive(List<AssemblyFiles.Entry> entries, File assemblyDirectory,\n                                          String imageName, MojoParameters mojoParameters)\n            throws MojoExecutionException {\n        BuildDirs dirs = createBuildDirs(imageName, mojoParameters);\n        try {\n            File archive = new File(dirs.getTemporaryRootDirectory(), \"changed-files.tar\");\n            File archiveDir = createArchiveDir(dirs);\n            for (AssemblyFiles.Entry entry : entries) {\n                File dest = prepareChangedFilesArchivePath(archiveDir,entry.getDestFile(),assemblyDirectory);\n                FileUtils.copyFile(entry.getSrcFile(), dest);\n            }\n            return createChangedFilesTarBall(archive, archiveDir);\n        } catch (IOException exp) {\n            throw new MojoExecutionException(\"Error while creating \" + dirs.getTemporaryRootDirectory() +\n                                             \"/changed-files.tar: \" + exp);\n        }\n    }\n\n    private File prepareChangedFilesArchivePath(File archiveDir, File destFile, File assemblyDir) throws IOException {\n        // Replace build target dir from destfile and add changed-files build dir instead\n        String relativePath = PathTool.getRelativeFilePath(assemblyDir.getCanonicalPath(),destFile.getCanonicalPath());\n        return new File(archiveDir,relativePath);\n    }\n\n    // Create final tar-ball to be used for building the archive to send to the Docker daemon\n    private File createBuildTarBall(BuildDirs buildDirs, List<ArchiverCustomizer> archiverCustomizers,\n                                    AssemblyConfiguration assemblyConfig, ArchiveCompression compression) throws MojoExecutionException {\n        File archive = new File(buildDirs.getTemporaryRootDirectory(), \"docker-build.\" + compression.getFileSuffix());\n        try {\n            TarArchiver archiver = createBuildArchiver(buildDirs.getOutputDirectory(), archive, assemblyConfig);\n            for (ArchiverCustomizer customizer : archiverCustomizers) {\n                if (customizer != null) {\n                    archiver = customizer.customize(archiver);\n                }\n            }\n            archiver.setCompression(compression.getTarCompressionMethod());\n            archiver.createArchive();\n            return archive;\n        } catch (NoSuchArchiverException e) {\n            throw new MojoExecutionException(\"No archiver for type 'tar' found\", e);\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Cannot create archive \" + archive, e);\n        }\n    }\n\n    private void addDockerIncludesExcludesIfPresent(DefaultFileSet fileSet, MojoParameters params) throws IOException {\n        addDockerExcludes(fileSet, params);\n        addDockerIncludes(fileSet);\n    }\n\n    private void addDockerExcludes(DefaultFileSet fileSet, MojoParameters params) throws IOException {\n        File directory = fileSet.getDirectory();\n        List<String> excludes = new ArrayList<>();\n        // Output directory will be always excluded\n        excludes.add(params.getOutputDirectory() + \"/**\");\n        for (String file : new String[] { DOCKER_EXCLUDE, DOCKER_IGNORE } ) {\n            File dockerIgnore = new File(directory, file);\n            if (dockerIgnore.exists()) {\n                excludes.addAll(Arrays.asList(FileUtils.fileReadArray(dockerIgnore)));\n                excludes.add(DOCKER_IGNORE);\n            }\n        }\n        fileSet.setExcludes(excludes.toArray(new String[0]));\n    }\n\n    private void addDockerIncludes(DefaultFileSet fileSet) throws IOException {\n        File directory = fileSet.getDirectory();\n        File dockerInclude = new File(directory, DOCKER_INCLUDE);\n        if (dockerInclude.exists()) {\n            ArrayList<String> includes = new ArrayList<>(Arrays.asList(FileUtils.fileReadArray(dockerInclude)));\n            fileSet.setIncludes(includes.toArray(new String[0]));\n        }\n    }\n\n    private File createChangedFilesTarBall(File archive, File archiveDir) throws MojoExecutionException {\n        try {\n            TarArchiver archiver = (TarArchiver) archiverManager.getArchiver(\"tar\");\n            archiver.setLongfile(TarLongFileMode.posix);\n            archiver.addFileSet(DefaultFileSet.fileSet(archiveDir));\n            archiver.setDestFile(archive);\n            archiver.createArchive();\n            return archive;\n        } catch (NoSuchArchiverException e) {\n            throw new MojoExecutionException(\"No archiver for type 'tar' found\", e);\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Cannot create archive \" + archive, e);\n        } catch (RuntimeException e) {\n            e.printStackTrace();\n            throw e;\n        }\n    }\n\n    private File createArchiveDir(BuildDirs dirs) throws IOException, MojoExecutionException {\n        File archiveDir = new File(dirs.getTemporaryRootDirectory(), \"changed-files\");\n        if (archiveDir.exists()) {\n            // Remove old stuff to\n            FileUtils.cleanDirectory(archiveDir);\n        } else {\n            if (!archiveDir.mkdir()) {\n                throw new MojoExecutionException(\"Cannot create \" + archiveDir);\n            }\n        }\n        return archiveDir;\n    }\n\n    private TarArchiver createBuildArchiver(File outputDir, File archive, AssemblyConfiguration assemblyConfig) throws NoSuchArchiverException {\n        TarArchiver archiver = (TarArchiver) archiverManager.getArchiver(\"tar\");\n        archiver.setLongfile(TarLongFileMode.posix);\n\n        AssemblyMode mode = assemblyConfig != null ? assemblyConfig.getMode() : null;\n        if (mode != null && mode.isArchive()) {\n            DefaultArchivedFileSet archiveSet =\n                    DefaultArchivedFileSet.archivedFileSet(new File(outputDir,  assemblyConfig.getName() + \".\" + mode.getExtension()));\n            archiveSet.setPrefix(assemblyConfig.getName() + \"/\");\n            archiveSet.setIncludingEmptyDirectories(true);\n            archiveSet.setUsingDefaultExcludes(false);\n            archiver.addArchivedFileSet(archiveSet);\n        } else {\n            DefaultFileSet fileSet = DefaultFileSet.fileSet(outputDir);\n            fileSet.setUsingDefaultExcludes(false);\n            archiver.addFileSet(fileSet);\n        }\n        archiver.setDestFile(archive);\n        return archiver;\n    }\n\n    // visible for testing\n    @SuppressWarnings(\"deprecation\")\n    DockerFileBuilder createDockerFileBuilder(BuildImageConfiguration buildConfig, AssemblyConfiguration assemblyConfig) {\n        DockerFileBuilder builder =\n                new DockerFileBuilder()\n                        .env(buildConfig.getEnv())\n                        .labels(buildConfig.getLabels())\n                        .expose(buildConfig.getPorts())\n                        .run(buildConfig.getRunCmds())\n                        .volumes(buildConfig.getVolumes())\n                        .user(buildConfig.getUser());\n        if (buildConfig.getMaintainer() != null) {\n            builder.maintainer(buildConfig.getMaintainer());\n        }\n        if (buildConfig.getWorkdir() != null) {\n            builder.workdir(buildConfig.getWorkdir());\n        }\n        if (assemblyConfig != null) {\n            builder.add(assemblyConfig.getName(), \"\")\n                   .basedir(assemblyConfig.getTargetDir())\n                   .assemblyUser(assemblyConfig.getUser())\n                   .exportTargetDir(assemblyConfig.exportTargetDir());\n        } else {\n            builder.exportTargetDir(false);\n        }\n\n        builder.baseImage(buildConfig.getFrom());\n\n        if (buildConfig.getHealthCheck() != null) {\n            builder.healthCheck(buildConfig.getHealthCheck());\n        }\n\n        if (buildConfig.getCmd() != null){\n            builder.cmd(buildConfig.getCmd());\n        } else if (buildConfig.getCommand() != null) {\n            Arguments args = Arguments.Builder.get().withShell(buildConfig.getCommand()).build();\n            builder.cmd(args);\n        }\n\n        if (buildConfig.getEntryPoint() != null){\n            builder.entryPoint(buildConfig.getEntryPoint());\n        }\n\n        if (buildConfig.optimise()) {\n            builder.optimise();\n        }\n\n        return builder;\n    }\n\n    private void createAssemblyArchive(AssemblyConfiguration assemblyConfig, MojoParameters params, BuildDirs buildDirs)\n            throws MojoExecutionException {\n        DockerAssemblyConfigurationSource source = new DockerAssemblyConfigurationSource(params, buildDirs, assemblyConfig);\n        Assembly assembly = getAssemblyConfig(assemblyConfig, source);\n\n        AssemblyMode buildMode = assemblyConfig.getMode();\n        File originalArtifactFile = null;\n        try {\n            originalArtifactFile = ensureThatArtifactFileIsSet(params.getProject());\n            assembly.setId(\"docker\");\n            assemblyArchiver.createArchive(assembly, assemblyConfig.getName(), buildMode.getExtension(), source, false, null);\n        } catch (ArchiveCreationException | AssemblyFormattingException e) {\n            String error = \"Failed to create assembly for docker image \" +\n                           \" (with mode '\" + buildMode + \"'): \" + e.getMessage() + \".\";\n            if (params.getProject().getArtifact().getFile() == null) {\n                error += \" If you include the build artifact please ensure that you have \" +\n                         \"built the artifact before with 'mvn package' (should be available in the target/ dir). \" +\n                         \"Please see the documentation (section \\\"Assembly\\\") for more information.\";\n            }\n            throw new MojoExecutionException(error, e);\n        } catch (InvalidAssemblerConfigurationException e) {\n            throw new MojoExecutionException(assembly, \"Assembly is incorrectly configured: \" + assembly.getId(),\n                                            \"Assembly: \" + assembly.getId() + \" is not configured correctly: \"\n                                            + e.getMessage());\n        } finally {\n            setArtifactFile(params.getProject(), originalArtifactFile);\n        }\n    }\n\n    // Set an artifact file if it is missing. This workaround the issues\n    // mentioned first in https://issues.apache.org/jira/browse/MASSEMBLY-94 which requires the package\n    // phase to run so set the ArtifactFile. There is no good solution, so we are trying\n    // to be very defensive and add a workaround for some situation which won't work for every occasion.\n    // Unfortunately a plain forking of the Maven lifecycle is not good enough, since the MavenProject\n    // gets cloned before the fork, and the 'package' plugin (e.g. JarPlugin) sets the file on the cloned\n    // object which is then not available for the BuildMojo (there the file is still null leading to the\n    // the \"Cannot include project artifact: ... The following patterns were never triggered in this artifact inclusion filter: <artifact>\"\n    // warning with an error following.\n    private File ensureThatArtifactFileIsSet(MavenProject project) {\n        Artifact artifact = project.getArtifact();\n        if (artifact == null) {\n            return null;\n        }\n        File oldFile = artifact.getFile();\n        if (oldFile != null) {\n            return oldFile;\n        }\n        Build build = project.getBuild();\n        if (build == null) {\n            return null;\n        }\n        String finalName = build.getFinalName();\n        String target = build.getDirectory();\n        if (finalName == null || target == null) {\n            return null;\n        }\n        File artifactFile = new File(target, finalName + \".\" + project.getPackaging());\n        if (artifactFile.exists() && artifactFile.isFile()) {\n            setArtifactFile(project, artifactFile);\n        }\n        return null;\n    }\n\n    private void setArtifactFile(MavenProject project, File artifactFile) {\n        Artifact artifact = project.getArtifact();\n        if (artifact != null) {\n            artifact.setFile(artifactFile);\n        }\n    }\n\n    private Assembly getAssemblyConfig(AssemblyConfiguration assemblyConfig, DockerAssemblyConfigurationSource source)\n            throws MojoExecutionException {\n        Assembly assembly = assemblyConfig.getInline();\n        if (assembly == null) {\n            assembly = extractAssembly(source);\n        }\n        return assembly;\n    }\n\n    private Assembly extractAssembly(AssemblerConfigurationSource config) throws MojoExecutionException {\n        try {\n            List<Assembly> assemblies = assemblyReader.readAssemblies(config);\n            if (assemblies.size() != 1) {\n                throw new MojoExecutionException(\"Only one assembly can be used for creating a Docker base image (and not \"\n                        + assemblies.size() + \")\");\n            }\n            return assemblies.get(0);\n        }\n        catch (AssemblyReadException e) {\n            throw new MojoExecutionException(\"Error reading assembly: \" + e.getMessage(), e);\n        }\n        catch (InvalidAssemblerConfigurationException e) {\n            throw new MojoExecutionException(assemblyReader, e.getMessage(), \"Docker assembly configuration is invalid: \" + e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/DockerFileBuilder.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.base.Joiner;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.HealthCheckConfiguration;\n\nimport org.codehaus.plexus.util.FileUtils;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * Create a dockerfile\n *\n * @author roland\n * @since 17.04.14\n */\npublic class DockerFileBuilder {\n\n    private static final Joiner JOIN_ON_COMMA = Joiner.on(\"\\\",\\\"\");\n\n    private static final Pattern ENV_VAR_PATTERN = Pattern.compile(\"^\\\\$(\\\\{[a-zA-Z0-9_]+\\\\}|[a-zA-Z0-9_]+).*\");\n\n    // Base image to use as from\n    private String baseImage;\n\n    // Maintainer of this image\n    private String maintainer;\n\n    // Workdir\n    private String workdir = null;\n\n    // Basedir to be export\n    private String basedir = \"/maven\";\n\n    private Arguments entryPoint;\n    private Arguments cmd;\n\n    private Boolean exportTargetDir = null;\n\n    // User under which the files should be added\n    private String assemblyUser;\n\n    // User to run as\n    private String user;\n\n    private HealthCheckConfiguration healthCheck;\n\n    // List of files to add. Source and destination follow except that destination\n    // in interpreted as a relative path to the exportDir\n    // See also http://docs.docker.io/reference/builder/#copy\n    private List<CopyEntry> copyEntries = new ArrayList<>();\n\n    // list of ports to expose and environments to use\n    private List<String> ports = new ArrayList<>();\n\n    // SHELL executable and params to be used with the runCmds see issue #1156 on github\n    private Arguments shell;\n\n    // list of RUN Commands to run along with image build see issue #191 on github\n    private List<String> runCmds = new ArrayList<>();\n\n    // environment\n    private Map<String,String> envEntries = new LinkedHashMap<>();\n\n    // image labels\n    private Map<String, String> labels = new LinkedHashMap<>();\n\n    // exposed volumes\n    private List<String> volumes = new ArrayList<>();\n\n    // whether the Dockerfile should be optimised. i.e. compressing run statements into a single statement\n    private boolean shouldOptimise = false;\n\n    /**\n     * Create a DockerFile in the given directory\n     * @param  destDir directory where to store the dockerfile\n     * @return the full path to the docker file\n     * @throws IOException if writing fails\n     */\n    public File write(File destDir) throws IOException {\n        File target = new File(destDir,\"Dockerfile\");\n        FileUtils.fileWrite(target, content());\n        return target;\n    }\n\n    /**\n     * Create a Dockerfile following the format described in the\n     * <a href=\"http://docs.docker.io/reference/builder/#usage\">Docker reference manual</a>\n     *\n     * @return the dockerfile create\n     * @throws IllegalArgumentException if no src/dest entries have been added\n     */\n    public String content() throws IllegalArgumentException {\n\n        StringBuilder b = new StringBuilder();\n\n        DockerFileKeyword.FROM.addTo(b, baseImage != null ? baseImage : DockerAssemblyManager.DEFAULT_DATA_BASE_IMAGE);\n        if (maintainer != null) {\n            DockerFileKeyword.MAINTAINER.addTo(b, maintainer);\n        }\n\n        addOptimisation();\n        addEnv(b);\n        addLabels(b);\n        addPorts(b);\n\n        addCopy(b);\n        addWorkdir(b);\n        addShell(b);\n        addRun(b);\n        addVolumes(b);\n\n        addHealthCheck(b);\n\n        addCmd(b);\n        addEntryPoint(b);\n\n        addUser(b);\n\n        return b.toString();\n    }\n\n    private void addUser(StringBuilder b) {\n        if (user != null) {\n            DockerFileKeyword.USER.addTo(b, user);\n        }\n    }\n\n    private void addHealthCheck(StringBuilder b) {\n        if (healthCheck != null) {\n            StringBuilder healthString = new StringBuilder();\n\n            switch (healthCheck.getMode()) {\n            case cmd:\n                buildOption(healthString, DockerFileOption.HEALTHCHECK_INTERVAL, healthCheck.getInterval());\n                buildOption(healthString, DockerFileOption.HEALTHCHECK_TIMEOUT, healthCheck.getTimeout());\n                buildOption(healthString, DockerFileOption.HEALTHCHECK_START_PERIOD, healthCheck.getStartPeriod());\n                buildOption(healthString, DockerFileOption.HEALTHCHECK_RETRIES, healthCheck.getRetries());\n                buildArguments(healthString, DockerFileKeyword.CMD, false, healthCheck.getCmd());\n                break;\n            case none:\n                DockerFileKeyword.NONE.addTo(healthString, false);\n                break;\n            default:\n                throw new IllegalArgumentException(\"Unsupported health check mode: \" + healthCheck.getMode());\n            }\n\n            DockerFileKeyword.HEALTHCHECK.addTo(b, healthString.toString());\n        }\n    }\n\n    private void addWorkdir(StringBuilder b) {\n        if (workdir != null) {\n            DockerFileKeyword.WORKDIR.addTo(b, workdir);\n        }\n    }\n\n    private void addEntryPoint(StringBuilder b){\n        if (entryPoint != null) {\n            buildArguments(b, DockerFileKeyword.ENTRYPOINT, true, entryPoint);\n        }\n    }\n\n    private void addCmd(StringBuilder b){\n        if (cmd != null) {\n            buildArguments(b, DockerFileKeyword.CMD, true, cmd);\n        }\n    }\n\n    private static void buildArguments(StringBuilder b, DockerFileKeyword key, boolean newline, Arguments arguments) {\n        String arg;\n        if (arguments.getShell() != null) {\n            arg = arguments.getShell();\n        } else {\n            arg = \"[\\\"\"  + JOIN_ON_COMMA.join(arguments.getExec()) + \"\\\"]\";\n        }\n        key.addTo(b, newline, arg);\n    }\n\n    private static void buildArgumentsAsJsonFormat(StringBuilder b, DockerFileKeyword key, boolean newline, Arguments arguments) {\n        String arg = \"[\\\"\"  + JOIN_ON_COMMA.join(arguments.asStrings()) + \"\\\"]\";\n        key.addTo(b, newline, arg);\n    }\n\n    private static void buildOption(StringBuilder b, DockerFileOption option, Object value) {\n        if (value != null) {\n            option.addTo(b, value);\n        }\n    }\n\n    private void addCopy(StringBuilder b) {\n        if (assemblyUser != null) {\n            String[] userParts = assemblyUser.split(\":\");\n\n            for (CopyEntry entry : copyEntries) {\n                if (userParts.length > 2) {\n                    DockerFileKeyword.USER.addTo(b, \"root\");\n                }\n                addCopyEntries(b, \"\", (userParts.length > 1 ?\n                        userParts[0] + \":\" + userParts[1] :\n                        userParts[0]));\n                if (userParts.length > 2) {\n                    DockerFileKeyword.USER.addTo(b, userParts[2]);\n                }\n            }\n        } else {\n            addCopyEntries(b, \"\", null);\n        }\n    }\n\n    private void addCopyEntries(StringBuilder b, String topLevelDir, String ownerAndGroup) {\n        for (CopyEntry entry : copyEntries) {\n            String dest = topLevelDir + (basedir.equals(\"/\") ? \"\" : basedir) + \"/\" + entry.destination;\n            if (ownerAndGroup == null) {\n                DockerFileKeyword.COPY.addTo(b, entry.source, dest);\n            } else {\n                DockerFileKeyword.COPY.addTo(b, \" --chown=\" + ownerAndGroup, entry.source, dest);\n            }\n        }\n    }\n\n    private void addEnv(StringBuilder b) {\n        addMap(b, DockerFileKeyword.ENV, envEntries);\n    }\n\n    private void addLabels(StringBuilder b) {\n        addMap(b, DockerFileKeyword.LABEL, labels);\n    }\n\n    private void addMap(StringBuilder b,DockerFileKeyword keyword, Map<String,String> map) {\n        if (map != null && map.size() > 0) {\n            String entries[] = new String[map.size()];\n            int i = 0;\n            for (Map.Entry<String, String> entry : map.entrySet()) {\n                entries[i++] = createKeyValue(entry.getKey(), entry.getValue());\n            }\n            keyword.addTo(b, entries);\n        }\n    }\n\n    /**\n     * Escape any slashes, quotes, and newlines int the value.  If any escaping occurred, quote the value.\n     * @param key The key\n     * @param value The value\n     * @return Escaped and quoted key=\"value\"\n     */\n    private String createKeyValue(String key, String value) {\n        StringBuilder sb = new StringBuilder();\n        // no quoting the key; \"Keys are alphanumeric strings which may contain periods (.) and hyphens (-)\"\n        sb.append(key).append('=');\n        if (value == null || value.isEmpty()) {\n            return sb.append(\"\\\"\\\"\").toString();\n        }\n\tStringBuilder valBuf = new StringBuilder();\n\tboolean toBeQuoted = false;\n        for (int i = 0; i < value.length(); ++i) {\n            char c = value.charAt(i);\n            switch (c) {\n                case '\"':\n                case '\\n':\n                case '\\\\':\n                    // escape the character\n                    valBuf.append('\\\\');\n                case ' ':\n                    // space needs quotes, too\n                    toBeQuoted = true;\n                default:\n                    // always append\n                    valBuf.append(c);\n            }\n        }\n        if (toBeQuoted) {\n            // need to keep quotes\n            sb.append('\"').append(valBuf.toString()).append('\"');\n        } else {\n            sb.append(value);\n        }\n        return sb.toString();\n    }\n\n    private void addPorts(StringBuilder b) {\n        if (ports.size() > 0) {\n            String[] portsS = new String[ports.size()];\n            int i = 0;\n            for(String port : ports) {\n            \tportsS[i++] = validatePortExposure(port);\n            }\n            DockerFileKeyword.EXPOSE.addTo(b, portsS);\n        }\n    }\n\n    private String validatePortExposure(String input) throws IllegalArgumentException {\n        try {\n            Matcher matcher = Pattern.compile(\"(.*?)(?:/(tcp|udp))?$\", Pattern.CASE_INSENSITIVE).matcher(input);\n            // Matches always.  If there is a tcp/udp protocol, should end up in the second group\n            // and get factored out.  If it's something invalid, it should get stuck to the first group.\n            matcher.matches();\n            Integer.valueOf(matcher.group(1));\n            return input.toLowerCase();\n        } catch (NumberFormatException exp) {\n            throw new IllegalArgumentException(\"\\nInvalid port mapping '\" + input + \"'\\n\" +\n                    \"Required format: '<hostIP>(/tcp|udp)'\\n\" +\n                    \"See the reference manual for more details\");\n        }\n    }\n\n    private void addOptimisation() {\n        if (runCmds != null && !runCmds.isEmpty() && shouldOptimise) {\n            String optimisedRunCmd = StringUtils.join(runCmds.iterator(), \" && \");\n            runCmds.clear();\n            runCmds.add(optimisedRunCmd);\n        }\n    }\n\n    private void addShell(StringBuilder b) {\n        if (shell != null) {\n            buildArgumentsAsJsonFormat(b, DockerFileKeyword.SHELL, true, shell);\n        }\n    }\n\n\tprivate void addRun(StringBuilder b) {\n\t\tfor (String run : runCmds) {\n            DockerFileKeyword.RUN.addTo(b, run);\n\t\t}\n\t}\n\n    private void addVolumes(StringBuilder b) {\n        if (exportTargetDir != null ? exportTargetDir : baseImage == null) {\n            addVolume(b, basedir);\n        }\n\n        for (String volume : volumes) {\n            addVolume(b, volume);\n        }\n    }\n\n    private void addVolume(StringBuilder buffer, String volume) {\n        while (volume.endsWith(\"/\")) {\n            volume = volume.substring(0, volume.length() - 1);\n        }\n        // don't export '/'\n        if (volume.length() > 0) {\n            DockerFileKeyword.VOLUME.addTo(buffer, \"[\\\"\" + volume + \"\\\"]\");\n        }\n    }\n\n    // ==========================================================================\n    // Builder stuff ....\n    public DockerFileBuilder() {}\n\n    public DockerFileBuilder baseImage(String baseImage) {\n        if (baseImage != null) {\n            this.baseImage = baseImage;\n        }\n        return this;\n    }\n\n    public DockerFileBuilder maintainer(String maintainer) {\n        this.maintainer = maintainer;\n        return this;\n    }\n\n    public DockerFileBuilder workdir(String workdir) {\n        this.workdir = workdir;\n        return this;\n    }\n\n    public DockerFileBuilder basedir(String dir) {\n        if (dir != null) {\n            if (!dir.startsWith(\"/\") && !ENV_VAR_PATTERN.matcher(dir).matches()) {\n                throw new IllegalArgumentException(\"'basedir' must be an absolute path starting with / (and not \" +\n                                                   \"'\" + basedir + \"') or start with an environment variable\");\n            }\n            basedir = dir;\n        }\n        return this;\n    }\n\n    public DockerFileBuilder cmd(Arguments cmd) {\n        this.cmd = cmd;\n        return this;\n    }\n\n    public DockerFileBuilder entryPoint(Arguments entryPoint) {\n        this.entryPoint = entryPoint;\n        return this;\n    }\n\n    public DockerFileBuilder assemblyUser(String assemblyUser) {\n        this.assemblyUser = assemblyUser;\n        return this;\n    }\n\n    public DockerFileBuilder user(String user) {\n        this.user = user;\n        return this;\n    }\n\n    public DockerFileBuilder healthCheck(HealthCheckConfiguration healthCheck) {\n        this.healthCheck = healthCheck;\n        return this;\n    }\n\n    public DockerFileBuilder add(String source, String destination) {\n        this.copyEntries.add(new CopyEntry(source, destination));\n        return this;\n    }\n\n    public DockerFileBuilder expose(List<String> ports) {\n        if (ports != null) {\n            this.ports.addAll(ports);\n        }\n        return this;\n    }\n\n    /**\n     * Adds the SHELL Command plus params within the build image section\n     * @param shell\n     * @return\n     */\n    public DockerFileBuilder shell(Arguments shell) {\n        this.shell = shell;\n        return this;\n    }\n\n    /**\n     * Adds the RUN Commands within the build image section\n     * @param runCmds\n     * @return\n     */\n    public DockerFileBuilder run(List<String> runCmds) {\n        if (runCmds != null) {\n            for (String cmd : runCmds) {\n                if (!StringUtils.isEmpty(cmd)) {\n                    this.runCmds.add(cmd);\n                }\n            }\n        }\n        return this;\n    }\n\n    public DockerFileBuilder exportTargetDir(Boolean exportTargetDir) {\n        this.exportTargetDir = exportTargetDir;\n        return this;\n    }\n\n    public DockerFileBuilder env(Map<String, String> values) {\n        if (values != null) {\n            this.envEntries.putAll(values);\n            validateMap(envEntries);\n        }\n        return this;\n    }\n\n    public DockerFileBuilder labels(Map<String,String> values) {\n        if (values != null) {\n            this.labels.putAll(values);\n        }\n        return this;\n    }\n\n    public DockerFileBuilder volumes(List<String> volumes) {\n        if (volumes != null) {\n           this.volumes.addAll(volumes);\n        }\n        return this;\n    }\n\n    public DockerFileBuilder optimise() {\n        this.shouldOptimise = true;\n        return this;\n    }\n\n    private void validateMap(Map<String, String> env) {\n        for (Map.Entry<String,String> entry : env.entrySet()) {\n            if (entry.getValue() == null || entry.getValue().length() == 0) {\n                throw new IllegalArgumentException(\"Environment variable '\" +\n                                                   entry.getKey() + \"' must not be null or empty if building an image\");\n            }\n        }\n    }\n\n    // All entries required, destination is relative to exportDir\n    private static final class CopyEntry {\n        private String source,destination;\n\n        private CopyEntry(String src, String dest) {\n            source = src;\n\n            // Strip leading slashes\n            destination = dest;\n\n            // squeeze slashes\n            while (destination.startsWith(\"/\")) {\n                destination = destination.substring(1);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/DockerFileKeyword.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\n/**\n * Fields for  a dockerfile\n * @author Paris Apostolopoulos <javapapo@mac.com>\n * @author Christian Fischer <sw-dev@computerlyrik.de>\n * @since 13.06.05\n */\npublic enum DockerFileKeyword\n{\n    MAINTAINER,\n    ADD,\n    EXPOSE,\n    FROM,\n    SHELL,\n    RUN,\n    WORKDIR,\n    ENTRYPOINT,\n    CMD,\n    USER,\n    ENV,\n    ARG,\n    LABEL,\n    COPY,\n    VOLUME,\n    HEALTHCHECK,\n    NONE;\n\n    /**\n     * Append this keyword + optionally some args to a {@link StringBuilder} plus a trailing newline.\n     *\n     * @param sb stringbuilder to add to\n     * @param args args added (space separated)\n     */\n    public void addTo(StringBuilder sb, String ... args) {\n        addTo(sb, true, args);\n    }\n\n    /**\n     * Append this keyword + optionally some args to a {@link StringBuilder} and a optional trailing newline.\n     *\n     * @param sb stringbuilder to add to\n     * @param newline flag indicating whether a new line should be added\n     * @param args args added (space separated)\n     */\n    public void addTo(StringBuilder sb, boolean newline, String ... args) {\n        sb.append(name());\n        for (String arg : args) {\n            sb.append(\" \").append(arg);\n        }\n        if (newline) {\n            sb.append(\"\\n\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/DockerFileOption.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\n/**\n * List of options for Docker keywords\n */\npublic enum DockerFileOption\n{\n    HEALTHCHECK_INTERVAL(\"interval\"),\n    HEALTHCHECK_TIMEOUT(\"timeout\"),\n    HEALTHCHECK_START_PERIOD(\"start-period\"),\n    HEALTHCHECK_RETRIES(\"retries\");\n\n    private String key;\n\n    DockerFileOption(String key) {\n        this.key = key;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    /**\n     * Appends the option with the giv\n     */\n    public void addTo(StringBuilder sb, Object value) {\n        sb.append(\"--\");\n        sb.append(getKey());\n        sb.append(\"=\");\n        sb.append(value);\n        sb.append(\" \");\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/assembly/MappingTrackArchiver.java",
    "content": "package io.fabric8.maven.docker.assembly;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.*;\nimport java.util.ArrayList;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.maven.artifact.*;\nimport org.apache.maven.artifact.handler.DefaultArtifactHandler;\nimport org.apache.maven.artifact.repository.ArtifactRepository;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.shared.utils.io.DirectoryScanner;\nimport org.codehaus.plexus.archiver.Archiver;\nimport org.codehaus.plexus.archiver.FileSet;\nimport org.codehaus.plexus.archiver.diags.TrackingArchiver;\nimport org.codehaus.plexus.component.annotations.Component;\nimport org.codehaus.plexus.components.io.resources.PlexusIoFileResource;\n\n/**\n * An archiver which remembers all resolved files and directories and returns them\n * on request.\n *\n * @author roland\n * @since 15/06/15\n */\n@Component(role = Archiver.class, hint = \"track\", instantiationStrategy = \"singleton\")\npublic class MappingTrackArchiver extends TrackingArchiver {\n\n    // Logger to use\n    protected Logger log;\n\n    // Target directory to use for storing the assembly files (== name)\n    private String assemblyName;\n\n    /**\n     * Get all files depicted by this assembly.\n     *\n     * @return assembled files\n     */\n    public AssemblyFiles getAssemblyFiles(MavenSession session) {\n        AssemblyFiles ret = new AssemblyFiles(new File(getDestFile().getParentFile(), assemblyName));\n        // Where the 'real' files are copied to\n        for (Addition addition : added) {\n            Object resource = addition.resource;\n            File target = new File(ret.getAssemblyDirectory(), addition.destination);\n            if (resource instanceof File && addition.destination != null) {\n                addFileEntry(ret, session, (File) resource, target);\n            } else if (resource instanceof PlexusIoFileResource) {\n                addFileEntry(ret, session, ((PlexusIoFileResource) resource).getFile(), target);\n            } else if (resource instanceof FileSet) {\n                FileSet fs = (FileSet) resource;\n                DirectoryScanner ds = new DirectoryScanner();\n                File base = addition.directory;\n                ds.setBasedir(base);\n                ds.setIncludes(fs.getIncludes());\n                ds.setExcludes(fs.getExcludes());\n                ds.setCaseSensitive(fs.isCaseSensitive());\n                ds.scan();\n                for (String f : ds.getIncludedFiles()) {\n                    File source = new File(base, f);\n                    File subTarget = new File(target, f);\n                    addFileEntry(ret, session, source, subTarget);\n                }\n            } else {\n                throw new IllegalStateException(\"Unknown resource type \" + resource.getClass() + \": \" + resource);\n            }\n        }\n        return ret;\n    }\n\n    private void addFileEntry(AssemblyFiles ret, MavenSession session, File source, File target) {\n        ret.addEntry(source, target);\n        addLocalMavenRepoEntry(ret, session, source, target);\n    }\n\n    private void addLocalMavenRepoEntry(AssemblyFiles ret, MavenSession session, File source, File target) {\n        File localMavenRepoFile = getLocalMavenRepoFile(session, source);\n        try {\n            if (localMavenRepoFile != null &&\n                ! source.getCanonicalFile().equals(localMavenRepoFile.getCanonicalFile())) {\n                ret.addEntry(localMavenRepoFile, target);\n            }\n        } catch (IOException e) {\n            log.warn(\"Cannot add %s for watching: %s. Ignoring for watch ...\", localMavenRepoFile, e.getMessage());\n        }\n    }\n\n    private File getLocalMavenRepoFile(MavenSession session, File source) {\n        ArtifactRepository localRepo = session.getLocalRepository();\n        if (localRepo == null) {\n            log.warn(\"No local repo found so not adding any extra watches in the local repository\");\n            return null;\n        }\n\n        Artifact artifact = getArtifactFromJar(source);\n        if (artifact != null) {\n            try {\n                return new File(localRepo.getBasedir(), localRepo.pathOf(artifact));\n            } catch (InvalidArtifactRTException e) {\n                log.warn(\"Cannot get the local repository path for %s in base dir %s : %s\",\n                         artifact, localRepo.getBasedir(), e.getMessage());\n            }\n        }\n        return null;\n    }\n\n    // look into a jar file and check for pom.properties. The first pom.properties found are returned.\n    private Artifact getArtifactFromJar(File jar) {\n        // Lets figure the real mvn source of file.\n        String type = extractFileType(jar);\n        if (type != null) {\n            try {\n                ArrayList<Properties> options = new ArrayList<Properties>();\n                try (ZipInputStream in = new ZipInputStream(new FileInputStream(jar))) {\n                    ZipEntry entry;\n                    while ((entry = in.getNextEntry()) != null) {\n                        if (entry.getName().startsWith(\"META-INF/maven/\") && entry.getName().endsWith(\"pom.properties\")) {\n                            byte[] buf = new byte[1024];\n                            int len;\n                            ByteArrayOutputStream out = new ByteArrayOutputStream(); //change ouptut stream as required\n                            while ((len = in.read(buf)) > 0) {\n                                out.write(buf, 0, len);\n                            }\n                            Properties properties = new Properties();\n                            properties.load(new ByteArrayInputStream(out.toByteArray()));\n                            options.add(properties);\n                        }\n                    }\n                }\n                if (options.size() == 1) {\n                    return getArtifactFromPomProperties(type,options.get(0));\n                } else {\n                    log.warn(\"Found %d pom.properties in %s\", options.size(), jar);\n                }\n            } catch (IOException e) {\n                log.warn(\"IO Exception while examining %s for maven coordinates: %s. Ignoring for watching ...\",\n                         jar, e.getMessage());\n            }\n        }\n        return null;\n    }\n\n    // type when it is a Java archive, null otherwise\n    private final static Pattern JAVA_ARCHIVE_DETECTOR = Pattern.compile(\"^.*\\\\.(jar|war|ear)$\");\n    private String extractFileType(File source) {\n        Matcher matcher = JAVA_ARCHIVE_DETECTOR.matcher(source.getName());\n        return matcher.matches() ? matcher.group(1) : null;\n    }\n\n    private Artifact getArtifactFromPomProperties(String type, Properties pomProps) {\n        return new DefaultArtifact(\n                pomProps.getProperty(\"groupId\"),\n                pomProps.getProperty(\"artifactId\"),\n                pomProps.getProperty(\"version\"),\n                \"runtime\",\n                type,\n                pomProps.getProperty(\"classifier\", \"\"),\n                new DefaultArtifactHandler(type)\n        );\n    }\n\n    public void init(Logger log, String assemblyName) {\n        this.log = log;\n        this.assemblyName = assemblyName;\n        added.clear();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/ArchiveCompression.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2015 Roland Huss\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\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;\nimport org.codehaus.plexus.archiver.tar.TarArchiver;\n\n/**\n * Enumeration for determine the compression mode when creating docker\n * build archives.\n *\n * @author roland\n * @since 26/10/15\n */\npublic enum ArchiveCompression {\n\n    none(TarArchiver.TarCompressionMethod.none, \"tar\"),\n\n    gzip(TarArchiver.TarCompressionMethod.gzip,\"tar.gz\") {\n        @Override\n        public OutputStream wrapOutputStream(OutputStream out) throws IOException {\n            return new GZIPOutputStream(out);\n        }\n    },\n\n    bzip2(TarArchiver.TarCompressionMethod.bzip2,\"tar.bz\") {\n        @Override\n        public OutputStream wrapOutputStream(OutputStream out) throws IOException {\n            return new BZip2CompressorOutputStream(out);\n        }\n    };\n\n    // ====================================================================\n\n    private final TarArchiver.TarCompressionMethod tarCompressionMethod;\n    private final String fileSuffix;\n\n    ArchiveCompression(TarArchiver.TarCompressionMethod tarCompressionMethod, String fileSuffix) {\n        this.tarCompressionMethod = tarCompressionMethod;\n        this.fileSuffix = fileSuffix;\n    }\n\n    public TarArchiver.TarCompressionMethod getTarCompressionMethod() {\n        return tarCompressionMethod;\n    }\n\n    public String getFileSuffix() {\n        return fileSuffix;\n    }\n\n    public OutputStream wrapOutputStream(OutputStream outputStream) throws IOException {\n        return outputStream;\n    }\n\n    public static ArchiveCompression fromFileName(String filename) {\n\t\tif (filename.endsWith(\".tar.gz\") || filename.endsWith(\".tgz\")) {\n\t\t\treturn ArchiveCompression.gzip;\n\t\t}\n\n        if (filename.endsWith(\".tar.bz\") || filename.endsWith(\".tar.bzip2\") || filename.endsWith(\".tar.bz2\")) {\n            return ArchiveCompression.bzip2;\n        }\n        return ArchiveCompression.none;\n    }\n\n    private static final int GZIP_BUFFER_SIZE = 65536;\n    // According to https://bugs.openjdk.java.net/browse/JDK-8142920, 3 is a better default\n    private static final int GZIP_COMPRESSION_LEVEL = 3;\n\n    private static class GZIPOutputStream extends java.util.zip.GZIPOutputStream {\n        private GZIPOutputStream(OutputStream out) throws IOException {\n            super(out, GZIP_BUFFER_SIZE);\n            def.setLevel(GZIP_COMPRESSION_LEVEL);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/Arguments.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport org.apache.maven.plugins.annotations.Parameter;\n\nimport java.io.Serializable;\nimport java.util.*;\n\npublic class Arguments implements Serializable {\n\n    @Parameter\n    private String shell;\n\n    @Parameter\n    private List<String> exec;\n\n    /**\n     * Used to distinguish between shorter version\n     *\n     * <pre>\n     *   &lt;cmd&gt;\n     *     &lt;arg&gt;echo&lt;/arg&gt;\n     *     &lt;arg&gt;Hello, world!&lt;/arg&gt;\n     *   &lt;/cmd&gt;\n     * </pre>\n     *\n     * from the full one\n     *\n     * <pre>\n     *   &lt;cmd&gt;\n     *     &lt;exec&gt;\n     *       &lt;arg&gt;echo&lt;/arg&gt;\n     *       &lt;arg&gt;Hello, world!&lt;/arg&gt;\n     *     &lt;exec&gt;\n     *   &lt;/cmd&gt;\n     * </pre>\n     *\n     * and throw a validation error if both specified.\n     */\n    private List<String> execInlined = new ArrayList<>();\n\n    public Arguments() { }\n\n    public Arguments(String shell) {\n        this.shell = shell;\n    }\n\n    public Arguments(List<String> exec) { this.exec = exec; }\n\n    /**\n     * Used to support shell specified as a default parameter, e.g.\n     *\n     * <pre>\n     *   &lt;cmd&gt;java -jar $HOME/server.jar&lt;/cmd&gt;\n     * </pre>\n     *\n     * Read <a href=\"http://blog.sonatype.com/2011/03/configuring-plugin-goals-in-maven-3/#.VeR3JbQ56Rv\">more</a> on\n     * this and other useful techniques.\n     *\n     */\n    public void set(String shell) {\n        setShell(shell);\n    }\n\n    public void setShell(String shell) {\n        this.shell = shell;\n    }\n\n    public String getShell() {\n        return shell;\n    }\n\n    public void setExec(List<String> exec) {\n        this.exec = exec;\n    }\n\n    /**\n     * @see Arguments#execInlined\n     */\n    @SuppressWarnings(\"unused\")\n    public void setArg(String arg) {\n        this.execInlined.add(arg);\n    }\n\n    public List<String> getExec() {\n        return exec == null ? execInlined : exec;\n    }\n\n    public void validate() throws IllegalArgumentException {\n        int valueSources = 0;\n        if (shell != null) {\n            valueSources ++;\n        }\n        if (exec != null && !exec.isEmpty()) {\n            valueSources ++;\n        }\n        if (!execInlined.isEmpty()) {\n            valueSources ++;\n        }\n\n        if (valueSources != 1){\n            throw new IllegalArgumentException(\"Argument conflict: either shell or args should be specified and only in one form.\");\n        }\n    }\n\n    public List<String> asStrings() {\n        if (shell != null) {\n            return Arrays.asList(EnvUtil.splitOnSpaceWithEscape(shell));\n        }\n        if (exec != null) {\n            return Collections.unmodifiableList(exec);\n        }\n        return Collections.unmodifiableList(execInlined);\n    }\n\n    public static class Builder {\n        private String shell;\n        private List<String> params;\n\n        public static Builder get(){\n            return new Builder();\n        }\n\n        public Builder withShell(String shell){\n            this.shell = shell;\n            return this;\n        }\n\n        public Builder withParam(String param){\n            if (params == null) {\n                params = new ArrayList<>();\n            }\n            this.params.add(param);\n            return this;\n        }\n\n        public Arguments build(){\n            Arguments a = new Arguments();\n            a.setShell(shell);\n            a.setExec(params);\n            return a;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/AssemblyConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\n\nimport java.io.Serializable;\n\nimport org.apache.maven.plugins.assembly.model.Assembly;\nimport org.apache.maven.plugins.annotations.Parameter;\n\npublic class AssemblyConfiguration implements Serializable {\n\n    /**\n     * @deprecated Use 'targetDir' instead\n     */\n    @Deprecated\n    private String basedir;\n\n    /**\n     * New replacement for base directory which better reflects its\n     * purpose\n     */\n    @Parameter\n    private String targetDir;\n\n    /**\n     * Name of the assembly which is used also as name of the archive\n     * which is created and has to be used when providing an own Dockerfile\n     */\n    @Parameter\n    private String name = \"maven\";\n\n    @Parameter\n    private String descriptor;\n\n    @Parameter\n    private Assembly inline;\n\n    @Parameter\n    private String descriptorRef;\n\n    /**\n     * @deprecated Use {@link BuildImageConfiguration#dockerFileDir} instead\n     */\n    @Parameter\n    @Deprecated\n    private String dockerFileDir;\n\n    // use 'exportTargetDir' instead\n    @Deprecated\n    private Boolean exportBasedir;\n\n    /**\n     * Whether the target directory should be\n     * exported.\n     *\n     */\n    @Parameter\n    private Boolean exportTargetDir;\n\n    /**\n     * @deprecated use permissionMode == ignore instead.\n     */\n    @Parameter\n    private Boolean ignorePermissions;\n\n    @Deprecated\n    public Boolean getIgnorePermissions() {\n        return ignorePermissions;\n    }\n\n    @Parameter\n    private AssemblyMode mode;\n\n    @Parameter\n    private String user;\n\n    @Parameter\n    private String tarLongFileMode;\n\n    public Boolean getExportTargetDir() {\n        return exportTargetDir;\n    }\n\n    public Boolean exportTargetDir() {\n        if (exportTargetDir != null) {\n            return exportTargetDir;\n        } else if (exportBasedir != null) {\n            return exportBasedir;\n        } else {\n            return null;\n        }\n    }\n\n    public String getTargetDir() {\n        if (targetDir != null) {\n            return targetDir;\n        } else if (basedir != null) {\n            return basedir;\n        } else {\n            return \"/\" + getName();\n        }\n    }\n\n    /**\n     * @parameter default-value=\"keep\"\n     */\n    private PermissionMode permissions;\n\n    public Assembly getInline() {\n        return inline;\n    }\n\n    public String getDescriptor() {\n        return descriptor;\n    }\n\n    public String getDescriptorRef() {\n        return descriptorRef;\n    }\n\n    public String getDockerFileDir() {\n        return dockerFileDir;\n    }\n\n    public String getUser() {\n        return user;\n    }\n\n    public AssemblyMode getMode() {\n        return mode != null ? mode : AssemblyMode.dir;\n    }\n\n    public String getModeRaw() {\n        return mode != null ? mode.name() : null;\n    }\n\n    public String getTarLongFileMode() {\n        return tarLongFileMode;\n    }\n\n    public Boolean isIgnorePermissions() {\n        // New permission mode has precedence\n        if (permissions != null) {\n            return permissions == PermissionMode.ignore;\n        }\n        return (ignorePermissions != null) ? ignorePermissions : Boolean.FALSE;\n    }\n\n     public PermissionMode getPermissions() {\n        return permissions != null ? permissions : PermissionMode.keep;\n    }\n\n    public String getPermissionsRaw() {\n        return permissions != null ? permissions.name() : null;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public static class Builder {\n\n        private final AssemblyConfiguration config = new AssemblyConfiguration();\n        private boolean isEmpty = true;\n\n        public AssemblyConfiguration build() {\n            return isEmpty ? null : config;\n        }\n\n        public Builder targetDir(String targetDir) {\n            config.targetDir = set(targetDir);\n            return this;\n        }\n\n        public Builder assemblyDef(Assembly descriptor) {\n            config.inline = set(descriptor);\n            return this;\n        }\n\n        public Builder descriptor(String descriptorFile) {\n            config.descriptor = set(descriptorFile);\n            return this;\n        }\n\n        public Builder descriptorRef(String descriptorRef) {\n            config.descriptorRef = set(descriptorRef);\n            return this;\n        }\n\n        public Builder dockerFileDir(String dockerFileDir) {\n            config.dockerFileDir = set(dockerFileDir);\n            return this;\n        }\n\n        public Builder exportBasedir(Boolean export) {\n            config.exportBasedir = set(export);\n            return this;\n        }\n\n        @Deprecated\n        public Builder ignorePermissions(Boolean ignorePermissions) {\n            config.ignorePermissions = set(ignorePermissions);\n            return this;\n        }\n\n        public Builder permissions(String permissions) {\n            if (permissions != null) {\n                config.permissions = PermissionMode.valueOf(permissions.toLowerCase());\n            }\n            return this;\n        }\n\n        public Builder user(String user) {\n            config.user = set(user);\n            return this;\n        }\n\n        public Builder mode(String mode) {\n            if (mode != null) {\n                config.mode = AssemblyMode.valueOf(mode.toLowerCase());\n                isEmpty = false;\n            }\n            return this;\n        }\n\n        public Builder tarLongFileMode(String tarLongFileMode) {\n            config.tarLongFileMode = set(tarLongFileMode);\n            return this;\n        }\n\n         private <T> T set(T prop) {\n            if (prop != null) {\n                isEmpty = false;\n            }\n            return prop;\n        }\n    }\n\n    public enum PermissionMode {\n\n        /**\n         * Auto detect permission mode\n         */\n        auto,\n\n        /**\n         * Make everything executable\n         */\n        exec,\n\n        /**\n         * Leave all as it is\n         */\n        keep,\n\n        /**\n         * Ignore permission when using an assembly mode of \"dir\"\n         */\n        ignore\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/AssemblyMode.java",
    "content": "package io.fabric8.maven.docker.config;/*\n * \n * Copyright 2014 Roland Huss\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\n/**\n * Various modes how to add file for the tarball for \"docker:build\".\n *\n * @author roland\n * @since 18/05/15\n */\npublic enum AssemblyMode {\n\n    /**\n     * Copy files directly in the directory\n     */\n    dir(\"dir\",false),\n\n    /**\n     * Use a ZIP container as intermediate format\n     */\n    zip(\"zip\",true),\n\n    /**\n     * Use a TAR container as intermediate format\n     */\n    tar(\"tar\",true),\n\n    /**\n     * Use a compressed TAR container as intermediate format\n     */\n    tgz(\"tgz\",true);\n\n    private final String extension;\n    private boolean isArchive;\n\n    AssemblyMode(String extension, boolean isArchive) {\n        this.extension = extension;\n        this.isArchive = isArchive;\n    }\n\n    /**\n     * Get the extension as known by the Maven assembler\n     *\n     * @return extension\n     */\n    public String getExtension() {\n        return extension;\n    }\n\n    public boolean isArchive() {\n        return isArchive;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/BuildImageConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.File;\nimport java.io.Serializable;\nimport java.util.*;\n\nimport io.fabric8.maven.docker.util.*;\nimport org.apache.maven.plugins.annotations.Parameter;\n\nimport javax.annotation.Nonnull;\n\n/**\n * @author roland\n * @since 02.09.14\n */\npublic class BuildImageConfiguration implements Serializable {\n\n    public static final String DEFAULT_FILTER = \"${*}\";\n    public static final String DEFAULT_CLEANUP = \"try\";\n\n    /**\n     * Directory is used as build context.\n     * If not specified, dockerfile's parent directory is used as build context.\n     */\n    @Parameter\n    private String contextDir;\n\n    /**\n     * Directory holding an external Dockerfile which is used to build the\n     * image. This Dockerfile will be enriched by the addition build configuration\n     */\n    @Parameter\n    @Deprecated\n    private String dockerFileDir;\n\n    /**\n     * Path to a dockerfile to use.\n     * Multiple different Dockerfiles can be specified that way. If set overwrites a possibly givem\n     * <code>dockerFileDir</code>\n     */\n    @Parameter\n    private String dockerFile;\n\n    /**\n     * Path to a docker archive to load an image instead of building from scratch.\n     * Note only either dockerFile/dockerFileDir or\n     * dockerArchive can be used.\n     */\n    @Parameter\n    private String dockerArchive;\n\n    /**\n     * Pattern for the image name we expect to find in the dockerArchive.\n     *\n     * If set, the archive is scanned prior to sending to Docker and checked to\n     * ensure a matching name is found linked to one of the images in the archive.\n     * After loading, the image with the matching name will be tagged with the\n     * image name configured in this project.\n     */\n    @Parameter\n    private String loadNamePattern;\n\n    /**\n     * How interpolation of a dockerfile should be performed\n     */\n    @Parameter\n    private String filter;\n\n    /**\n     * Base Image\n     */\n    @Parameter\n    private String from;\n\n    /**\n     * Extended version for <from>\n     */\n    @Parameter\n    private Map<String, String> fromExt;\n\n    @Parameter\n    private List<String> cacheFrom;\n\n    @Parameter\n    private String registry;\n\n    @Parameter\n    private String maintainer;\n\n    @Parameter\n    private String network;\n\n    @Parameter\n    private List<String> ports;\n\n    /**\n     * Policy for pulling the base images\n     */\n    @Parameter\n    private String imagePullPolicy;\n\n    /**\n     * SHELL excutable with params\n     */\n    @Parameter\n    private Arguments shell;\n\n    /**\n     * RUN Commands within Build/Image\n     */\n    @Parameter\n    private List<String> runCmds;\n\n    @Parameter\n    private String cleanup;\n\n    @Deprecated\n    @Parameter\n    private Boolean nocache;\n\n    @Parameter\n    private Boolean noCache;\n\n    @Parameter\n    private Boolean squash;\n\n    @Parameter\n    private Boolean optimise;\n\n    @Parameter\n    private List<String> volumes;\n\n    @Parameter\n    private List<String> tags;\n\n    @Parameter\n    private Map<String, String> env;\n\n    @Parameter\n    private Map<String, String> labels;\n\n    @Parameter\n    private Map<String, String> args;\n\n    @Parameter\n    private Arguments entryPoint;\n\n    @Parameter\n    @Deprecated\n    private String command;\n\n    @Parameter\n    private String workdir;\n\n    @Parameter\n    private Arguments cmd;\n\n    @Parameter\n    private String user;\n\n    @Parameter\n    private HealthCheckConfiguration healthCheck;\n\n    @Parameter\n    private AssemblyConfiguration assembly;\n\n    @Parameter\n    private Boolean skip;\n\n    @Parameter\n    private Boolean skipPush;\n\n    @Parameter\n    private ArchiveCompression compression = ArchiveCompression.none;\n\n    @Parameter\n    private Map<String,String> buildOptions;\n\n    // Path to Dockerfile to use, initialized lazily ....\n    private File dockerFileFile, dockerArchiveFile;\n\n    public BuildImageConfiguration() {}\n\n    public boolean isDockerFileMode() {\n        return dockerFileFile != null;\n    }\n\n    public String getLoadNamePattern() {\n        return loadNamePattern;\n    }\n\n    public File getContextDir() {\n        if (!isDockerFileMode()) {\n            return null;\n        }\n        if (contextDir != null) {\n            return new File(contextDir);\n        }\n        if (getDockerFile().getParentFile() == null) {\n            return new File(\"\");\n        }\n        return getDockerFile().getParentFile();\n    }\n\n    public String getContextDirRaw() {\n        return contextDir;\n    }\n\n    public File getDockerFile() {\n        return dockerFileFile;\n    }\n\n    public File getDockerArchive() {\n        return dockerArchiveFile;\n    }\n\n    public String getDockerFileRaw() {\n        return dockerFile;\n    }\n\n    public String getDockerArchiveRaw() {\n        return dockerArchive;\n    }\n\n    public String getDockerFileDirRaw() {\n        return dockerFileDir;\n    }\n\n    public String getFilter() {\n        return filter != null ? filter : DEFAULT_FILTER;\n    }\n\n    public String getFilterRaw() {\n        return filter;\n    }\n\n    public String getFrom() {\n        if (from == null && getFromExt() != null) {\n            return getFromExt().get(\"name\");\n        }\n        return from;\n    }\n\n    public Map<String, String> getFromExt() {\n        return fromExt;\n    }\n\n    public List<String> getCacheFrom() {\n        return cacheFrom;\n    }\n\n    public String getNetwork() {\n        return network;\n    }\n\n    public String getRegistry() {\n        return registry;\n    }\n\n    public String getMaintainer() {\n        return maintainer;\n    }\n\n    public String getWorkdir() {\n        return workdir;\n    }\n\n    public AssemblyConfiguration getAssemblyConfiguration() {\n        return assembly;\n    }\n\n    @Nonnull\n    public List<String> getPorts() {\n        return EnvUtil.removeEmptyEntries(ports);\n    }\n\n    public String getImagePullPolicy() {\n        return imagePullPolicy;\n    }\n\n    @Nonnull\n    public List<String> getVolumes() {\n        return EnvUtil.removeEmptyEntries(volumes);\n    }\n\n    @Nonnull\n    public List<String> getTags() {\n        return EnvUtil.removeEmptyEntries(tags);\n    }\n\n    public Map<String, String> getEnv() {\n        return env;\n    }\n\n    public Map<String, String> getLabels() {\n        return labels;\n    }\n\n    public Arguments getCmd() {\n        return cmd;\n    }\n\n    @Deprecated\n    public String getCommand() {\n        return command;\n    }\n\n    public String getCleanup() {\n        return cleanup;\n    }\n\n    public CleanupMode cleanupMode() {\n        return CleanupMode.parse(cleanup != null ? cleanup : DEFAULT_CLEANUP);\n    }\n\n    public boolean noCache() {\n        if (noCache != null) {\n            return noCache;\n        }\n        if (nocache != null) {\n            return nocache;\n        }\n        return false;\n    }\n\n    public boolean squash() {\n        if (squash != null) {\n            return squash;\n        }\n        return false;\n    }\n\n    public boolean optimise() {\n        return optimise != null ? optimise : false;\n    }\n\n    public boolean skip() {\n        return skip != null ? skip : false;\n    }\n\n    public boolean skipPush() {\n        return skipPush != null ? skipPush : false;\n    }\n\n    public Boolean getNoCache() {\n        return noCache != null ? noCache : nocache;\n    }\n\n    public Boolean getSquash() {\n        return squash != null ? squash : false;\n    }\n\n    public Boolean getOptimise() {\n        return optimise;\n    }\n\n    public Boolean getSkip() {\n        return skip;\n    }\n\n    public Boolean getSkipPush() {\n        return skipPush;\n    }\n\n    public ArchiveCompression getCompression() {\n        return compression;\n    }\n\n    public Map<String, String> getBuildOptions() {\n        return buildOptions;\n    }\n\n    public Arguments getEntryPoint() {\n        return entryPoint;\n    }\n\n    public Arguments getShell() {\n        return shell;\n    }\n\n    @Nonnull\n    public List<String> getRunCmds() {\n        return EnvUtil.removeEmptyEntries(runCmds);\n    }\n\n    public String getUser() {\n      return user;\n    }\n\n    public HealthCheckConfiguration getHealthCheck() {\n        return healthCheck;\n    }\n\n    public Map<String, String> getArgs() {\n        return args;\n    }\n\n    public File getAbsoluteContextDirPath(MojoParameters mojoParams) {\n        return EnvUtil.prepareAbsoluteSourceDirPath(mojoParams, getContextDir().getPath());\n    }\n\n    public File getAbsoluteDockerFilePath(MojoParameters mojoParams) {\n        return EnvUtil.prepareAbsoluteSourceDirPath(mojoParams, getDockerFile().getPath());\n    }\n\n    public File getAbsoluteDockerTarPath(MojoParameters mojoParams) {\n        return EnvUtil.prepareAbsoluteSourceDirPath(mojoParams, getDockerArchive().getPath());\n    }\n\n    public static class Builder {\n        private final BuildImageConfiguration config;\n\n        public Builder() {\n            this(null);\n        }\n\n        public Builder(BuildImageConfiguration that) {\n            if (that == null) {\n                this.config = new BuildImageConfiguration();\n            } else {\n                this.config = DeepCopy.copy(that);\n            }\n        }\n\n        public Builder contextDir(String dir) {\n            config.contextDir = dir;\n            return this;\n        }\n\n        public Builder dockerFileDir(String dir) {\n            config.dockerFileDir = dir;\n            return this;\n        }\n\n        public Builder dockerFile(String file) {\n            config.dockerFile = file;\n            return this;\n        }\n\n        public Builder dockerArchive(String archive) {\n            config.dockerArchive = archive;\n            return this;\n        }\n\n        public Builder loadNamePattern(String archiveEntryRepoTagPattern) {\n            config.loadNamePattern = archiveEntryRepoTagPattern;\n            return this;\n        }\n\n        public Builder filter(String filter) {\n            config.filter = filter;\n            return this;\n        }\n\n        public Builder from(String from) {\n            config.from = from;\n            return this;\n        }\n\n        public Builder fromExt(Map<String, String> fromExt) {\n            config.fromExt = fromExt;\n            return this;\n        }\n\n        public Builder cacheFrom(String cacheFrom, String ...more) {\n            if (more == null || more.length == 0) {\n                return cacheFrom(Collections.singletonList(cacheFrom));\n            }\n\n            List<String> list = new ArrayList<>();\n            list.add(cacheFrom);\n            list.addAll(Arrays.asList(more));\n            return cacheFrom(list);\n        }\n\n        public Builder cacheFrom(Collection<String> cacheFrom) {\n            config.cacheFrom = cacheFrom != null ? new ArrayList<>(cacheFrom) : null;\n            return this;\n        }\n\n        public Builder registry(String registry) {\n            config.registry = registry;\n            return this;\n        }\n\n        public Builder maintainer(String maintainer) {\n            config.maintainer = maintainer;\n            return this;\n        }\n\n        public Builder network(String network) {\n            config.network = network;\n            return this;\n        }\n\n        public Builder workdir(String workdir) {\n            config.workdir = workdir;\n            return this;\n        }\n\n        public Builder assembly(AssemblyConfiguration assembly) {\n            config.assembly = assembly;\n            return this;\n        }\n\n        public Builder ports(List<String> ports) {\n            config.ports = ports;\n            return this;\n        }\n\n        public Builder imagePullPolicy(String imagePullPolicy) {\n            config.imagePullPolicy = imagePullPolicy;\n            return this;\n        }\n\n        public Builder shell(Arguments shell) {\n            if(shell != null) {\n                config.shell = shell;\n            }\n\n            return this;\n        }\n\n        public Builder runCmds(List<String> theCmds) {\n            if (theCmds == null) {\n                config.runCmds = new ArrayList<>();\n            } else {\n                config.runCmds = theCmds;\n            }\n            return this;\n        }\n\n        public Builder volumes(List<String> volumes) {\n            config.volumes = volumes;\n            return this;\n        }\n\n        public Builder tags(List<String> tags) {\n            config.tags = tags;\n            return this;\n        }\n\n        public Builder env(Map<String, String> env) {\n            config.env = env;\n            return this;\n        }\n\n        public Builder args(Map<String, String> args) {\n            config.args = args;\n            return this;\n        }\n\n        public Builder labels(Map<String, String> labels) {\n            config.labels = labels;\n            return this;\n        }\n\n        public Builder cmd(Arguments cmd) {\n            if (cmd != null) {\n                config.cmd = cmd;\n            }\n            return this;\n        }\n\n        public Builder cleanup(String cleanup) {\n            config.cleanup = cleanup;\n            return this;\n        }\n\n        public Builder compression(String compression) {\n            if (compression == null) {\n                config.compression = ArchiveCompression.none;\n            } else {\n                config.compression = ArchiveCompression.valueOf(compression);\n            }\n            return this;\n        }\n\n        public Builder noCache(Boolean noCache) {\n            config.noCache = noCache;\n            return this;\n        }\n\n        public Builder squash(Boolean squash) {\n            config.squash = squash;\n            return this;\n        }\n\n        public Builder optimise(Boolean optimise) {\n            config.optimise = optimise;\n            return this;\n        }\n\n        public Builder entryPoint(Arguments entryPoint) {\n            if (entryPoint != null) {\n                config.entryPoint = entryPoint;\n            }\n            return this;\n        }\n\n        public Builder user(String user) {\n            config.user = user;\n            return this;\n        }\n\n        public Builder healthCheck(HealthCheckConfiguration healthCheck) {\n            config.healthCheck = healthCheck;\n            return this;\n        }\n\n        public Builder skip(Boolean skip) {\n            config.skip = skip;\n            return this;\n        }\n\n        public Builder skipPush(Boolean skipPush) {\n            config.skipPush = skipPush;\n            return this;\n        }\n\n        public Builder buildOptions(Map<String,String> buildOptions) {\n            config.buildOptions = buildOptions;\n            return this;\n        }\n\n        public BuildImageConfiguration build() {\n            return config;\n        }\n    }\n\n    public String initAndValidate(Logger log) throws IllegalArgumentException {\n        if (entryPoint != null) {\n            entryPoint.validate();\n        }\n        if (cmd != null) {\n            cmd.validate();\n        }\n        if (healthCheck != null) {\n            healthCheck.validate();\n        }\n\n        if (command != null) {\n            log.warn(\"<command> in the <build> configuration is deprecated and will be be removed soon\");\n            log.warn(\"Please use <cmd> with nested <shell> or <exec> sections instead.\");\n            log.warn(\"\");\n            log.warn(\"More on this is explained in the user manual: \");\n            log.warn(\"https://github.com/fabric8io/docker-maven-plugin/blob/master/doc/manual.md#start-up-arguments\");\n            log.warn(\"\");\n            log.warn(\"Migration is trivial, see changelog to version 0.12.0 -->\");\n            log.warn(\"https://github.com/fabric8io/docker-maven-plugin/blob/master/doc/changelog.md\");\n            log.warn(\"\");\n            log.warn(\"For now, the command is automatically translated for you to the shell form:\");\n            log.warn(\"   <cmd>%s</cmd>\", command);\n        }\n\n        initDockerFileFile(log);\n\n        if (cacheFrom != null && !cacheFrom.isEmpty()) {\n            // cachefrom query param was introduced in v1.25\n            return \"1.25\";\n        } else if (healthCheck != null) {\n            // HEALTHCHECK support added later\n            return \"1.24\";\n        } else if (args != null) {\n            // ARG support came in later\n            return \"1.21\";\n        } else {\n            return null;\n        }\n    }\n\n    // Initialize the dockerfile location and the build mode\n    private void initDockerFileFile(Logger log) {\n        // can't have dockerFile/dockerFileDir and dockerArchive\n        if ((dockerFile != null || dockerFileDir != null) && dockerArchive != null) {\n            throw new IllegalArgumentException(\"Both <dockerFile> (<dockerFileDir>) and <dockerArchive> are set. \" +\n                                               \"Only one of them can be specified.\");\n        }\n        dockerFileFile = findDockerFileFile(log);\n\n        if (dockerArchive != null) {\n            dockerArchiveFile = new File(dockerArchive);\n        }\n    }\n\n    private File findDockerFileFile(Logger log) {\n        if(dockerFileDir != null && contextDir != null) {\n            log.warn(\"Both contextDir (%s) and deprecated dockerFileDir (%s) are configured. Using contextDir.\", contextDir, dockerFileDir);\n        }\n\n        if (dockerFile != null) {\n            File dFile = new File(dockerFile);\n            if (dockerFileDir == null && contextDir == null) {\n                return dFile;\n            } else {\n                if(contextDir != null) {\n                    if (dFile.isAbsolute()) {\n                        return dFile;\n                    }\n                    return new File(contextDir, dockerFile);\n                }\n\n                if (dockerFileDir != null) {\n                    if (dFile.isAbsolute()) {\n                        throw new IllegalArgumentException(\"<dockerFile> can not be absolute path if <dockerFileDir> also set.\");\n                    }\n                    log.warn(\"dockerFileDir parameter is deprecated, please migrate to contextDir\");\n                    return new File(dockerFileDir, dockerFile);\n                }\n            }\n        }\n\n\n        if (contextDir != null) {\n            return new File(contextDir, \"Dockerfile\");\n        }\n\n        if (dockerFileDir != null) {\n            return new File(dockerFileDir, \"Dockerfile\");\n        }\n\n        // TODO: Remove the following deprecated handling section\n        if (dockerArchive == null) {\n            String deprecatedDockerFileDir =\n                getAssemblyConfiguration() != null ?\n                    getAssemblyConfiguration().getDockerFileDir() :\n                    null;\n            if (deprecatedDockerFileDir != null) {\n                log.warn(\"<dockerFileDir> in the <assembly> section of a <build> configuration is deprecated\");\n                log.warn(\"Please use <dockerFileDir> or <dockerFile> directly within the <build> configuration instead\");\n                return new File(deprecatedDockerFileDir,\"Dockerfile\");\n            }\n        }\n\n        // No dockerfile mode\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/BuildImageSelectMode.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\n/**\n * How to build source tar files.\n *\n * @author roland\n * @since 26/04/16\n */\npublic enum BuildImageSelectMode {\n\n    // Pick only the first build configuration\n    first,\n\n    // Include all builds with alias names as classifiers\n    all;\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/CleanupMode.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\n/**\n * Mode specifying how a cleanup should be performed.\n *\n * @author roland\n * @since 01/03/16\n */\npublic enum CleanupMode {\n    NONE(false, \"none\"),\n    TRY_TO_REMOVE(true, \"try\"),\n    REMOVE(true, \"remove\");\n\n    private final boolean remove;\n    private final String parameter;\n\n    CleanupMode(boolean remove, String parameter) {\n        this.remove = remove;\n        this.parameter = parameter;\n    }\n\n    public static CleanupMode parse(String param) {\n        if (param == null || param.equalsIgnoreCase(\"try\")) {\n            return TRY_TO_REMOVE;\n        } else if (param.equalsIgnoreCase(\"false\") || param.equalsIgnoreCase(\"none\")) {\n            return NONE;\n        } else if (param.equalsIgnoreCase(\"true\") || param.equalsIgnoreCase(\"remove\")) {\n            return REMOVE;\n        } else {\n            throw new IllegalArgumentException(\"Invalid clean up mode \" + param + \" (should be one of: none/try/remove)\");\n        }\n    }\n\n    public boolean isRemove() {\n        return remove;\n    }\n\n    public String toParameter() {\n        return parameter;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/ConfigHelper.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.handler.property.PropertyConfigHandler;\nimport io.fabric8.maven.docker.config.handler.property.PropertyMode;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.utils.StringUtils;\n\n/**\n * Utility class which helps in resolving, customizing, initializing and validating\n * image configuration.\n *\n * @author roland\n * @since 17/05/16\n */\npublic class ConfigHelper {\n    // Property which can be set to activate externalConfiguration through properties.\n    // Only works for single image project.\n    public static final String EXTERNALCONFIG_ACTIVATION_PROPERTY = \"docker.imagePropertyConfiguration\";\n\n    private ConfigHelper() {}\n\n    /**\n     * Resolve image with an external image resolver\n     *\n     * @param images the original image config list (can be null)\n     * @param imageResolver the resolver used to extend on an image configuration\n     * @param imageNameFilter filter to select only certain image configurations with the given name\n     * @param imageCustomizer final customization hook for mangling the configuration\n     * @return a list of resolved and customized image configuration.\n     */\n    public static List<ImageConfiguration> resolveImages(Logger logger,\n                                                         List<ImageConfiguration> images,\n                                                         Resolver imageResolver,\n                                                         String imageNameFilter,\n                                                         Customizer imageCustomizer) {\n        List<ImageConfiguration> ret = resolveConfiguration(imageResolver, images);\n        ret = imageCustomizer.customizeConfig(ret);\n        List<ImageConfiguration> filtered =  filterImages(imageNameFilter,ret);\n        if (ret.size() > 0 && filtered.size() == 0 && imageNameFilter != null) {\n            List<String> imageNames = new ArrayList<>();\n            for (ImageConfiguration image : ret) {\n                imageNames.add(image.getName());\n            }\n            logger.warn(\"None of the resolved images [%s] match the configured filter '%s'\",\n                        StringUtils.join(imageNames.iterator(), \",\"), imageNameFilter);\n        }\n        return filtered;\n    }\n\n    public static void validateExternalPropertyActivation(MavenProject project, List<ImageConfiguration> images) throws MojoFailureException {\n        String prop = getExternalConfigActivationProperty(project);\n        if(prop == null) {\n            return;\n        }\n\n        if(images.size() == 1) {\n            return;\n        }\n\n        // With more than one image, externally activating propertyConfig get's tricky. We can only allow it to affect\n        // one single image. Go through each image and check if they will be controlled by default properties.\n        // If more than one image matches, fail.\n        int imagesWithoutExternalConfig = 0;\n        for (ImageConfiguration image : images) {\n            if(PropertyConfigHandler.canCoexistWithOtherPropertyConfiguredImages(image.getExternalConfig())) {\n                continue;\n            }\n\n            // else, it will be affected by the external property.\n            imagesWithoutExternalConfig++;\n        }\n\n        if(imagesWithoutExternalConfig > 1) {\n            throw new MojoFailureException(\"Configuration error: Cannot use property \"+EXTERNALCONFIG_ACTIVATION_PROPERTY+\" on projects with multiple images without explicit image external configuration.\");\n        }\n    }\n\n    public static String getExternalConfigActivationProperty(MavenProject project) {\n        Properties properties = EnvUtil.getPropertiesWithSystemOverrides(project);\n        String value = properties.getProperty(EXTERNALCONFIG_ACTIVATION_PROPERTY);\n\n        // This can be used to disable in a more \"local\" context, if set globally\n        if(PropertyMode.Skip.name().equalsIgnoreCase(value)) {\n            return null;\n        }\n\n        return value;\n    }\n\n    /**\n     * Initialize and validate the configuration.\n     *\n     *\n     * @param images the images to check\n     * @param apiVersion the original API version intended to use\n     * @param nameFormatter formatter for image names\n     * @param log a logger for printing out diagnostic messages\n     * @return the minimal API Docker API required to be used for the given configuration.\n     */\n    public static String initAndValidate(List<ImageConfiguration> images, String apiVersion, NameFormatter nameFormatter,\n                                         Logger log) {\n        // Init and validate configs. After this step, getResolvedImages() contains the valid configuration.\n        for (ImageConfiguration imageConfiguration : images) {\n            apiVersion = EnvUtil.extractLargerVersion(apiVersion, imageConfiguration.initAndValidate(nameFormatter, log));\n        }\n        return apiVersion;\n    }\n\n    // Check if the provided image configuration matches the given\n    public static boolean matchesConfiguredImages(String imageList, ImageConfiguration imageConfig) {\n        if (imageList == null) {\n            return true;\n        }\n        Set<String> imagesAllowed = new HashSet<>(Arrays.asList(imageList.split(\"\\\\s*,\\\\s*\")));\n        return imagesAllowed.contains(imageConfig.getName()) || imagesAllowed.contains(imageConfig.getAlias());\n    }\n\n    // ===========================================================================================================\n\n    // Filter image configuration on name. Given filter should be either null (no filter) or a comma separated\n    // list of image names which should be used\n    private static List<ImageConfiguration> filterImages(String nameFilter, List<ImageConfiguration> imagesToFilter) {\n        List<ImageConfiguration> ret = new ArrayList<>();\n        for (ImageConfiguration imageConfig : imagesToFilter) {\n            if (matchesConfiguredImages(nameFilter, imageConfig)) {\n                ret.add(imageConfig);\n            }\n        }\n        return ret;\n    }\n\n    // Resolve and initialize external configuration\n    private static List<ImageConfiguration> resolveConfiguration(Resolver imageResolver,\n                                                                 List<ImageConfiguration> unresolvedImages) {\n        List<ImageConfiguration> ret = new ArrayList<>();\n        if (unresolvedImages != null) {\n            for (ImageConfiguration image : unresolvedImages) {\n                ret.addAll(imageResolver.resolve(image));\n            }\n            verifyImageNames(ret);\n        }\n        return ret;\n    }\n\n\n    // Extract authentication information\n    private static void verifyImageNames(List<ImageConfiguration> ret) {\n        for (ImageConfiguration config : ret) {\n            if (config.getName() == null) {\n                throw new IllegalArgumentException(\"Configuration error: <image> must have a non-null <name>\");\n            }\n        }\n    }\n\n\n    // =========================================================================\n\n    /**\n     * Allow subclasses to customize the given set of image configurations. This is called\n     * after resolving of images. a customizer is free to change the image configuration as he want.\n     * Use this with responsibility.\n     */\n    public interface Customizer {\n        List<ImageConfiguration> customizeConfig(List<ImageConfiguration> configs);\n    }\n\n    /**\n     * A resolver can map one given image configuration to one or more image configurations\n     * This is e.g. used for resolving properties\n     */\n    public interface Resolver {\n        List<ImageConfiguration> resolve(ImageConfiguration image);\n    }\n\n    /**\n     * Format an image name by replacing certain placeholders\n     */\n    public interface NameFormatter {\n        String format(String name);\n\n        NameFormatter IDENTITY = new NameFormatter() {\n            public String format(String name) {\n                return name;\n            }\n        };\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/DockerMachineConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\npublic class DockerMachineConfiguration implements Serializable {\n\n    public static final String DOCKER_MACHINE_NAME_PROP = \"docker.machine.name\";\n    public static final String DOCKER_MACHINE_AUTO_CREATE_PROP = \"docker.machine.autoCreate\";\n    public static final String DOCKER_MACHINE_REGENERATE_CERTS_AFTER_START_PROP = \"docker.machine.regenerateCertsAfterStart\";\n\n    /**\n     * Name of the docker-machine\n     */\n    @Parameter\n    private String name = \"default\";\n\n    /**\n     * Should the docker-machine be created if it does not exist?\n     */\n    @Parameter\n    private Boolean autoCreate = Boolean.FALSE;\n\n    /**\n     * Should the docker-machine's certificates be regenerated after starting?\n     */\n    @Parameter\n    private Boolean regenerateCertsAfterStart = Boolean.FALSE;\n\n    /**\n     * When creating a docker-machine, the map of createOptions for the driver.\n     * Do not include the '--' portion of the option name.  For options without values, leave the value text empty.\n     * e.g. --virtualbox-cpu-count 1 --virtualbox-no-share would be written as:<code>\n     * &lt;virtualbox-cpu-count&gt;1&lt;/virtualbox-cpu-count&gt;\n     * &lt;virtualbox-no-share/&gt;\n     * </code>\n     */\n    @Parameter\n    private Map<String, String> createOptions;\n\n    public DockerMachineConfiguration() {}\n\n    public DockerMachineConfiguration(String name, String autoCreate, String regenerateCertsAfterStart) {\n        this.name = name;\n        this.autoCreate = autoCreate != null ? Boolean.parseBoolean(autoCreate) : Boolean.FALSE;\n        this.regenerateCertsAfterStart = regenerateCertsAfterStart != null ? Boolean.parseBoolean(regenerateCertsAfterStart) : Boolean.FALSE;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Boolean getAutoCreate() {\n        return autoCreate;\n    }\n\n    public Boolean getRegenerateCertsAfterStart() {\n        return regenerateCertsAfterStart;\n    }\n\n    public Map<String, String> getCreateOptions() {\n        return createOptions;\n    }\n\n    @Override\n    public String toString() {\n        return \"MachineConfiguration [name=\" + name + \", autoCreate=\" + autoCreate + \",regenerateCertsAfterStart=\" + regenerateCertsAfterStart + \", createOptions=\" + createOptions + \"]\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/HealthCheckConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\n\n/**\n * Build configuration for health checks.\n */\npublic class HealthCheckConfiguration implements Serializable {\n\n    private HealthCheckMode mode = HealthCheckMode.cmd;\n\n    private String interval;\n\n    private String timeout;\n\n    private String startPeriod;\n\n    private Integer retries;\n\n    private Arguments cmd;\n\n    public HealthCheckConfiguration() {}\n\n    public String getInterval() {\n        return prepareTimeValue(interval);\n    }\n\n    public String getTimeout() {\n        return prepareTimeValue(timeout);\n    }\n\n    public String getStartPeriod() {\n        return prepareTimeValue(startPeriod);\n    }\n\n    private String prepareTimeValue(String timeout) {\n        // Seconds as default\n        if (timeout == null) {\n            return null;\n        }\n        return timeout.matches(\"^\\\\d+$\") ? timeout + \"s\" : timeout;\n    }\n\n    public Arguments getCmd() {\n        return cmd;\n    }\n\n    public HealthCheckMode getMode() {\n        return mode;\n    }\n\n    public Integer getRetries() {\n        return retries;\n    }\n\n    public void validate() throws IllegalArgumentException {\n        if (mode == null) {\n            throw new IllegalArgumentException(\"HealthCheck: mode must not be null\");\n        }\n\n        switch(mode) {\n        case none:\n            if (interval != null || timeout != null || startPeriod != null || retries != null || cmd != null) {\n                throw new IllegalArgumentException(\"HealthCheck: no parameters are allowed when the health check mode is set to 'none'\");\n            }\n            break;\n        case cmd:\n            if (cmd == null) {\n                throw new IllegalArgumentException(\"HealthCheck: the parameter 'cmd' is mandatory when the health check mode is set to 'cmd' (default)\");\n            }\n        }\n    }\n\n    // ===========================================\n\n    public static class Builder {\n\n        private HealthCheckConfiguration config = new HealthCheckConfiguration();\n\n        public Builder() {\n            this.config = new HealthCheckConfiguration();\n        }\n\n        public Builder interval(String interval) {\n            config.interval = interval;\n            return this;\n        }\n\n        public Builder timeout(String timeout) {\n            config.timeout = timeout;\n            return this;\n        }\n\n        public Builder startPeriod(String startPeriod) {\n            config.startPeriod = startPeriod;\n            return this;\n        }\n\n        public Builder cmd(Arguments command) {\n            if (command != null) {\n                config.cmd = command;\n            }\n            return this;\n        }\n\n        public Builder retries(Integer retries) {\n            config.retries = retries;\n            return this;\n        }\n\n        public Builder mode(String mode) {\n            return this.mode(mode != null ? HealthCheckMode.valueOf(mode) : (HealthCheckMode) null);\n        }\n\n        public Builder mode(HealthCheckMode mode) {\n            config.mode = mode;\n            return this;\n        }\n\n        public HealthCheckConfiguration build() {\n            return config;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/HealthCheckMode.java",
    "content": "package io.fabric8.maven.docker.config;\n\n\npublic enum HealthCheckMode {\n\n    /**\n     * Mainly used to disable any health check provided by the base image.\n     */\n    none,\n\n    /**\n     * A command based health check.\n     */\n    cmd;\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.util.DeepCopy;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.StartOrderResolver;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * @author roland\n * @since 02.09.14\n */\npublic class ImageConfiguration implements StartOrderResolver.Resolvable, Serializable {\n\n    @Parameter(required = true)\n    private String name;\n\n    @Parameter\n    private String alias;\n\n    @Parameter\n    private String stopNamePattern;\n\n    @Parameter\n    private String removeNamePattern;\n\n    @Parameter\n    private RunImageConfiguration run;\n\n    @Parameter\n    private BuildImageConfiguration build;\n\n    @Parameter\n    private WatchImageConfiguration watch;\n\n    @Parameter\n    private Map<String,String> external;\n\n    @Parameter\n    private String registry;\n\n    // Used for injection\n    public ImageConfiguration() {}\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * Change the name which can be useful in long running runs e.g. for updating\n     * images when doing updates. Use with caution and only for those circumstances.\n     *\n     * @param name image name to set.\n     */\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    /**\n     * Changes image registry to specified one\n     * @param registry string value for registry to be modified\n     */\n    public void setRegistry(String registry) {\n        this.registry = registry;\n    }\n\n    /**\n     * Override externalConfiguration when defined via special property.\n     *\n     * @param externalConfiguration Map with alternative config\n     */\n    public void setExternalConfiguration(Map<String, String> externalConfiguration) {\n        this.external = externalConfiguration;\n    }\n\n    @Override\n\tpublic String getAlias() {\n        return alias;\n    }\n\n    public void setAlias(String alias) {\n        this.alias = alias;\n    }\n\n    public String getStopNamePattern() {\n        return stopNamePattern;\n    }\n\n    public String getRemoveNamePattern() {\n        return removeNamePattern;\n    }\n\n    public RunImageConfiguration getRunConfiguration() {\n        return (run == null) ? RunImageConfiguration.DEFAULT : run;\n    }\n\n    public BuildImageConfiguration getBuildConfiguration() {\n        return build;\n    }\n\n    public WatchImageConfiguration getWatchConfiguration() {\n        return watch;\n    }\n\n    public Map<String, String> getExternalConfig() {\n        return external;\n    }\n\n    @Override\n    public List<String> getDependencies() {\n        RunImageConfiguration runConfig = getRunConfiguration();\n        List<String> ret = new ArrayList<>();\n        if (runConfig != null) {\n            addVolumes(runConfig, ret);\n            addLinks(runConfig, ret);\n            addContainerNetwork(runConfig, ret);\n            addDependsOn(runConfig, ret);\n        }\n        return ret;\n    }\n\n    private void addVolumes(RunImageConfiguration runConfig, List<String> ret) {\n        RunVolumeConfiguration volConfig = runConfig.getVolumeConfiguration();\n        if (volConfig != null) {\n            List<String> volumeImages = volConfig.getFrom();\n            if (volumeImages != null) {\n                ret.addAll(volumeImages);\n            }\n        }\n    }\n\n    private void addLinks(RunImageConfiguration runConfig, List<String> ret) {\n        // Custom networks can have circular links, no need to be considered for the starting order.\n        if (!runConfig.getNetworkingConfig().isCustomNetwork()) {\n            for (String[] link : EnvUtil.splitOnLastColon(runConfig.getLinks())) {\n                ret.add(link[0]);\n            }\n        }\n    }\n\n    private void addContainerNetwork(RunImageConfiguration runConfig, List<String> ret) {\n        NetworkConfig config = runConfig.getNetworkingConfig();\n        String alias = config.getContainerAlias();\n        if (alias != null) {\n            ret.add(alias);\n        }\n    }\n\n    private void addDependsOn(RunImageConfiguration runConfig, List<String> ret) {\n        // Only used in custom networks.\n        if (runConfig.getNetworkingConfig().isCustomNetwork()) {\n            ret.addAll(runConfig.getDependsOn());\n        }\n    }\n\n    public boolean isDataImage() {\n        // If there is no explicit run configuration, its a data image\n        // TODO: Probably add an explicit property so that a user can indicated whether it\n        // is a data image or not on its own.\n        return run == null;\n    }\n\n    public String getDescription() {\n        return String.format(\"[%s] %s\", new ImageName(name).getFullName(), (alias != null ? \"\\\"\" + alias + \"\\\"\" : \"\")).trim();\n    }\n\n    public String getRegistry() {\n        return registry;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"ImageConfiguration {name='%s', alias='%s'}\", name, alias);\n    }\n\n    public String initAndValidate(ConfigHelper.NameFormatter nameFormatter, Logger log) {\n        name = nameFormatter.format(name);\n        String minimalApiVersion = null;\n        if (build != null) {\n            minimalApiVersion = build.initAndValidate(log);\n        }\n        if (run != null) {\n            minimalApiVersion = EnvUtil.extractLargerVersion(minimalApiVersion, run.initAndValidate());\n        }\n        return minimalApiVersion;\n    }\n\n    // =========================================================================\n    // Builder for image configurations\n\n    public static class Builder {\n        private final ImageConfiguration config;\n\n        public Builder()  {\n            this(null);\n        }\n\n\n        public Builder(ImageConfiguration that) {\n            if (that == null) {\n                this.config = new ImageConfiguration();\n            } else {\n                this.config = DeepCopy.copy(that);\n            }\n        }\n\n        public Builder name(String name) {\n            config.name = name;\n            return this;\n        }\n\n        public Builder alias(String alias) {\n            config.alias = alias;\n            return this;\n        }\n\n        public Builder removeNamePattern(String removeNamePattern) {\n            config.removeNamePattern = removeNamePattern;\n            return this;\n        }\n\n        public Builder stopNamePattern(String stopNamePattern) {\n            config.stopNamePattern = stopNamePattern;\n            return this;\n        }\n\n        public Builder runConfig(RunImageConfiguration runConfig) {\n            config.run = runConfig;\n            return this;\n        }\n\n        public Builder buildConfig(BuildImageConfiguration buildConfig) {\n            config.build = buildConfig;\n            return this;\n        }\n\n        public Builder externalConfig(Map<String, String> externalConfig) {\n            config.external = externalConfig;\n            return this;\n        }\n\n        public Builder registry(String registry) {\n            config.registry = registry;\n            return this;\n        }\n\n        public ImageConfiguration build() {\n            return config;\n        }\n\n        public Builder watchConfig(WatchImageConfiguration watchConfig) {\n            config.watch = watchConfig;\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/ImagePullPolicy.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author roland\n * @since 05.11.17\n */\npublic enum ImagePullPolicy {\n\n    /**\n     * Pull always images\n     */\n    Always,\n\n    /**\n     * Pull image only if not present\n     */\n    IfNotPresent,\n\n    /**\n     * Don't auto pull images\n     */\n    Never;\n\n    public static ImagePullPolicy fromString(String imagePullPolicy) {\n        for (ImagePullPolicy policy : values()) {\n            if (policy.name().equalsIgnoreCase(imagePullPolicy)) {\n                return policy;\n            }\n        }\n        throw new IllegalArgumentException(String.format(\"No policy %s known. Valid values are: %s\",\n                                           imagePullPolicy, StringUtils.join(values(), \", \")));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/LogConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * @author roland\n * @since 12.10.14\n */\npublic class LogConfiguration implements Serializable {\n\n    public static final LogConfiguration DEFAULT = new LogConfiguration(null, null, null, null, null, null);\n\n    @Parameter(defaultValue = \"true\")\n    private Boolean enabled;\n\n    @Parameter\n    private String prefix;\n\n    @Parameter\n    private String date;\n\n    @Parameter\n    private String color;\n\n    @Parameter\n    private String file;\n\n    @Parameter\n    private LogDriver driver;\n\n    public LogConfiguration() {}\n\n    private LogConfiguration(Boolean enabled, String prefix, String color, String date, String file, LogDriver driver) {\n        this.enabled = enabled;\n        this.prefix = prefix;\n        this.date = date;\n        this.color = color;\n        this.file = file;\n        this.driver = driver;\n    }\n\n    public String getPrefix() {\n        return prefix;\n    }\n\n    public String getDate() {\n        return date;\n    }\n\n    public String getColor() {\n        return color;\n    }\n\n    public Boolean isEnabled() {\n        return enabled;\n    }\n\n    /**\n     * If explicitly enabled, or configured in any way and NOT explicitly disabled, return true.\n     *\n     * @return\n     */\n    public boolean isActivated() {\n        return enabled != null ? enabled : !isBlank();\n    }\n\n    /**\n     * Returns true if all options (except enabled) are null, used to decide value of enabled.\n     *\n     * @return\n     */\n    private boolean isBlank() {\n        return prefix == null && date == null && color == null && file == null && driver == null;\n    }\n\n    public String getFileLocation() {\n        return file;\n    }\n\n    public LogDriver getDriver() {\n        return driver;\n    }\n\n    // =======================================================================================\n\n    public static class LogDriver implements Serializable {\n\n        /** @parameter */\n        private String name;\n\n        /** @parameter */\n        private Map<String, String> opts;\n\n        public LogDriver() {};\n\n        private LogDriver(String name, Map<String, String> opts) {\n            this.name = name;\n            this.opts = opts;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public Map<String, String> getOpts() {\n            return opts;\n        }\n    }\n\n    // =============================================================================\n\n    public static class Builder {\n        private Boolean enabled;\n        private String prefix, date, color, file;\n        private Map<String, String> driverOpts;\n        private String driverName;\n        public Builder enabled(Boolean enabled) {\n            this.enabled = enabled;\n            return this;\n        }\n\n        public Builder prefix(String prefix) {\n            this.prefix = prefix;\n            return this;\n        }\n\n        public Builder date(String date) {\n            this.date = date;\n            return this;\n        }\n\n        public Builder color(String color) {\n            this.color = color;\n            return this;\n        }\n\n        public Builder file(String file) {\n            this.file = file;\n            return this;\n        }\n\n        public Builder logDriverName(String logDriver) {\n            this.driverName = logDriver;\n            return this;\n        }\n\n        public Builder logDriverOpts(Map<String, String> logOpts) {\n            this.driverOpts = logOpts;\n            return this;\n        }\n\n        public LogConfiguration build() {\n            return new LogConfiguration(enabled, prefix, color, date, file,\n                                        driverName != null ? new LogDriver(driverName,driverOpts) : null);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/NetworkConfig.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Network config encapsulating network specific configuration\n * @author roland\n * @since 29/07/16\n */\n\npublic class NetworkConfig implements Serializable {\n\n    @Parameter\n    private String name;\n\n    @Parameter\n    private Mode mode;\n\n    private List<String> aliases = new ArrayList<>();\n\n    // Use by Maven to add flattened <alias> entries\n    // See  http://blog.sonatype.com/2011/03/configuring-plugin-goals-in-maven-3/\n    public void addAlias(String alias) {\n        aliases.add(alias);\n    }\n\n    /**\n     * Legacy constructor using the &lt;net> config\n     * @param net net, encapsulating mode & name.\n     */\n    public NetworkConfig(String net) {\n        initLegacyNetSpec(net);\n    }\n\n\n    public NetworkConfig(Mode mode, String name) {\n        this.name = name;\n        this.mode = mode;\n    }\n\n    public NetworkConfig() {\n        name = null;\n        mode = null;\n    }\n\n    private void initLegacyNetSpec(String net) {\n        if (net != null) {\n            this.mode = extractMode(net);\n            if (this.mode == Mode.container) {\n                this.name = net.substring(Mode.container.name().length() + 1);\n            } else if (this.mode == Mode.custom) {\n                this.name = net;\n            } else {\n                this.name = null;\n            }\n        } else {\n            this.mode = null;\n            this.name = null;\n        }\n    }\n\n    private Mode extractMode(String mode) {\n        if (mode != null && mode.length() > 0) {\n            try {\n                return Mode.valueOf(mode.toLowerCase());\n            } catch (IllegalArgumentException exp) { /* could be a custom mode, too */ }\n            if (mode.toLowerCase().startsWith(Mode.container.name() + \":\")) {\n                return Mode.container;\n            } else {\n                return Mode.custom;\n            }\n        }\n        return null;\n    }\n\n    public List<String> getAliases() {\n        return aliases;\n    }\n\n    public boolean isCustomNetwork() {\n        return (mode != null && mode == Mode.custom) || (mode == null && name != null);\n    }\n\n    public boolean isStandardNetwork() {\n        return mode != null && mode != Mode.custom;\n    }\n\n    public String getStandardMode(String containerId) {\n        if (isCustomNetwork()) {\n            throw new IllegalArgumentException(\"Custom network for network '\" + name +\n                                               \"' can not be used as standard mode\");\n        }\n        if (mode == null) {\n            return null;\n        }\n        return mode.name().toLowerCase() + (mode == Mode.container ? \":\" + containerId : \"\");\n    }\n\n    public String getContainerAlias() {\n        return mode == Mode.container ? name : null;\n    }\n\n    public String getCustomNetwork() {\n        return mode == Mode.custom || mode == null ? name : null;\n    }\n\n    public boolean hasAliases() {\n        return aliases != null && !aliases.isEmpty();\n    }\n\n    public Mode getMode() {\n        return mode;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    // ==============================================================================\n\n    // Mode used for determining the network\n    public enum Mode {\n        none,\n        bridge,\n        host,\n        container,\n        custom;\n    }\n\n    public static class Builder {\n\n        private final NetworkConfig config = new NetworkConfig();\n        private boolean isEmpty = true;\n\n        public NetworkConfig build() {\n            return isEmpty ? null : config;\n        }\n\n        public Builder mode(String mode) {\n            config.mode = set(mode != null ? Mode.valueOf(mode) : null);\n            return this;\n        }\n\n        public Builder name(String name) {\n            config.name = set(name);\n            return this;\n        }\n\n\n        public Builder aliases(List<String> aliases) {\n            config.aliases = set(aliases);\n            return this;\n        }\n\n        private <T> T set(T prop) {\n            if (prop != null) {\n                isEmpty = false;\n            }\n            return prop;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/RegistryAuthConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.maven.plugins.annotations.Parameter;\n\npublic class RegistryAuthConfiguration implements Serializable {\n\n    @Parameter\n    private Map<String, String> push;\n\n    @Parameter\n    private Map<String, String> pull;\n\n    @Parameter\n    private String username;\n\n    @Parameter\n    private String password;\n\n    @Parameter\n    private String email;\n\n    @Parameter\n    @Deprecated\n    private String authToken;\n\n    @Parameter\n    private String auth;\n\n    public Map toMap() {\n        final Map authMap = new TreeMap<>();\n\n        if (push != null) {\n            authMap.put(\"push\", push);\n        }\n        if (pull != null) {\n            authMap.put(\"pull\", pull);\n        }\n        if (StringUtils.isNotBlank(username)) {\n            authMap.put(AuthConfig.AUTH_USERNAME, username);\n        }\n        if (StringUtils.isNotBlank(password)) {\n            authMap.put(AuthConfig.AUTH_PASSWORD, password);\n        }\n\n        if (StringUtils.isNotBlank(authToken) && StringUtils.isNotBlank(auth)) {\n            throw new IllegalStateException(\"For a registry configuration either 'auth' or 'authToken' (deprecated) can be specified but not both. Use only 'auth' and remove 'authToken' in the registry configuration\");\n        }\n\n        if (StringUtils.isNotBlank(authToken)) {\n            authMap.put(AuthConfig.AUTH_AUTH, authToken);\n        }\n\n        if (StringUtils.isNotBlank(auth)) {\n            authMap.put(AuthConfig.AUTH_AUTH, auth);\n        }\n\n        if (StringUtils.isNotBlank(email)) {\n            authMap.put(AuthConfig.AUTH_EMAIL, email);\n        }\n        return authMap;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/RestartPolicy.java",
    "content": "package io.fabric8.maven.docker.config;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.Serializable;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n* @author roland\n* @since 08/12/14\n*/\npublic class RestartPolicy implements Serializable {\n\n    public static final RestartPolicy DEFAULT = new RestartPolicy();\n\n    @Parameter\n    private String name;\n\n    @Parameter\n    private int retry;\n\n    public RestartPolicy() {};\n\n    public String getName() {\n        return name;\n    }\n\n    public int getRetry() {\n        return retry;\n    }\n\n    // ================================================\n\n    public static class Builder {\n\n        private RestartPolicy policy = new RestartPolicy();\n\n        public Builder name(String name) {\n            policy.name = name;\n            return this;\n        }\n\n        public Builder retry(int retry) {\n            policy.retry = retry;\n            return this;\n        }\n\n        public RestartPolicy build() {\n            return policy;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nonnull;\n\nimport io.fabric8.maven.docker.util.DeepCopy;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * @author roland\n * @since 02.09.14\n */\npublic class RunImageConfiguration implements Serializable {\n\n    static final RunImageConfiguration DEFAULT = new RunImageConfiguration();\n\n    public boolean isDefault() {\n        return this == RunImageConfiguration.DEFAULT;\n    }\n\n    /**\n     * Environment variables to set when starting the container. key: variable name, value: env value\n     */\n    @Parameter\n    private Map<String, String> env;\n\n    @Parameter\n    private Map<String,String> labels;\n\n    // Path to a property file holding environment variables\n    @Parameter\n    private String envPropertyFile;\n\n    // Command to execute in container\n    @Parameter\n    private Arguments cmd;\n\n    // container domain name\n    @Parameter\n    private String domainname;\n\n    // container domain name\n    @Parameter\n    private List<String> dependsOn;\n\n    // container entry point\n    @Parameter\n    private Arguments entrypoint;\n\n    // container hostname\n    @Parameter\n    private String hostname;\n\n    // container user\n    @Parameter\n    private String user;\n\n    // working directory\n    @Parameter\n    private String workingDir;\n\n    // Size of /dev/shm in bytes\n    /** @parameter */\n    private Long shmSize;\n\n    // memory in bytes\n    @Parameter\n    private Long memory;\n\n    // total memory (swap + ram) in bytes, -1 to disable\n    @Parameter\n    private Long memorySwap;\n\n    // Path to a file where the dynamically mapped properties are written to\n    @Parameter\n    private String portPropertyFile;\n\n    // For simple network setups. For complex stuff use \"network\"\n    @Parameter\n    private String net;\n\n    @Parameter\n    private NetworkConfig network;\n\n    @Parameter\n    private List<String> dns;\n\n    @Parameter\n    private List<String> dnsSearch;\n\n    @Parameter\n    private List<String> capAdd;\n\n    @Parameter\n    private List<String> capDrop;\n\n    @Parameter\n    private List<String> securityOpts;\n\n    @Parameter\n    private Boolean privileged;\n\n    @Parameter\n    private List<String> extraHosts;\n\n    @Parameter\n    private Long cpuShares;\n\n    @Parameter\n    private Long cpus;\n\n    @Parameter\n    private String cpuSet;\n\n    // Port mapping. Can contain symbolic names in which case dynamic\n    // ports are used\n    @Parameter\n    private List<String> ports;\n\n    /**\n     * @deprecated\n     */\n    @Parameter\n    @Deprecated\n    private NamingStrategy namingStrategy;\n\n    /**\n     * A pattern to define the naming of the container where\n     *\n     * - %a for the \"alias\" mode\n     * - %n for the image name\n     * - %t for a timestamp\n     * - %i for an increasing index of container names\n     *\n     */\n    @Parameter\n    private String containerNamePattern;\n\n    /**\n     * Property key part used to expose the container ip when running.\n     */\n    @Parameter\n    private String exposedPropertyKey;\n\n    // Mount volumes from the given image's started containers\n    @Parameter\n    private RunVolumeConfiguration volumes;\n\n    // Links to other container started\n    @Parameter\n    private List<String> links;\n\n    // Configuration for how to wait during startup of the container\n    @Parameter\n    private WaitConfiguration wait;\n\n    // Mountpath for tmps\n    @Parameter\n    private List<String> tmpfs;\n\n    @Parameter\n    private LogConfiguration log;\n\n    @Parameter\n    private RestartPolicy restartPolicy;\n\n    @Parameter\n    private List<UlimitConfig> ulimits;\n\n    @Parameter\n    private Boolean skip;\n    \n    /**\n     * Policy for pulling the image to start\n     */\n    @Parameter\n    private String imagePullPolicy;\n\n    // Mount the container's root filesystem as read only\n    @Parameter\n    private Boolean readOnly;\n\n    // Automatically remove the container when it exists\n    @Parameter\n    private Boolean autoRemove;\n\n    // How to stop a container\n    @Parameter\n    private StopMode stopMode;\n\n    public RunImageConfiguration() { }\n\n    public String initAndValidate() {\n        if (entrypoint != null) {\n            entrypoint.validate();\n        }\n        if (cmd != null) {\n            cmd.validate();\n        }\n\n        // Custom networks are available since API 1.21 (Docker 1.9)\n        NetworkConfig config = getNetworkingConfig();\n        if (config != null && config.isCustomNetwork()) {\n            return \"1.21\";\n        }\n\n        return null;\n    }\n\n    public Map<String, String> getEnv() {\n        return env;\n    }\n\n    public Map<String, String> getLabels() {\n        return labels;\n    }\n\n    public String getEnvPropertyFile() {\n        return envPropertyFile;\n    }\n\n    public Arguments getEntrypoint() {\n        return entrypoint;\n    }\n\n    public String getHostname() {\n        return hostname;\n    }\n\n    public String getDomainname() {\n        return domainname;\n    }\n\n    @Nonnull\n    public List<String> getDependsOn() {\n        return EnvUtil.splitAtCommasAndTrim(dependsOn);\n    }\n\n    public String getUser() {\n        return user;\n    }\n\n    public Long getShmSize() {\n        return shmSize;\n    }\n\n    public Long getMemory() {\n        return memory;\n    }\n\n    public Long getMemorySwap() {\n        return memorySwap;\n    }\n\n    public Long getCpuShares() {\n        return cpuShares;\n    }\n\n    public Long getCpus() {\n        return cpus;\n    }\n\n    public String getCpuSet() {\n        return cpuSet;\n    }\n\n    @Nonnull\n    public List<String> getPorts() {\n        return EnvUtil.removeEmptyEntries(ports);\n    }\n\n    public Arguments getCmd() {\n        return cmd;\n    }\n\n    public String getPortPropertyFile() {\n        return portPropertyFile;\n    }\n\n    public String getWorkingDir() {\n        return workingDir;\n    }\n\n    public WaitConfiguration getWaitConfiguration() {\n        return wait;\n    }\n\n    public LogConfiguration getLogConfiguration() {\n        return log;\n    }\n\n    public List<String> getCapAdd() {\n        return capAdd;\n    }\n\n    public List<String> getCapDrop() {\n        return capDrop;\n    }\n\n    public List<String> getSecurityOpts() {\n        return securityOpts;\n    }\n\n    public List<String> getDns() {\n        return dns;\n    }\n\n    @Deprecated\n    public String getNetRaw() {\n        return net;\n    }\n\n    public NetworkConfig getNetworkingConfig() {\n        if (network != null) {\n            return network;\n        } else if (net != null) {\n            return new NetworkConfig(net);\n        } else {\n            return new NetworkConfig();\n        }\n    }\n\n    public List<String> getDnsSearch() {\n        return dnsSearch;\n    }\n\n    public List<String> getExtraHosts() {\n        return extraHosts;\n    }\n\n    public RunVolumeConfiguration getVolumeConfiguration() {\n        return volumes;\n    }\n\n    @Nonnull\n    public List<String> getLinks() {\n        return EnvUtil.splitAtCommasAndTrim(links);\n    }\n\n    public List<UlimitConfig> getUlimits() {\n        return ulimits;\n    }\n\n    public List<String> getTmpfs() {\n        return tmpfs;\n    }\n\n    /**\n     * @deprecated\n     */\n    // Naming scheme for how to name container\n    @Deprecated // for backward compatibility, us containerNamePattern instead\n    public enum NamingStrategy {\n        /**\n         * No extra naming\n         */\n        none,\n        /**\n         * Use the alias as defined in the configuration\n         */\n        alias\n    }\n\n    public String getExposedPropertyKey() {\n        return exposedPropertyKey;\n    }\n\n    public Boolean getPrivileged() {\n        return privileged;\n    }\n\n    public RestartPolicy getRestartPolicy() {\n        return (restartPolicy == null) ? RestartPolicy.DEFAULT : restartPolicy;\n    }\n\n    public RestartPolicy getRestartPolicyRaw() {\n        return restartPolicy;\n    }\n\n    public boolean skip() {\n        return skip != null ? skip : false;\n    }\n\n    public Boolean getSkip() {\n        return skip;\n    }\n\n    public String getImagePullPolicy() {\n        return imagePullPolicy;\n    }\n\n    public String getContainerNamePattern() {\n        return containerNamePattern;\n    }\n\n    public Boolean getReadOnly() {\n        return readOnly;\n    }\n\n    public Boolean getAutoRemove() {\n        return autoRemove;\n    }\n\n    public StopMode getStopMode() {\n        if (stopMode == null) {\n            return StopMode.graceful;\n        }\n        return stopMode;\n    }\n\n    /**\n     * @deprecated use {@link #getContainerNamePattern} instead\n     */\n    @Deprecated\n    public NamingStrategy getNamingStrategy() {\n        return namingStrategy;\n    }\n\n    // ======================================================================================\n\n    public static class Builder {\n\n        public Builder(RunImageConfiguration config) {\n            if (config == null) {\n                this.config = new RunImageConfiguration();\n            } else {\n                this.config = DeepCopy.copy(config);\n            }\n        }\n\n        public Builder() {\n            this(null);\n        }\n\n        private RunImageConfiguration config;\n\n        public Builder env(Map<String, String> env) {\n            config.env = env;\n            return this;\n        }\n\n        public Builder labels(Map<String, String> labels) {\n            config.labels = labels;\n            return this;\n        }\n\n\n        public Builder envPropertyFile(String envPropertyFile) {\n            config.envPropertyFile = envPropertyFile;\n            return this;\n        }\n\n        public Builder cmd(String cmd) {\n            if (cmd != null) {\n                config.cmd = new Arguments(cmd);\n            }\n            return this;\n        }\n\n        public Builder cmd(Arguments args) {\n            config.cmd = args;\n            return this;\n        }\n\n        public Builder domainname(String domainname) {\n            config.domainname = domainname;\n            return this;\n        }\n\n        public Builder entrypoint(Arguments args) {\n            config.entrypoint = args;\n            return this;\n        }\n\n        public Builder hostname(String hostname) {\n            config.hostname = hostname;\n            return this;\n        }\n\n        public Builder portPropertyFile(String portPropertyFile) {\n            config.portPropertyFile = portPropertyFile;\n            return this;\n        }\n\n        public Builder workingDir(String workingDir) {\n            config.workingDir = workingDir;\n            return this;\n        }\n\n        public Builder user(String user) {\n            config.user = user;\n            return this;\n        }\n\n        public Builder shmSize(Long shmSize) {\n            config.shmSize = shmSize;\n            return this;\n        }\n\n        public Builder memory(Long memory) {\n            config.memory = memory;\n            return this;\n        }\n\n        public Builder memorySwap(Long memorySwap) {\n            config.memorySwap = memorySwap;\n            return this;\n        }\n\n        public Builder capAdd(List<String> capAdd) {\n            config.capAdd = capAdd;\n            return this;\n        }\n\n        public Builder capDrop(List<String> capDrop) {\n            config.capDrop = capDrop;\n            return this;\n        }\n\n        public Builder securityOpts(List<String> securityOpts) {\n            config.securityOpts = securityOpts;\n            return this;\n        }\n\n        public Builder net(String net) {\n            config.net = net;\n            return this;\n        }\n\n        public Builder network(NetworkConfig networkConfig) {\n            config.network = networkConfig;\n            return this;\n        }\n\n        public Builder dependsOn(List<String> dependsOn) {\n            config.dependsOn = dependsOn;\n            return this;\n        }\n\n        public Builder dns(List<String> dns) {\n            config.dns = dns;\n            return this;\n        }\n\n        public Builder dnsSearch(List<String> dnsSearch) {\n            config.dnsSearch = dnsSearch;\n            return this;\n        }\n\n        public Builder extraHosts(List<String> extraHosts) {\n            config.extraHosts = extraHosts;\n            return this;\n        }\n\n        public Builder ulimits(List<UlimitConfig> ulimits) {\n            config.ulimits = ulimits;\n            return this;\n        }\n\n        public Builder ports(List<String> ports) {\n            config.ports = ports;\n            return this;\n        }\n\n        public Builder volumes(RunVolumeConfiguration volumes) {\n            config.volumes = volumes;\n            return this;\n        }\n\n        public Builder links(List<String> links) {\n            config.links = links;\n            return this;\n        }\n\n        public Builder tmpfs(List<String> tmpfs) {\n            config.tmpfs = tmpfs;\n            return this;\n        }\n\n        public Builder wait(WaitConfiguration wait) {\n            config.wait = wait;\n            return this;\n        }\n\n        public Builder log(LogConfiguration log) {\n            config.log = log;\n            return this;\n        }\n\n        public Builder cpuShares(Long cpuShares){\n            config.cpuShares = cpuShares;\n            return this;\n        }\n\n        public Builder cpus(Long cpus){\n            config.cpus = cpus;\n            return this;\n        }\n\n        public Builder cpuSet(String cpuSet){\n            config.cpuSet = cpuSet;\n            return this;\n        }\n\n        public Builder containerNamePattern(String pattern) {\n            config.containerNamePattern = pattern;\n            return this;\n        }\n\n        /**\n         * @deprecated use {@link #containerNamePattern} instead\n         */\n        @Deprecated\n        public Builder namingStrategy(String namingStrategy) {\n            config.namingStrategy = namingStrategy == null ?\n                    NamingStrategy.none :\n                    NamingStrategy.valueOf(namingStrategy.toLowerCase());\n            return this;\n        }\n\n        /**\n         * @deprecated use {@link #containerNamePattern} instead\n         */\n        @Deprecated\n        public Builder namingStrategy(NamingStrategy namingStrategy) {\n            config.namingStrategy = namingStrategy;\n            return this;\n        }\n\n        public Builder exposedPropertyKey(String key) {\n            config.exposedPropertyKey = key;\n            return this;\n        }\n\n        public Builder privileged(Boolean privileged) {\n            config.privileged = privileged;\n            return this;\n        }\n\n        public Builder restartPolicy(RestartPolicy restartPolicy) {\n            config.restartPolicy = restartPolicy;\n            return this;\n        }\n\n        public Builder skip(Boolean skip) {\n            config.skip = skip;\n            return this;\n        }\n\n        public Builder stopMode(StopMode stopMode) {\n            config.stopMode = stopMode;\n            return this;\n        }\n\n        public Builder imagePullPolicy(String imagePullPolicy) {\n            if (imagePullPolicy != null) {\n                config.imagePullPolicy = imagePullPolicy;\n            }\n            return this;\n        }\n\n        public Builder readOnly(Boolean readOnly) {\n            config.readOnly = readOnly;\n            return this;\n        }\n\n        public Builder autoRemove(Boolean autoRemove) {\n            config.autoRemove = autoRemove;\n            return this;\n        }\n\n        public RunImageConfiguration build() {\n            return config;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/RunVolumeConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.Serializable;\nimport java.util.*;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Run configuration for volumes.\n *\n * @author roland\n * @since 08/12/14\n */\npublic class RunVolumeConfiguration implements Serializable {\n\n    /**\n     * List of images names from where volumes are mounted\n     */\n    @Parameter\n    private List<String> from;\n\n    /**\n     * List of bind parameters for binding/mounting host directories\n     * into the container\n     */\n    @Parameter\n    private List<String> bind;\n\n    /**\n     * List of images to mount from\n     *\n     * @return images\n     */\n    public List<String> getFrom() {\n        return from;\n    }\n\n    /**\n     * List of docker bind specification for mounting local directories\n     * @return list of bind specs\n     */\n    public List<String> getBind() {\n        return bind;\n    }\n\n    // ===========================================\n\n    public static class Builder {\n\n        private RunVolumeConfiguration config = new RunVolumeConfiguration();\n\n        public Builder() {\n            this.config = new RunVolumeConfiguration();\n        }\n\n        public Builder from(List<String> args) {\n            if (args != null) {\n                if (config.from == null) {\n                    config.from = new ArrayList<>();\n                }\n                config.from.addAll(args);\n            }\n            return this;\n        }\n\n        public Builder bind(List<String> args) {\n            if (args != null) {\n                if (config.bind == null) {\n                    config.bind = new ArrayList<>();\n                }\n                config.bind.addAll(args);\n            }\n            return this;\n        }\n\n        public RunVolumeConfiguration build() {\n            return config;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/StopMode.java",
    "content": "package io.fabric8.maven.docker.config;\n\n/*\n * Enum for holding information regarding stopping containers in dmp\n */\npublic enum StopMode {\n    /**\n     * Would wait for the process to terminate correctly\n     */\n    graceful,\n\n    /**\n     * Would terminate the process with a sigkill\n     */\n    kill\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/UlimitConfig.java",
    "content": "/*\r\n * Projet docker-maven-plugin\r\n * Copyright S2E 2016\r\n * ULimitConfig.java, 2016, <Modifier le fichier ecplise.ini pour fixer le nom d'utilisateur>\r\n */\r\npackage io.fabric8.maven.docker.config;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.apache.maven.plugins.annotations.Parameter;\r\n\r\n/**\r\n * Configuration for ulimit\r\n *\r\n * @since 0.15\r\n * @author Alexis Thaveau\r\n */\r\npublic class UlimitConfig implements Serializable {\r\n\r\n    @Parameter\r\n    private String name;\r\n\r\n    @Parameter\r\n    private Integer hard;\r\n\r\n    @Parameter\r\n    private Integer soft;\r\n\r\n    public UlimitConfig(String name, Integer hard, Integer soft) {\r\n        this.name = name;\r\n        this.hard = hard;\r\n        this.soft = soft;\r\n    }\r\n\r\n    public String getName() {\r\n\t\treturn name;\r\n    }\r\n\r\n\tpublic Integer getHard() {\r\n\t\treturn hard;\r\n    }\r\n\r\n\tpublic Integer getSoft() {\r\n\t\treturn soft;\r\n\t}\r\n\r\n    Pattern ULIMIT_PATTERN = Pattern.compile(\"^(?<name>[^=]+)=(?<hard>[^:]*):?(?<soft>[^:]*)$\");\r\n\r\n    public UlimitConfig() {}\r\n\r\n    public UlimitConfig(String ulimit) {\r\n        Matcher matcher = ULIMIT_PATTERN.matcher(ulimit);\r\n        if (matcher.matches()) {\r\n            name = matcher.group(\"name\");\r\n            hard = asInteger(matcher.group(\"hard\"));\r\n            soft = asInteger(matcher.group(\"soft\"));\r\n        } else {\r\n            throw new IllegalArgumentException(\"Invalid ulimit specification \" + ulimit);\r\n        }\r\n    }\r\n\r\n    private Integer asInteger(String number) {\r\n        if (number == null || number.length() == 0) {\r\n            return null;\r\n        }\r\n        return Integer.parseInt(number);\r\n    }\r\n\r\n    public String serialize() {\r\n        if(hard != null && soft != null) {\r\n            return name + \"=\"+hard+\":\"+soft;\r\n        } else if(hard != null) {\r\n            return name + \"=\"+hard;\r\n        } else if(soft != null) {\r\n            return name + \"=:\"+soft;\r\n        } else {\r\n            return null;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/VolumeConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\n\nimport io.fabric8.maven.docker.util.DeepCopy;\n\nimport java.io.Serializable;\nimport java.lang.String;\nimport java.util.Map;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n *  Volume Configuration for Volumes to be created prior to container start\n *\n *  @author Tom Burton\n *  @version Dec 15, 2016\n */\npublic class VolumeConfiguration implements Serializable\n{\n   /** Volume Name */\n   @Parameter\n   private String name;\n\n   /** Volume driver for mounting the volume */\n   @Parameter\n   private String driver;\n\n   /** Driver specific options */\n   @Parameter\n   private Map<String, String> opts;\n\n   /** Volume labels */\n   @Parameter\n   private Map<String, String> labels;\n\n   public String getName() {\n       return name;\n   }\n\n   public String getDriver() {\n       return driver;\n   }\n\n   public Map<String, String> getOpts() {\n       return opts;\n   }\n\n   public Map<String, String> getLabels() {\n       return labels;\n   }\n\n   // =============================================================================\n\n   public static class Builder {\n       private final VolumeConfiguration config;\n\n       public Builder()  {\n           this(null);\n       }\n\n       public Builder(VolumeConfiguration that) {\n           this.config = that == null ? new VolumeConfiguration() : DeepCopy.copy(that);\n       }\n\n       public Builder name(String name) {\n           config.name = name;\n           return this;\n       }\n\n       public Builder driver(String driver) {\n           config.driver = driver;\n           return this;\n       }\n\n       public Builder opts(Map<String, String> opts) {\n           config.opts = opts;\n           return this;\n       }\n\n       public Builder labels(Map<String, String> labels) {\n           config.labels = labels;\n           return this;\n       }\n\n       public VolumeConfiguration build() {\n           return config;\n       }\n   }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/WaitConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * @author roland\n * @since 12.10.14\n */\npublic class WaitConfiguration implements Serializable {\n\n    // Default HTTP Method to use\n    public static final String DEFAULT_HTTP_METHOD = \"HEAD\";\n\n    // Default status codes\n    public static final int DEFAULT_MIN_STATUS = 200;\n    public static final int DEFAULT_MAX_STATUS = 399;\n\n    public static final String DEFAULT_STATUS_RANGE = String.format(\"%d..%d\", DEFAULT_MIN_STATUS, DEFAULT_MAX_STATUS);\n\n    @Parameter\n    private Integer time;\n\n    /**\n     * @deprecated Use &lt;http&gt;&lturl&gt;&lt;/url&gt;&lt;/http&gt; instead\n     */\n    @Parameter\n    private String url;\n\n    @Parameter\n    private HttpConfiguration http;\n\n    @Parameter\n    private ExecConfiguration exec;\n\n    @Parameter\n    private TcpConfiguration tcp;\n\n    @Parameter Boolean healthy;\n\n    @Parameter\n    private String log;\n\n    @Parameter\n    private Integer shutdown;\n\n    @Parameter\n    private Integer kill;\n\n    @Parameter\n    private Integer exit;\n\n    public WaitConfiguration() {}\n\n    private WaitConfiguration(Integer time, ExecConfiguration exec, HttpConfiguration http, TcpConfiguration tcp, Boolean healthy, String log, Integer shutdown, Integer kill, Integer exit) {\n        this.time = time;\n        this.exec = exec;\n        this.http = http;\n        this.tcp = tcp;\n        this.healthy = healthy;\n        this.log = log;\n        this.shutdown = shutdown;\n        this.kill = kill;\n        this.exit = exit;\n    }\n\n    public Integer getTime() { return time; }\n\n    public String getUrl() {\n        return http != null ? http.getUrl() : url;\n    }\n\n    public ExecConfiguration getExec() {\n        return exec;\n    }\n\n    public HttpConfiguration getHttp() {\n        return http;\n    }\n\n    public TcpConfiguration getTcp() {\n        return tcp;\n    }\n\n    public String getLog() {\n        return log;\n    }\n\n    public Integer getExit() {\n        return exit;\n    }\n\n    public Integer getShutdown() {\n        return shutdown;\n    }\n\n    public Integer getKill() {\n        return kill;\n    }\n\n    public Boolean getHealthy() { return healthy; }\n\n    // =============================================================================\n\n    public static class Builder {\n        private Integer time, shutdown, kill;\n        private String url,log,status;\n        Boolean healthy;\n        private String method;\n        private String preStop;\n        private String postStart;\n        private List<Integer> tcpPorts;\n        private String tcpHost;\n        private TcpConfigMode tcpMode;\n        private Integer exit;\n        private Boolean breakOnError = false;\n\n        public Builder time(int time) {\n            this.time = time;\n            return this;\n        }\n\n        public Builder url(String url) {\n            this.url = url;\n            return this;\n        }\n\n        public Builder method(String method) {\n            this.method = method;\n            return this;\n        }\n\n        public Builder status(String status) {\n            this.status = status;\n            return this;\n        }\n\n        public Builder healthy(Boolean healthy) {\n            this.healthy = healthy;\n            return this;\n        }\n\n        public Builder log(String log) {\n            this.log = log;\n            return this;\n        }\n\n        public Builder shutdown(Integer shutdown) {\n            this.shutdown = shutdown;\n            return this;\n        }\n\n        public Builder kill(Integer kill) {\n            this.kill = kill;\n            return this;\n        }\n\n        public Builder exit(Integer exit) {\n            this.exit = exit;\n            return this;\n        }\n\n        public Builder tcpPorts(List<Integer> tcpPorts) {\n            this.tcpPorts = tcpPorts;\n            return this;\n        }\n\n        public Builder tcpHost(String tcpHost) {\n            this.tcpHost = tcpHost;\n            return this;\n        }\n\n        public Builder tcpMode(String tcpMode) {\n            if (tcpMode != null) {\n                this.tcpMode = TcpConfigMode.valueOf(tcpMode.toLowerCase());\n            }\n            return this;\n        }\n\n\n        public WaitConfiguration build() {\n            return new WaitConfiguration(time,\n                                         postStart != null || preStop != null ? new ExecConfiguration(postStart, preStop, breakOnError != null ? breakOnError : false) : null,\n                                         url != null ? new HttpConfiguration(url,method,status) : null,\n                                         tcpPorts != null ? new TcpConfiguration(tcpMode, tcpHost, tcpPorts) : null,\n                                         healthy,\n                                         log,\n                                         shutdown,\n                                         kill,\n                                         exit);\n        }\n\n        public Builder preStop(String command) {\n            this.preStop = command;\n            return this;\n        }\n\n        public Builder postStart(String command) {\n            this.postStart = command;\n            return this;\n        }\n\n        public Builder breakOnError(Boolean stop) {\n            this.breakOnError = stop;\n            return this;\n        }\n    }\n\n    public static class ExecConfiguration implements Serializable {\n        @Parameter\n        private String postStart;\n\n        @Parameter\n        private String preStop;\n\n        @Parameter\n        private boolean breakOnError;\n\n        public ExecConfiguration() {}\n\n        public ExecConfiguration(String postStart, String preStop, boolean breakOnError) {\n            this.postStart = postStart;\n            this.preStop = preStop;\n            this.breakOnError = breakOnError;\n        }\n\n        public String getPostStart() {\n            return postStart;\n        }\n\n        public String getPreStop() {\n            return preStop;\n        }\n\n        public boolean isBreakOnError() {\n            return breakOnError;\n        }\n    }\n\n    public static class HttpConfiguration implements Serializable {\n\n        @Parameter\n        private String url;\n\n        @Parameter\n        private String method = DEFAULT_HTTP_METHOD;\n\n        @Parameter\n        private String status = DEFAULT_STATUS_RANGE;\n\n        @Parameter\n        private boolean allowAllHosts;\n\n        public HttpConfiguration() {}\n\n        private HttpConfiguration(String url, String method, String status) {\n            this.url = url;\n            this.method = method;\n            this.status = status;\n        }\n\n        public String getUrl() {\n            return url;\n        }\n\n        public String getMethod() {\n            return method;\n        }\n\n        public String getStatus() {\n            return status;\n        }\n\n        public boolean isAllowAllHosts() {\n            return allowAllHosts;\n        }\n    }\n\n    public enum TcpConfigMode {\n        // Use mapped ports\n        mapped,\n        // Use ports directly on the container\n        direct,\n    }\n\n    public static class TcpConfiguration implements Serializable {\n        @Parameter\n        private String host;\n\n        @Parameter\n        private List<Integer> ports;\n\n        @Parameter\n        private TcpConfigMode mode;\n\n        public TcpConfiguration() {}\n\n        private TcpConfiguration(TcpConfigMode mode, String host, List<Integer> ports) {\n            this.mode = mode;\n            this.host = host;\n            this.ports = ports;\n        }\n\n        public String getHost() {\n            return host;\n        }\n\n        public List<Integer> getPorts() {\n            return ports;\n        }\n\n        public TcpConfigMode getMode() {\n            return mode;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/WatchImageConfiguration.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.Serializable;\n\nimport io.fabric8.maven.docker.util.DeepCopy;\nimport org.apache.maven.plugins.annotations.Parameter;\n\n/**\n * Configuration for watching on image changes\n */\npublic class WatchImageConfiguration implements Serializable {\n\n    @Parameter\n    private Integer interval;\n\n    @Parameter\n    private WatchMode mode;\n\n    @Parameter\n    private String postGoal;\n\n    @Parameter\n    private String postExec;\n\n    public WatchImageConfiguration() {};\n\n    public int getInterval() {\n        return interval != null ? interval : 5000;\n    }\n\n    public Integer getIntervalRaw() {\n        return interval;\n    }\n\n    public WatchMode getMode() {\n        return mode;\n    }\n\n    public String getPostGoal() {\n        return postGoal;\n    }\n\n    public String getPostExec() {\n        return postExec;\n    }\n\n    public static class Builder {\n\n        private final WatchImageConfiguration c;\n\n        public Builder() {\n            this(null);\n        }\n\n        public Builder(WatchImageConfiguration that) {\n            if (that == null) {\n                this.c = new WatchImageConfiguration();\n            } else {\n                this.c = DeepCopy.copy(that);\n            }\n        }\n\n        public Builder interval(Integer interval) {\n            c.interval = interval;\n            return this;\n        }\n\n        public Builder mode(String mode) {\n            if (mode != null) {\n                c.mode = WatchMode.valueOf(mode.toLowerCase());\n            }\n            return this;\n        }\n\n        public Builder postGoal(String goal) {\n            c.postGoal = goal;\n            return this;\n        }\n\n        public Builder postExec(String exec) {\n            c.postExec = exec;\n            return this;\n        }\n\n        public WatchImageConfiguration build() {\n            return c;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/WatchMode.java",
    "content": "package io.fabric8.maven.docker.config;/*\n *\n * Copyright 2014 Roland Huss\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\n/**\n * How to watch for image changes\n * @author roland\n * @since 16/06/15\n */\npublic enum WatchMode {\n\n    /**\n     * Copy watched artefacts into container\n     */\n    copy(false, false, true, \"build\"),\n\n    /**\n     * Build only images\n     */\n    build(true, false, false, \"build\"),\n\n    /**\n     * Run images\n     */\n    run(false, true, false, \"run\"),\n\n    /**\n     * Build and run images\n     */\n    both(true, true, false, \"build and run\"),\n\n    /**\n     * Neither build nor run\n     */\n    none(false, false, false, \"no build and no run\");\n\n    private final boolean doRun;\n    private final boolean doBuild;\n    private final boolean doCopy;\n    private final String description;\n\n    WatchMode(boolean doBuild, boolean doRun, boolean doCopy, String description) {\n        this.doBuild = doBuild;\n        this.doRun = doRun;\n        this.doCopy = doCopy;\n        this.description = description;\n    }\n\n    public boolean isRun() {\n        return doRun;\n    }\n\n    public boolean isBuild() {\n        return doBuild;\n    }\n\n    public boolean isCopy() {\n        return doCopy;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/ExternalConfigHandler.java",
    "content": "package io.fabric8.maven.docker.config.handler;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.List;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\n\n/**\n * Interface which needs to be implemented to create\n * image configurations from external sources\n *\n * @author roland\n * @since 18/11/14\n */\npublic interface ExternalConfigHandler {\n\n    /**\n     * Get the unique type of this plugin as referenced with the <code>&lt;type&gt;</code> tag within a\n     * <code>&lt;reference&gt;</code> configuration section\n     *\n     * @return plugin type\n     */\n    String getType();\n\n    /**\n     * For the given plugin configuration (which also contains the type) extract one or more\n     * {@link ImageConfiguration} objects describing the image to manage\n     *\n     * @param unresolvedConfig the original, unresolved config\n     * @param project maven project\n     * @param session maven session\n     * @return list of image configuration. Must not be null but can be empty.\n     * @throws ExternalConfigHandlerException if there is a problem resolving the image configuration\n     */\n    List<ImageConfiguration> resolve(ImageConfiguration unresolvedConfig, MavenProject project, MavenSession session)\n        throws ExternalConfigHandlerException;\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/ExternalConfigHandlerException.java",
    "content": "package io.fabric8.maven.docker.config.handler;\n\npublic class ExternalConfigHandlerException extends RuntimeException\n{\n    private static final long serialVersionUID = -2742743075207582636L;\n\n    public ExternalConfigHandlerException(String message)\n    {\n        super(message);\n    }\n\n    public ExternalConfigHandlerException(String message, Throwable cause)\n    {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/ImageConfigResolver.java",
    "content": "package io.fabric8.maven.docker.config.handler;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.handler.compose.DockerComposeConfigHandler;\nimport io.fabric8.maven.docker.config.handler.property.PropertyConfigHandler;\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.component.annotations.Component;\nimport org.codehaus.plexus.component.annotations.Requirement;\nimport org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;\nimport org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;\n\n/**\n * Manager holding all config handlers for external configuration\n *\n * @author roland\n * @since 18/11/14\n */\n\n@Component(role = ImageConfigResolver.class, instantiationStrategy = \"singleton\")\npublic class ImageConfigResolver implements Initializable {\n    // Map type to handler\n    private Map<String,ExternalConfigHandler> registry;\n\n    // No List<ExternalConfigHandler> injection possible currently with Plexus.\n    // Strangely, only the first element is injected in the list.\n    // So the elements are injected via scalar field injection and collected later.\n    // Very ugly, but I dont see any other solution until Plexus is fixed.\n\n    @Requirement(role = PropertyConfigHandler.class)\n    private ExternalConfigHandler propertyConfigHandler;\n\n    @Requirement(role = DockerComposeConfigHandler.class)\n    private ExternalConfigHandler dockerComposeConfigHandler;\n\n    private Logger log;\n\n    @Override\n    public void initialize() throws InitializationException {\n        this.registry = new HashMap<>();\n        for (ExternalConfigHandler handler : new ExternalConfigHandler[] { propertyConfigHandler, dockerComposeConfigHandler }) {\n            if (handler != null) {\n                registry.put(handler.getType(), handler);\n            }\n        }\n    }\n\n    public void setLog(Logger log) {\n        this.log = log;\n    }\n\n    /**\n     * Resolve an image configuration. If it contains a reference to an external configuration\n     * the corresponding resolver is called and the resolved image configurations are returned (can\n     * be multiple).\n     *\n     * If no reference to an external configuration is found, the original configuration\n     * is returned directly.\n     *\n     * @param unresolvedConfig the configuration to resolve\n     * @param project project used for resolving\n     * @param session\n     * @return list of resolved image configurations\n     * @throws IllegalArgumentException if no type is given when an external reference configuration is provided\n     * or when the type is not known (i.e. no handler is registered for this type).\n     */\n    public List<ImageConfiguration> resolve(ImageConfiguration unresolvedConfig, MavenProject project, MavenSession session) {\n        injectExternalConfigActivation(unresolvedConfig, project);\n        Map<String,String> externalConfig = unresolvedConfig.getExternalConfig();\n        if (externalConfig != null) {\n            String type = externalConfig.get(\"type\");\n            if (type == null) {\n                throw new IllegalArgumentException(unresolvedConfig.getDescription() + \": No config type given\");\n            }\n            ExternalConfigHandler handler = registry.get(type);\n            if (handler == null) {\n                throw new IllegalArgumentException(unresolvedConfig.getDescription() + \": No handler for type \" + type + \" given\");\n            }\n            return handler.resolve(unresolvedConfig, project, session);\n        } else {\n            return Collections.singletonList(unresolvedConfig);\n        }\n    }\n\n    private void injectExternalConfigActivation(ImageConfiguration unresolvedConfig, MavenProject project) {\n        // Allow external activation of property configuration\n        String mode = ConfigHelper.getExternalConfigActivationProperty(project);\n\n        if(mode == null) {\n            return;\n        }\n\n        Map<String, String> externalConfig = unresolvedConfig.getExternalConfig();\n        if(externalConfig == null) {\n            externalConfig = new HashMap<>();\n            externalConfig.put(\"type\", propertyConfigHandler.getType());\n            externalConfig.put(\"mode\", mode);\n            unresolvedConfig.setExternalConfiguration(externalConfig);\n\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"Global %s=%s property activates property configuration for image\", ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY, mode);\n        }\n        else\n        {\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"Ignoring %s=%s property, image has <external> in POM which takes precedence\", ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY, mode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/compose/ComposeUtils.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport io.fabric8.maven.docker.util.DockerPathUtil;\nimport org.apache.maven.project.MavenProject;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Path-resolution methods\n */\nclass ComposeUtils {\n\n    /**\n     * Resolves a docker-compose file against the supplied base directory.  The returned {@code File} is guaranteed to\n     * be {@link File#isAbsolute() absolute}.\n     * <p>\n     * If {@code composeFile} is {@link File#isAbsolute() absolute}, then it is returned unmodified.  Otherwise, the\n     * {@code composeFile} is returned as an absolute {@code File} using the {@link #resolveAbsolutely(String,\n     * MavenProject) resolved} {@code baseDir} as its parent.\n     * </p>\n     *\n     * @param baseDir the base directory containing the docker-compose file (ignored if {@code composeFile} is absolute)\n     * @param composeFile the path of the docker-compose file, may be absolute\n     * @param project the {@code MavenProject} used to resolve the {@code baseDir}\n     * @return an absolute {@code File} reference to the {@code composeFile}\n     */\n    static File resolveComposeFileAbsolutely(String baseDir, String composeFile, MavenProject project) {\n        File yamlFile = new File(composeFile);\n        if (yamlFile.isAbsolute()) {\n            return yamlFile;\n        }\n\n        File toCanonicalize = new File(resolveAbsolutely(baseDir, project), composeFile);\n\n        try {\n            return toCanonicalize.getCanonicalFile();\n        } catch (IOException e) {\n            throw new RuntimeException(\"Unable to canonicalize the resolved docker-compose file path '\" + toCanonicalize + \"'\");\n        }\n    }\n\n    /**\n     * Resolves the supplied resource (a path or directory on the filesystem) relative the Maven {@link\n     * MavenProject#getBasedir() base directory}.  The returned {@code File} is guaranteed to be {@link\n     * File#isAbsolute() absolute}.  The returned file is <em>not</em> guaranteed to exist.\n     * <p>\n     * If {@code pathToResolve} is {@link File#isAbsolute() absolute}, then it is returned unmodified.  Otherwise, the\n     * {@code pathToResolve} is returned as an absolute {@code File} using the {@link MavenProject#getBasedir() Maven\n     * Project base directory} as its parent.\n     * </p>\n     *\n     * @param pathToResolve represents a filesystem resource, which may be an absolute path\n     * @param project the Maven project used to resolve non-absolute path resources, may be {@code null} if\n     *                {@code pathToResolve} is {@link File#isAbsolute() absolute}\n     * @return an absolute {@code File} reference to {@code pathToResolve}; <em>not</em> guaranteed to exist\n     * @throws IllegalArgumentException if {@code pathToResolve} is relative, and {@code project} is {@code null} or\n     *                                  provides a relative {@link MavenProject#getBasedir() base directory}\n     */\n    static File resolveAbsolutely(String pathToResolve, MavenProject project) {\n        // avoid an NPE if the Maven project is not needed by DockerPathUtil\n        return DockerPathUtil.resolveAbsolutely(pathToResolve,\n                (project == null) ? null : project.getBasedir().getAbsolutePath());\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandler.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport java.io.*;\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.*;\nimport io.fabric8.maven.docker.config.handler.ExternalConfigHandler;\nimport io.fabric8.maven.docker.config.handler.ExternalConfigHandlerException;\nimport io.fabric8.maven.docker.util.DeepCopy;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.filtering.MavenFilteringException;\nimport org.apache.maven.shared.filtering.MavenReaderFilter;\nimport org.apache.maven.shared.filtering.MavenReaderFilterRequest;\nimport org.yaml.snakeyaml.Yaml;\n\nimport static io.fabric8.maven.docker.config.handler.compose.ComposeUtils.resolveAbsolutely;\nimport static io.fabric8.maven.docker.config.handler.compose.ComposeUtils.resolveComposeFileAbsolutely;\n\n\n/**\n * Docker Compose handler for allowing a docker-compose file to be used\n * to specify the docker images.\n */\n\n// Moved temporarily to resources/META-INF/plexus/components.xml because of https://github.com/codehaus-plexus/plexus-containers/issues/4\n// @Component(role = ExternalConfigHandler.class)\npublic class DockerComposeConfigHandler implements ExternalConfigHandler {\n\n    @Override\n    public String getType() {\n        return \"compose\";\n    }\n\n    // Enable later when issue above is fixed. In the meantime its declared in the components.xml, too\n    // @Requirement(role = MavenReaderFilter.class)\n    MavenReaderFilter readerFilter;\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public List<ImageConfiguration> resolve(ImageConfiguration unresolvedConfig, MavenProject project, MavenSession session) {\n        List<ImageConfiguration> resolved = new ArrayList<>();\n\n        DockerComposeConfiguration handlerConfig = new DockerComposeConfiguration(unresolvedConfig.getExternalConfig());\n        File composeFile = resolveComposeFileAbsolutely(handlerConfig.getBasedir(), handlerConfig.getComposeFile(), project);\n\n        for (Object composeO : getComposeConfigurations(composeFile, project, session)) {\n            Map<String, Object> compose = (Map<String, Object>) composeO;\n            validateVersion(compose, composeFile);\n            Map<String, Object> services = (Map<String, Object>) compose.get(\"services\");\n            for (Map.Entry<String, Object> entry : services.entrySet()) {\n                String serviceName = entry.getKey();\n                Map<String, Object> serviceDefinition = (Map<String, Object>) entry.getValue();\n\n                DockerComposeServiceWrapper mapper = new DockerComposeServiceWrapper(serviceName, composeFile, serviceDefinition, unresolvedConfig, resolveAbsolutely(handlerConfig.getBasedir(), project));\n                resolved.add(buildImageConfiguration(mapper, composeFile.getParentFile(), unresolvedConfig, handlerConfig));\n            }\n        }\n\n        return resolved;\n    }\n\n    private void validateVersion(Map<String, Object> compose, File file) {\n        Object version = compose.get(\"version\");\n        if (version == null || !isVersion2(version.toString().trim())) {\n            throw new ExternalConfigHandlerException(\"Only version 2.x of the docker-compose format is supported for \" + file);\n        }\n    }\n\n    private boolean isVersion2(String version) {\n        return version.equals(\"2\") || version.startsWith(\"2.\");\n    }\n\n    private String extractDockerFilePath(DockerComposeServiceWrapper mapper, File parentDir) {\n        if (mapper.requiresBuild()) {\n            File buildDir = new File(mapper.getBuildDir());\n            String dockerFile = mapper.getDockerfile();\n            if (dockerFile == null) {\n                dockerFile = \"Dockerfile\";\n            }\n            File ret = new File(buildDir, dockerFile);\n            return ret.isAbsolute() ? ret.getAbsolutePath() : new File(parentDir, ret.getPath()).getAbsolutePath();\n        } else {\n            return null;\n        }\n    }\n\n    private ImageConfiguration buildImageConfiguration(DockerComposeServiceWrapper mapper,\n                                                       File composeParent,\n                                                       ImageConfiguration unresolvedConfig,\n                                                       DockerComposeConfiguration handlerConfig) {\n        ImageConfiguration.Builder builder = new ImageConfiguration.Builder()\n                .name(getImageName(mapper, unresolvedConfig))\n                .alias(mapper.getAlias())\n                .buildConfig(createBuildImageConfiguration(mapper, composeParent, unresolvedConfig, handlerConfig))\n                .runConfig(createRunConfiguration(mapper, unresolvedConfig));\n        if (serviceMatchesAlias(mapper, unresolvedConfig)) {\n            builder.watchConfig(DeepCopy.copy(unresolvedConfig.getWatchConfiguration()));\n        }\n        return builder.build();\n    }\n\n    private String getImageName(DockerComposeServiceWrapper mapper, ImageConfiguration unresolvedConfig) {\n        String name = mapper.getImage();\n        if (name != null) {\n            return name;\n        } else if (unresolvedConfig.getAlias() != null && unresolvedConfig.getAlias().equals(mapper.getAlias())) {\n            return unresolvedConfig.getName();\n        } else {\n            return null;\n        }\n    }\n\n    private Iterable<Object> getComposeConfigurations(File composePath, MavenProject project, MavenSession session) {\n        try {\n            Yaml yaml = new Yaml();\n            return yaml.loadAll(getFilteredReader(composePath, project, session));\n        }\n        catch (FileNotFoundException | MavenFilteringException e) {\n            throw new ExternalConfigHandlerException(\"failed to load external configuration: \" + composePath, e);\n        }\n    }\n\n    private Reader getFilteredReader(File path, MavenProject project, MavenSession session) throws FileNotFoundException, MavenFilteringException {\n        MavenReaderFilterRequest request =\n            new MavenReaderFilterRequest(\n                new FileReader(path),\n                true,\n                project,\n                Collections.<String>emptyList(),\n                false,\n                session,\n                null);\n        //request.setEscapeString(\"$\");\n        return readerFilter.filter(request);\n    }\n\n    private BuildImageConfiguration createBuildImageConfiguration(DockerComposeServiceWrapper mapper,\n                                                                  File composeParent,\n                                                                  ImageConfiguration imageConfig,\n                                                                  DockerComposeConfiguration handlerConfig) {\n        BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n        if (handlerConfig.isIgnoreBuild() || !mapper.requiresBuild()) {\n            if (serviceMatchesAlias(mapper, imageConfig)) {\n                // Only when the specified image name maps to the current docker-compose service\n                return buildConfig;\n            } else {\n                return null;\n            }\n        }\n\n        // Build from the specification as given in the docker-compose file\n        BuildImageConfiguration.Builder builder = new BuildImageConfiguration.Builder(buildConfig)\n                .dockerFile(extractDockerFilePath(mapper, composeParent))\n                .args(mapper.getBuildArgs());\n        return builder.build();\n    }\n\n    private boolean serviceMatchesAlias(DockerComposeServiceWrapper mapper, ImageConfiguration imageConfig) {\n        return mapper.getAlias() != null && mapper.getAlias().equals(imageConfig.getAlias());\n    }\n\n    private RunImageConfiguration createRunConfiguration(DockerComposeServiceWrapper wrapper, ImageConfiguration imageConfig) {\n        RunImageConfiguration.Builder builder =\n            serviceMatchesAlias(wrapper, imageConfig) ?\n                new RunImageConfiguration.Builder(imageConfig.getRunConfiguration()) :\n                new RunImageConfiguration.Builder();\n        return builder\n                .capAdd(wrapper.getCapAdd())\n                .capDrop(wrapper.getCapDrop())\n                .cmd(wrapper.getCommand())\n                // cgroup_parent not supported\n                // container_name is taken as an alias and ignored here for run config\n                // devices not supported\n                .dependsOn(wrapper.getDependsOn()) // depends_on relies that no container_name is set\n                .dns(wrapper.getDns())\n                .dnsSearch(wrapper.getDnsSearch())\n                .tmpfs(wrapper.getTmpfs())\n                .entrypoint(wrapper.getEntrypoint())\n                // env_file not supported\n                .env(wrapper.getEnvironment())\n                // expose (for running containers) not supported\n                // extends not supported\n                .extraHosts(wrapper.getExtraHosts())\n                // image added as top-level\n                .labels(wrapper.getLabels())\n                .links(wrapper.getLinks()) // external_links and links are handled the same in d-m-p\n                .log(wrapper.getLogConfiguration())\n                .network(wrapper.getNetworkConfig()) // TODO: Up to now only a single network is supported and not ipv4, ipv6\n                // pid not supported\n                .ports(wrapper.getPortMapping())\n                // security_opt not supported\n                // stop_signal not supported\n                .ulimits(wrapper.getUlimits())\n                .volumes(wrapper.getVolumeConfig())\n                .cpuShares(wrapper.getCpuShares())\n                .cpus(wrapper.getCpusCount())\n                .cpuSet(wrapper.getCpuSet())\n                // cpu_quota n.s.\n                .domainname(wrapper.getDomainname())\n                .hostname(wrapper.getHostname())\n                // ipc n.s.\n                // mac_address n.s.\n                .memory(wrapper.getMemory())\n                .memorySwap(wrapper.getMemorySwap())\n                .privileged(wrapper.getPrivileged())\n                // read_only n.s.\n                .restartPolicy(wrapper.getRestartPolicy())\n                .shmSize(wrapper.getShmSize())\n                // stdin_open n.s.\n                // tty n.s.\n                .user(wrapper.getUser())\n                .workingDir(wrapper.getWorkingDir())\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfiguration.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport java.util.*;\n\npublic class DockerComposeConfiguration {\n\n    private final String basedir;\n    private final String composeFile;\n    private final boolean ignoreBuild;\n    public DockerComposeConfiguration(Map<String, String> config) {\n        basedir = config.containsKey(\"basedir\") ? config.get(\"basedir\") : \"src/main/docker\";\n        composeFile = config.containsKey(\"composeFile\") ? config.get(\"composeFile\") : \"docker-compose.yml\";\n        ignoreBuild = config.containsKey(\"ignoreBuild\") ? Boolean.parseBoolean(config.get(\"ignoreBuilder\")) : false;\n    }\n\n    String getBasedir() {\n        return basedir;\n    }\n\n    String getComposeFile() {\n        return composeFile;\n    }\n\n    public boolean isIgnoreBuild() {\n        return ignoreBuild;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeServiceWrapper.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.UlimitConfig;\nimport io.fabric8.maven.docker.util.VolumeBindingUtil;\n\n\nclass DockerComposeServiceWrapper {\n\n    private final Map<String, Object> configuration;\n    private final String name;\n    private final File composeFile;\n    private final ImageConfiguration enclosingImageConfig;\n    private final File baseDir;\n\n    DockerComposeServiceWrapper(String serviceName, File composeFile, Map<String, Object> serviceDefinition,\n                                ImageConfiguration enclosingImageConfig, File baseDir) {\n        this.name = serviceName;\n        this.composeFile = composeFile;\n        this.configuration = serviceDefinition;\n        this.enclosingImageConfig = enclosingImageConfig;\n\n        if (!baseDir.isAbsolute()) {\n            throw new IllegalArgumentException(\n                    \"Expected the base directory '\" + baseDir + \"' to be an absolute path.\");\n        }\n        this.baseDir = baseDir;\n    }\n\n    String getAlias() {\n        // 'container_name' takes precidence\n        String alias = asString(\"container_name\");\n        return (alias != null) ? alias : name;\n    }\n\n    String getImage() {\n        return asString(\"image\");\n    }\n\n    // ==================================================================================\n    // Build config:\n\n    boolean requiresBuild() {\n        return asObject(\"build\") != null;\n    }\n\n    String getBuildDir() {\n        Object build = asObject(\"build\");\n        if (build == null) {\n            return null;\n        }\n        if (build instanceof String) {\n            return (String) build;\n        }\n        if (! (build instanceof Map)) {\n            throwIllegalArgumentException(\"build:' must be either a String or a Map\");\n        }\n        Map<String, String> buildConfig = (Map<String, String>) build;\n        if (!buildConfig.containsKey(\"context\")) {\n            throwIllegalArgumentException(\"'build:' a context directory for a build must be specified\");\n        }\n        return buildConfig.get(\"context\");\n    }\n\n    String getDockerfile() {\n        Object build = asObject(\"build\");\n        if (build instanceof Map) {\n            return (String) ((Map) build).get(\"dockerfile\");\n        } else {\n            return null;\n        }\n    }\n\n    Map<String, String> getBuildArgs() {\n        Object build = asObject(\"build\");\n        if (build instanceof Map) {\n            return (Map<String, String>) ((Map) build).get(\"args\");\n        } else {\n            return null;\n        }\n    }\n\n    // ===================================================================================\n    // Run config:\n\n    List<String> getCapAdd() {\n        return asList(\"cap_add\");\n    }\n\n    List<String> getCapDrop() {\n        return asList(\"cap_drop\");\n    }\n\n    Arguments getCommand() {\n        Object command = asObject(\"command\");\n        return command != null ? asArguments(command, \"command\") : null;\n    }\n\n    List<String> getDependsOn() {\n        return asList(\"depends_on\");\n    }\n\n    List<String> getDns() {\n        return asList(\"dns\");\n    }\n\n    List<String> getDnsSearch() {\n        return asList(\"dns_search\");\n    }\n\n    List<String> getTmpfs() {\n        return asList(\"tmpfs\");\n    }\n\n    Arguments getEntrypoint() {\n        Object entrypoint = asObject(\"entrypoint\");\n        return entrypoint != null ? asArguments(entrypoint, \"entrypoint\") : null;\n    }\n\n    Map<String, String> getEnvironment() {\n        // TODO: load the env files from compose as given with env_file and add them all to the map\n        return asMap(\"environment\");\n    }\n\n    // external_links are added with \"links\"\n\n    List<String> getExtraHosts() {\n        return asList(\"extra_hosts\");\n    }\n\n    // image is added as top-level in image configuration\n\n    Map<String, String> getLabels() {\n        return asMap(\"labels\");\n    }\n\n    public List<String> getLinks() {\n        List<String> ret = new ArrayList<>();\n        ret.addAll(this.<String>asList(\"links\"));\n        ret.addAll(this.<String>asList(\"external_links\"));\n        return ret;\n    }\n\n    LogConfiguration getLogConfiguration() {\n        Object logConfig = asObject(\"logging\");\n        if (logConfig == null) {\n            return null;\n        }\n        if (!(logConfig instanceof Map)) {\n            throwIllegalArgumentException(\"'logging' has to be a map and not \" + logConfig.getClass());\n        }\n        Map<String, Object> config = (Map<String, Object>) logConfig;\n        return new LogConfiguration.Builder()\n            .logDriverName((String) config.get(\"driver\"))\n            .logDriverOpts((Map<String, String>) config.get(\"options\"))\n            .build();\n    }\n\n    NetworkConfig getNetworkConfig() {\n        String net = asString(\"network_mode\");\n        if (net != null) {\n            return new NetworkConfig(net);\n        }\n        Object networks = asObject(\"networks\");\n        if (networks == null) {\n            return null;\n        }\n        if (networks instanceof List) {\n            List<String> toJoin = (List<String>) networks;\n            if (toJoin.size() > 1) {\n                throwIllegalArgumentException(\"'networks:' Only one custom network to join is supported currently\");\n            }\n            return new NetworkConfig(NetworkConfig.Mode.custom, toJoin.get(0));\n        } else if (networks instanceof Map) {\n            Map<String,Object> toJoin = (Map<String, Object>) networks;\n            if (toJoin.size() > 1) {\n                throwIllegalArgumentException(\"'networks:' Only one custom network to join is supported currently\");\n            }\n            String custom = toJoin.keySet().iterator().next();\n            NetworkConfig ret = new NetworkConfig(NetworkConfig.Mode.custom, custom);\n            Object aliases = toJoin.get(custom);\n            if (aliases != null) {\n                if (aliases instanceof List) {\n                    for (String alias : (List<String>) aliases) {\n                        ret.addAlias(alias);\n                    }\n                } else if (aliases instanceof Map) {\n                \tMap<String, List<String>> map = (Map<String, List<String>>) aliases;\n                    if (map.containsKey(\"aliases\")) {\n                        for (String alias : map.get(\"aliases\")) {\n                            ret.addAlias(alias);\n                        }\n                    } else {\n                        throwIllegalArgumentException(\n                                \"'networks:' Aliases must be given as a map of strings. 'aliases' key not founded\");\n                    }\n                } else {\n                    throwIllegalArgumentException(\"'networks:' No aliases entry found in network config map\");\n                }\n            }\n            return ret;\n        } else {\n            throwIllegalArgumentException(\"'networks:' must beu either a list or a map\");\n            return null;\n        }\n    }\n\n    List<String> getPortMapping() {\n        List<String> fromYml = asList(\"ports\");\n        int size = fromYml.size();\n\n        List<String> ports = new ArrayList<>(size);\n        for (int i = 0; i < size; i++) {\n            String port = fromYml.get(i);\n            if (port.contains(\":\")) {\n                ports.add(port);\n            }\n            else {\n                /*\n                 * docker-compose allows just the port number which triggers a random port and the plugin does not, so construct a property\n                 * name to mimic the required behavior. names will always based on position, and not the number of times we create the\n                 * string.\n                 */\n                ports.add(String.format(\"%s_port_%s:%s\", getAlias(), i + 1, port));\n            }\n        }\n        return ports;\n    }\n\n    List<UlimitConfig> getUlimits() {\n        Object ulimits = asObject(\"ulimits\");\n        if (ulimits == null) {\n            return null;\n        }\n        if (!(ulimits instanceof Map)) {\n            throwIllegalArgumentException(\"'ulimits:' must be a map\");\n        }\n        Map<String, Object> ulimitMap = (Map<String, Object>) ulimits;\n        List<UlimitConfig> ret = new ArrayList<>();\n        for (String ulimit : ulimitMap.keySet()) {\n            Object val = ulimitMap.get(ulimit);\n            if (val instanceof Map) {\n                Map<String, Integer> valMap = (Map<String, Integer>) val;\n                Integer soft = valMap.get(\"soft\");\n                Integer hard = valMap.get(\"hard\");\n                ret.add(new UlimitConfig(ulimit, hard, soft));\n            } else if (val instanceof Integer) {\n                ret.add(new UlimitConfig(ulimit, (Integer) val, null));\n            } else {\n                throwIllegalArgumentException(\"'ulimits:' invalid limit value \" + val + \" (class : \" + val.getClass() + \")\");\n            }\n        }\n        return ret;\n    }\n\n    RunVolumeConfiguration getVolumeConfig() {\n        RunVolumeConfiguration.Builder builder = new RunVolumeConfiguration.Builder();\n        List<String> volumes = asList(\"volumes\");\n        boolean added = false;\n        if (volumes.size() > 0) {\n            builder.bind(volumes);\n            added = true;\n        }\n        List<String> volumesFrom = asList(\"volumes_from\");\n        if (volumesFrom.size() > 0) {\n            builder.from(volumesFrom);\n            added = true;\n        }\n\n        if (added) {\n            RunVolumeConfiguration configuration = builder.build();\n            VolumeBindingUtil.resolveRelativeVolumeBindings(baseDir, configuration);\n            return configuration;\n        }\n\n        return null;\n    }\n\n    String getDomainname() {\n        return asString(\"domainname\");\n    }\n\n    String getHostname() {\n        return asString(\"hostname\");\n    }\n\n    Long getMemory() {\n        return asLong(\"mem_limit\");\n    }\n\n    Long getMemorySwap() {\n        return asLong(\"memswap_limit\");\n    }\n\n    Boolean getPrivileged() {\n        return asBoolean(\"privileged\");\n    }\n\n    RestartPolicy getRestartPolicy() {\n        String restart = asString(\"restart\");\n        if (restart == null) {\n            return null;\n        }\n\n        RestartPolicy.Builder builder = new RestartPolicy.Builder();\n        if (restart.contains(\":\")) {\n            String[] parts = restart.split(\":\", 2);\n            builder.name(parts[0]).retry(Integer.valueOf(parts[1]));\n        }\n        else {\n            builder.name(restart);\n        }\n\n        return builder.build();\n    }\n\n    Long getShmSize() {\n        return asLong(\"shm_size\");\n    }\n\n    String getUser() {\n        return asString(\"user\");\n    }\n\n    String getWorkingDir() {\n        return asString(\"working_dir\");\n    }\n\n    // ================================================================\n    // Not used yet:\n\n    public String getCGroupParent() {\n        return asString(\"cgroup_parent\");\n    }\n\n    public String getCpuSet() {\n        return asString(\"cpuset\");\n    }\n\n    public Long getCpuShares() {\n        return asLong(\"cpu_shares\");\n    }\n\n    public Long getCpusCount(){\n        Double cpus = asDouble(\"cpus\");\n        return convertToNanoCpus(cpus);\n    }\n\n    public List<String> getDevices() {\n        return asList(\"devices\");\n    }\n\n    // =======================================================================================================\n    // Helper methods\n\n    private Object asObject(String key) {\n        return configuration.get(key);\n    }\n\n    private String asString(String key) {\n        return String.class.cast(configuration.get(key));\n    }\n\n    private Long asLong(String key) {\n        Long value = null;\n        if (configuration.containsKey(key)) {\n            value = Long.valueOf(configuration.get(key).toString());\n        }\n        return value;\n    }\n\n    private Boolean asBoolean(String key) {\n        Boolean value = null;\n        if (configuration.containsKey(key)) {\n            value = Boolean.valueOf(configuration.get(key).toString());\n        }\n        return value;\n    }\n\n    private <T> List<T> asList(String key) {\n        if (configuration.containsKey(key)) {\n            Object value = configuration.get(key);\n            if (value instanceof String) {\n                value = Arrays.asList(value);\n            }\n\n            return List.class.cast(value);\n        }\n\n        return Collections.emptyList();\n    }\n\n    private Map<String, String> asMap(String key) {\n        if (configuration.containsKey(key)) {\n            Object value = configuration.get(key);\n            if (value instanceof List) {\n                value = convertToMap(List.class.cast(value));\n            }\n            return Map.class.cast(value);\n        }\n\n        return Collections.emptyMap();\n    }\n\n    private Double asDouble(String key){\n        Double value = null;\n        if (configuration.containsKey(key)) {\n            value = Double.valueOf(configuration.get(key).toString());\n        }\n        return value;\n    }\n\n    private Arguments asArguments(Object command, String label) {\n        if (command instanceof String) {\n            return new Arguments((String) command);\n        } else if (command instanceof List) {\n            return new Arguments((List<String>) command);\n        } else {\n            throwIllegalArgumentException(String.format(\"'%s' must be either String or List but not %s\", label, command.getClass()));\n            return null;\n        }\n    }\n\n    private Map<String, String> convertToMap(List<String> list) {\n        Map<String, String> map = new HashMap<>(list.size());\n        for (String entry : list) {\n            String[] parts = entry.split(\"=\", 2);\n            map.put(parts[0], parts[1]);\n        }\n        return map;\n    }\n\n    private Long convertToNanoCpus(Double cpus){\n        if(cpus == null){\n            return null;\n        }\n        return (long)(cpus * 1000000000);\n    }\n\n    private void throwIllegalArgumentException(String msg) {\n        throw new IllegalArgumentException(String.format(\"%s: %s - \", composeFile, name) + msg);\n    }\n\n}\n\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;/*\n *\n * Copyright 2014 Roland Huss\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\n/**\n * Enum holding possible configuration keys\n *\n * @author roland\n * @since 07/12/14\n */\npublic enum ConfigKey {\n\n    ALIAS,\n    ARGS(ValueCombinePolicy.Merge),\n    ASSEMBLY_BASEDIR(\"assembly.baseDir\"),\n    ASSEMBLY_DESCRIPTOR(\"assembly.descriptor\"),\n    ASSEMBLY_DESCRIPTOR_REF(\"assembly.descriptorRef\"),\n    ASSEMBLY_EXPORT_BASEDIR(\"assembly.exportBaseDir\"),\n    ASSEMBLY_IGNORE_PERMISSIONS(\"assembly.ignorePermissions\"),\n    ASSEMBLY_PERMISSIONS(\"assembly.permissions\"),\n    ASSEMBLY_DOCKER_FILE_DIR(\"assembly.dockerFileDir\"),\n    ASSEMBLY_USER(\"assembly.user\"),\n    ASSEMBLY_MODE(\"assembly.mode\"),\n    ASSEMBLY_TARLONGFILEMODE(\"assembly.tarLongFileMode\"),\n    AUTO_REMOVE,\n    BIND,\n    BUILD_OPTIONS,\n    CAP_ADD,\n    CAP_DROP,\n    CLEANUP,\n    CPUS,\n    CPUSET,\n    CPUSHARES,\n    CACHE_FROM,\n    CMD,\n    CONTEXT_DIR,\n    DEPENDS_ON,\n    DOMAINNAME,\n    DNS,\n    DNS_SEARCH,\n    DOCKER_ARCHIVE,\n    DOCKER_FILE,\n    DOCKER_FILE_DIR,\n    ENTRYPOINT,\n    ENV,\n    ENV_PROPERTY_FILE,\n    ENV_BUILD(\"envBuild\", ValueCombinePolicy.Merge),\n    ENV_RUN(\"envRun\", ValueCombinePolicy.Merge),\n    EXPOSED_PROPERTY_KEY,\n    EXTRA_HOSTS,\n    FILTER,\n    FROM,\n    FROM_EXT,\n    HEALTHCHECK,\n    HEALTHCHECK_MODE(\"healthcheck.mode\"),\n    HEALTHCHECK_INTERVAL(\"healthcheck.interval\"),\n    HEALTHCHECK_TIMEOUT(\"healthcheck.timeout\"),\n    HEALTHCHECK_START_PERIOD(\"healthcheck.startPeriod\"),\n    HEALTHCHECK_RETRIES(\"healthcheck.retries\"),\n    HEALTHCHECK_CMD(\"healthcheck.cmd\"),\n    HOSTNAME,\n    IMAGE_PULL_POLICY_BUILD(\"imagePullPolicy.build\"),\n    IMAGE_PULL_POLICY_RUN(\"imagePullPolicy.run\"),\n    LABELS(ValueCombinePolicy.Merge),\n    LINKS,\n    LOAD_NAME_PATTERN,\n    LOG_ENABLED(\"log.enabled\"),\n    LOG_PREFIX(\"log.prefix\"),\n    LOG_DATE(\"log.date\"),\n    LOG_FILE(\"log.file\"),\n    LOG_COLOR(\"log.color\"),\n    LOG_DRIVER_NAME(\"log.driver.name\"),\n    LOG_DRIVER_OPTS(\"log.driver.opts\"),\n    MAINTAINER,\n    MEMORY,\n    MEMORY_SWAP,\n    NAME,\n    NAMING_STRATEGY,\n    NET,\n    NETWORK_MODE(\"network.mode\"),\n    NETWORK_NAME(\"network.name\"),\n    NETWORK_ALIAS(\"network.alias\"),\n    NO_CACHE,\n    SQUASH,\n    OPTIMISE,\n    PORT_PROPERTY_FILE,\n    PORTS(ValueCombinePolicy.Merge),\n    PRIVILEGED,\n    READ_ONLY,\n    REGISTRY,\n    REMOVE_NAME_PATTERN,\n    RESTART_POLICY_NAME(\"restartPolicy.name\"),\n    RESTART_POLICY_RETRY(\"restartPolicy.retry\"),\n    SHELL,\n    RUN,\n    SECURITY_OPTS,\n    SHMSIZE,\n    SKIP_BUILD(\"skip.build\"),\n    SKIP_PUSH,\n    SKIP_RUN(\"skip.run\"),\n    STOP_NAME_PATTERN,\n    TAGS(ValueCombinePolicy.Merge),\n    TMPFS,\n    ULIMITS,\n    USER,\n    VOLUMES,\n    VOLUMES_FROM,\n    WAIT_LOG(\"wait.log\"),\n    WAIT_TIME(\"wait.time\"),\n    WAIT_HEALTHY(\"wait.healthy\"),\n    WAIT_URL(\"wait.url\"),\n    WAIT_HTTP_URL(\"wait.http.url\"),\n    WAIT_HTTP_METHOD(\"wait.http.method\"),\n    WAIT_HTTP_STATUS(\"wait.http.status\"),\n    WAIT_KILL(\"wait.kill\"),\n    WAIT_EXEC_POST_START(\"wait.exec.postStart\"),\n    WAIT_EXEC_PRE_STOP(\"wait.exec.preStop\"),\n    WAIT_EXEC_BREAK_ON_ERROR(\"wait.exec.breakOnError\"),\n    WAIT_EXIT(\"wait.exit\"),\n    WAIT_SHUTDOWN(\"wait.shutdown\"),\n    WAIT_TCP_MODE(\"wait.tcp.mode\"),\n    WAIT_TCP_HOST(\"wait.tcp.host\"),\n    WAIT_TCP_PORT(\"wait.tcp.port\"),\n    WATCH_INTERVAL(\"watch.interval\"),\n    WATCH_MODE(\"watch.mode\"),\n    WATCH_POSTGOAL(\"watch.postGoal\"),\n    WATCH_POSTEXEC(\"watch.postExec\"),\n    WORKDIR,\n    WORKING_DIR;\n\n    ConfigKey() {\n        this(ValueCombinePolicy.Replace);\n    }\n\n    ConfigKey(String key) {\n        this(key, ValueCombinePolicy.Replace);\n    }\n\n    ConfigKey(ValueCombinePolicy valueCombinePolicy) {\n        this.key = toVarName(name());\n        this.valueCombinePolicy = valueCombinePolicy;\n    }\n\n    ConfigKey(String key, ValueCombinePolicy valueCombinePolicy) {\n        this.key = key;\n        this.valueCombinePolicy = valueCombinePolicy;\n    }\n\n    private final String key;\n    private final ValueCombinePolicy valueCombinePolicy;\n\n    public static String DEFAULT_PREFIX = \"docker\";\n\n    // Convert to camel case\n    private String toVarName(String s) {\n        String[] parts = s.split(\"_\");\n        StringBuilder var = new StringBuilder(parts[0].toLowerCase());\n        for (int i = 1; i < parts.length; i++) {\n            var.append(parts[i].substring(0, 1).toUpperCase()).append(parts[i].substring(1).toLowerCase());\n        }\n        return var.toString();\n    }\n\n    public String asPropertyKey(String prefix) {\n        return prefix + \".\" + key;\n    }\n\n    public String asPropertyKey() {\n        return DEFAULT_PREFIX + \".\" + key;\n    }\n\n    public ValueCombinePolicy getValueCombinePolicy() {\n        return valueCombinePolicy;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.function.Supplier;\n\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.HealthCheckConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.UlimitConfig;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.config.WatchImageConfiguration;\nimport io.fabric8.maven.docker.config.handler.ExternalConfigHandler;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.util.CollectionUtils;\n\nimport static io.fabric8.maven.docker.config.handler.property.ConfigKey.*;\n\n/**\n * @author roland\n * @since 18/11/14\n */\n// Moved temporarily to resources/META-INF/plexus/components.xml because of https://github.com/codehaus-plexus/plexus-containers/issues/4\n// @Component(role = ExternalConfigHandler.class)\npublic class PropertyConfigHandler implements ExternalConfigHandler {\n\n    public static final String TYPE_NAME = \"properties\";\n    public static final String DEFAULT_PREFIX = \"docker\";\n\n    @Override\n    public String getType() {\n        return TYPE_NAME;\n    }\n\n    @Override\n    public List<ImageConfiguration> resolve(ImageConfiguration fromConfig, MavenProject project, MavenSession session)\n        throws IllegalArgumentException {\n        Map<String, String> externalConfig = fromConfig.getExternalConfig();\n        String prefix = getPrefix(externalConfig);\n        Properties properties = EnvUtil.getPropertiesWithSystemOverrides(project);\n        PropertyMode propertyMode = getMode(externalConfig);\n        ValueProvider valueProvider = new ValueProvider(prefix, properties, propertyMode);\n\n        RunImageConfiguration run = extractRunConfiguration(fromConfig, valueProvider);\n        BuildImageConfiguration build = extractBuildConfiguration(fromConfig, valueProvider, project);\n        WatchImageConfiguration watch = extractWatchConfig(fromConfig, valueProvider);\n        String name = valueProvider.getString(NAME, fromConfig.getName());\n        String alias = valueProvider.getString(ALIAS, fromConfig.getAlias());\n        String removeNamePattern = valueProvider.getString(REMOVE_NAME_PATTERN, fromConfig.getRemoveNamePattern());\n        String stopNamePattern = valueProvider.getString(STOP_NAME_PATTERN, fromConfig.getStopNamePattern());\n\n        if (name == null) {\n            throw new IllegalArgumentException(String.format(\"Mandatory property [%s] is not defined\", NAME));\n        }\n\n        return Collections.singletonList(\n                new ImageConfiguration.Builder()\n                        .name(name)\n                        .alias(alias)\n                        .removeNamePattern(removeNamePattern)\n                        .stopNamePattern(stopNamePattern)\n                        .runConfig(run)\n                        .buildConfig(build)\n                        .watchConfig(watch)\n                        .build());\n    }\n\n    private boolean isStringValueNull(ValueProvider valueProvider, BuildImageConfiguration config, ConfigKey key, Supplier<String> supplier) {\n        return valueProvider.getString(key, config == null ? null : supplier.get()) != null;\n    }\n    // Enable build config only when a `.from.`, `.dockerFile.`, or `.dockerFileDir.` is configured\n    private boolean buildConfigured(BuildImageConfiguration config, ValueProvider valueProvider, MavenProject project) {\n\n\n        if (isStringValueNull(valueProvider, config, FROM, () -> config.getFrom())) {\n            return true;\n        }\n\n        if (valueProvider.getMap(FROM_EXT, config == null ? null : config.getFromExt()) != null) {\n            return true;\n        }\n        if (isStringValueNull(valueProvider, config, DOCKER_FILE, () -> config.getDockerFileRaw() ))  {\n            return true;\n        }\n        if (isStringValueNull(valueProvider, config, DOCKER_ARCHIVE, () -> config.getDockerArchiveRaw())) {\n            return true;\n        }\n\n        if (isStringValueNull(valueProvider, config, CONTEXT_DIR, () -> config.getContextDirRaw())) {\n            return true;\n        }\n\n        if (isStringValueNull(valueProvider, config, DOCKER_FILE_DIR, () -> config.getDockerFileDirRaw())) {\n            return true;\n        }\n\n        // Simple Dockerfile mode\n        return new File(project.getBasedir(),\"Dockerfile\").exists();\n    }\n\n\n    private BuildImageConfiguration extractBuildConfiguration(ImageConfiguration fromConfig, ValueProvider valueProvider, MavenProject project) {\n        BuildImageConfiguration config = fromConfig.getBuildConfiguration();\n        if (!buildConfigured(config, valueProvider, project)) {\n            return null;\n        }\n\n        return new BuildImageConfiguration.Builder()\n                .cmd(extractArguments(valueProvider, CMD, config == null ? null : config.getCmd()))\n                .cleanup(valueProvider.getString(CLEANUP, config == null ? null : config.getCleanup()))\n                .noCache(valueProvider.getBoolean(NO_CACHE, config == null ? null : config.getNoCache()))\n                .squash(valueProvider.getBoolean(SQUASH, config == null ? null : config.getSquash()))\n                .cacheFrom(valueProvider.getString(CACHE_FROM, config == null ? null : (config.getCacheFrom() == null ? null : config.getCacheFrom().toString())))\n                .optimise(valueProvider.getBoolean(OPTIMISE, config == null ? null : config.getOptimise()))\n                .entryPoint(extractArguments(valueProvider, ENTRYPOINT, config == null ? null : config.getEntryPoint()))\n                .assembly(extractAssembly(config == null ? null : config.getAssemblyConfiguration(), valueProvider))\n                .env(CollectionUtils.mergeMaps(\n                        valueProvider.getMap(ENV_BUILD, config == null ? null : config.getEnv()),\n                        valueProvider.getMap(ENV, Collections.<String, String>emptyMap())\n                ))\n                .args(valueProvider.getMap(ARGS, config == null ? null : config.getArgs()))\n                .labels(valueProvider.getMap(LABELS, config == null ? null : config.getLabels()))\n                .ports(extractPortValues(config == null ? null : config.getPorts(), valueProvider))\n                .shell(extractArguments(valueProvider, SHELL, config == null ? null : config.getShell()))\n                .runCmds(valueProvider.getList(RUN, config == null ? null : config.getRunCmds()))\n                .from(valueProvider.getString(FROM, config == null ? null : config.getFrom()))\n                .fromExt(valueProvider.getMap(FROM_EXT, config == null ? null : config.getFromExt()))\n                .registry(valueProvider.getString(REGISTRY, config == null ? null : config.getRegistry()))\n                .volumes(valueProvider.getList(VOLUMES, config == null ? null : config.getVolumes()))\n                .tags(valueProvider.getList(TAGS, config == null ? null : config.getTags()))\n                .maintainer(valueProvider.getString(MAINTAINER, config == null ? null : config.getMaintainer()))\n                .workdir(valueProvider.getString(WORKDIR, config == null ? null : config.getWorkdir()))\n                .skip(valueProvider.getBoolean(SKIP_BUILD, config == null ? null : config.getSkip()))\n                .skipPush(valueProvider.getBoolean(SKIP_PUSH, config == null ? null : config.getSkipPush()))\n                .imagePullPolicy(valueProvider.getString(IMAGE_PULL_POLICY_BUILD, config == null ? null : config.getImagePullPolicy()))\n                .contextDir(valueProvider.getString(CONTEXT_DIR, config == null ? null : config.getContextDirRaw()))\n                .dockerArchive(valueProvider.getString(DOCKER_ARCHIVE, config == null ? null : config.getDockerArchiveRaw()))\n                .loadNamePattern(valueProvider.getString(LOAD_NAME_PATTERN, config == null ? null : config.getLoadNamePattern()))\n                .dockerFile(valueProvider.getString(DOCKER_FILE, config == null ? null : config.getDockerFileRaw()))\n                .dockerFileDir(valueProvider.getString(DOCKER_FILE_DIR, config == null ? null : config.getDockerFileDirRaw()))\n                .buildOptions(valueProvider.getMap(BUILD_OPTIONS, config == null ? null : config.getBuildOptions()))\n                .filter(valueProvider.getString(FILTER, config == null ? null : config.getFilterRaw()))\n                .user(valueProvider.getString(USER, config == null ? null : config.getUser()))\n                .healthCheck(extractHealthCheck(config == null ? null : config.getHealthCheck(), valueProvider))\n                .build();\n    }\n\n    private RunImageConfiguration extractRunConfiguration(ImageConfiguration fromConfig, ValueProvider valueProvider) {\n        RunImageConfiguration config = fromConfig.getRunConfiguration();\n        if (config.isDefault()) {\n            config = null;\n        }\n\n        return new RunImageConfiguration.Builder()\n                .capAdd(valueProvider.getList(CAP_ADD, config == null ? null : config.getCapAdd()))\n                .capDrop(valueProvider.getList(CAP_DROP, config == null ? null : config.getCapDrop()))\n                .securityOpts(valueProvider.getList(SECURITY_OPTS, config == null ? null : config.getSecurityOpts()))\n                .cmd(extractArguments(valueProvider, CMD, config == null ? null : config.getCmd()))\n                .dns(valueProvider.getList(DNS, config == null ? null : config.getDns()))\n                .dependsOn(valueProvider.getList(DEPENDS_ON, config == null ? null : config.getDependsOn()))\n                .net(valueProvider.getString(NET, config == null ? null : config.getNetRaw()))\n                .network(extractNetworkConfig(config == null ? null : config.getNetworkingConfig(), valueProvider))\n                .dnsSearch(valueProvider.getList(DNS_SEARCH, config == null ? null : config.getDnsSearch()))\n                .domainname(valueProvider.getString(DOMAINNAME, config == null ? null : config.getDomainname()))\n                .entrypoint(extractArguments(valueProvider, ENTRYPOINT, config == null ? null : config.getEntrypoint()))\n                .env(CollectionUtils.mergeMaps(\n                        valueProvider.getMap(ENV_RUN, config == null ? null : config.getEnv()),\n                        valueProvider.getMap(ENV, Collections.<String, String>emptyMap())\n                ))\n                .labels(valueProvider.getMap(LABELS, config == null ? null : config.getLabels()))\n                .envPropertyFile(valueProvider.getString(ENV_PROPERTY_FILE, config == null ? null : config.getEnvPropertyFile()))\n                .extraHosts(valueProvider.getList(EXTRA_HOSTS, config == null ? null : config.getExtraHosts()))\n                .hostname(valueProvider.getString(HOSTNAME, config == null ? null : config.getHostname()))\n                .links(valueProvider.getList(LINKS, config == null ? null : config.getLinks()))\n                .memory(valueProvider.getLong(MEMORY, config == null ? null : config.getMemory()))\n                .memorySwap(valueProvider.getLong(MEMORY_SWAP, config == null ? null : config.getMemorySwap()))\n                .namingStrategy(valueProvider.getString(NAMING_STRATEGY, config == null || config.getNamingStrategy() == null ? null : config.getNamingStrategy().name()))\n                .exposedPropertyKey(valueProvider.getString(EXPOSED_PROPERTY_KEY, config == null ? null : config.getExposedPropertyKey()))\n                .portPropertyFile(valueProvider.getString(PORT_PROPERTY_FILE, config == null ? null : config.getPortPropertyFile()))\n                .ports(valueProvider.getList(PORTS, config == null ? null : config.getPorts()))\n                .shmSize(valueProvider.getLong(SHMSIZE, config == null ? null : config.getShmSize()))\n                .privileged(valueProvider.getBoolean(PRIVILEGED, config == null ? null : config.getPrivileged()))\n                .restartPolicy(extractRestartPolicy(config == null ? null : config.getRestartPolicy(), valueProvider))\n                .user(valueProvider.getString(USER, config == null ? null : config.getUser()))\n                .workingDir(valueProvider.getString(WORKING_DIR, config == null ? null : config.getWorkingDir()))\n                .log(extractLogConfig(config == null ? null : config.getLogConfiguration(), valueProvider))\n                .wait(extractWaitConfig(config == null ? null : config.getWaitConfiguration(), valueProvider))\n                .volumes(extractVolumeConfig(config == null ? null : config.getVolumeConfiguration(), valueProvider))\n                .skip(valueProvider.getBoolean(SKIP_RUN, config == null ? null : config.getSkip()))\n                .imagePullPolicy(valueProvider.getString(IMAGE_PULL_POLICY_RUN, config == null ? null : config.getImagePullPolicy()))\n                .ulimits(extractUlimits(config == null ? null : config.getUlimits(), valueProvider))\n                .tmpfs(valueProvider.getList(TMPFS, config == null ? null : config.getTmpfs()))\n                .cpuShares(valueProvider.getLong(CPUSHARES, config == null ? null : config.getCpuShares()))\n                .cpus(valueProvider.getLong(CPUS, config == null ? null : config.getCpus()))\n                .cpuSet(valueProvider.getString(CPUSET, config == null ? null : config.getCpuSet()))\n                .readOnly(valueProvider.getBoolean(READ_ONLY, config == null ? null : config.getReadOnly()))\n                .autoRemove(valueProvider.getBoolean(AUTO_REMOVE, config == null ? null : config.getAutoRemove()))\n                .build();\n    }\n\n    private NetworkConfig extractNetworkConfig(NetworkConfig config, ValueProvider valueProvider) {\n        return new NetworkConfig.Builder()\n            .mode(valueProvider.getString(NETWORK_MODE, config == null || config.getMode() == null ? null : config.getMode().name()))\n            .name(valueProvider.getString(NETWORK_NAME, config == null ? null : config.getName()))\n            .aliases(valueProvider.getList(NETWORK_ALIAS, config == null ? null : config.getAliases()))\n            .build();\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private AssemblyConfiguration extractAssembly(AssemblyConfiguration config, ValueProvider valueProvider) {\n        return new AssemblyConfiguration.Builder()\n                .targetDir(valueProvider.getString(ASSEMBLY_BASEDIR, config == null ? null : config.getTargetDir()))\n                .descriptor(valueProvider.getString(ASSEMBLY_DESCRIPTOR, config == null ? null : config.getDescriptor()))\n                .descriptorRef(valueProvider.getString(ASSEMBLY_DESCRIPTOR_REF, config == null ? null : config.getDescriptorRef()))\n                .dockerFileDir(valueProvider.getString(ASSEMBLY_DOCKER_FILE_DIR, config == null ? null : config.getDockerFileDir()))\n                .exportBasedir(valueProvider.getBoolean(ASSEMBLY_EXPORT_BASEDIR, config == null ? null : config.getExportTargetDir()))\n                .ignorePermissions(valueProvider.getBoolean(ASSEMBLY_IGNORE_PERMISSIONS, config == null ? null : config.getIgnorePermissions()))\n                .permissions(valueProvider.getString(ASSEMBLY_PERMISSIONS, config == null ? null : config.getPermissionsRaw()))\n                .user(valueProvider.getString(ASSEMBLY_USER, config == null ? null : config.getUser()))\n                .mode(valueProvider.getString(ASSEMBLY_MODE, config == null ? null : config.getModeRaw()))\n                .tarLongFileMode(valueProvider.getString(ASSEMBLY_TARLONGFILEMODE, config == null ? null : config.getTarLongFileMode()))\n                .build();\n    }\n\n    private HealthCheckConfiguration extractHealthCheck(HealthCheckConfiguration config, ValueProvider valueProvider) {\n        Map<String, String> healthCheckProperties = valueProvider.getMap(HEALTHCHECK, Collections.<String, String>emptyMap());\n        if (healthCheckProperties != null && healthCheckProperties.size() > 0) {\n            return new HealthCheckConfiguration.Builder()\n                    .interval(valueProvider.getString(HEALTHCHECK_INTERVAL, config == null ? null : config.getInterval()))\n                    .timeout(valueProvider.getString(HEALTHCHECK_TIMEOUT, config == null ? null : config.getTimeout()))\n                    .startPeriod(valueProvider.getString(HEALTHCHECK_START_PERIOD, config == null ? null : config.getStartPeriod()))\n                    .retries(valueProvider.getInteger(HEALTHCHECK_RETRIES, config == null ? null : config.getRetries()))\n                    .mode(valueProvider.getString(HEALTHCHECK_MODE, config == null || config.getMode() == null ? null : config.getMode().name()))\n                    .cmd(extractArguments(valueProvider, HEALTHCHECK_CMD, config == null ? null : config.getCmd()))\n                    .build();\n        } else {\n            return config;\n        }\n    }\n\n    // Extract only the values of the port mapping\n\n    private List<String> extractPortValues(List<String> config, ValueProvider valueProvider) {\n        List<String> ret = new ArrayList<>();\n        List<String> ports = valueProvider.getList(PORTS, config);\n        if (ports == null) {\n            return null;\n        }\n        List<String[]> parsedPorts = EnvUtil.splitOnLastColon(ports);\n        for (String[] port : parsedPorts) {\n            ret.add(port[1]);\n        }\n        return ret;\n    }\n\n    private Arguments extractArguments(ValueProvider valueProvider, ConfigKey configKey, Arguments alternative) {\n        return valueProvider.getObject(configKey, alternative, raw -> raw != null ? new Arguments(raw) : null);\n    }\n\n    private RestartPolicy extractRestartPolicy(RestartPolicy config, ValueProvider valueProvider) {\n        return new RestartPolicy.Builder()\n                .name(valueProvider.getString(RESTART_POLICY_NAME, config == null ? null : config.getName()))\n                .retry(valueProvider.getInt(RESTART_POLICY_RETRY, config == null || config.getRetry() == 0 ? null : config.getRetry()))\n                .build();\n    }\n\n    private LogConfiguration extractLogConfig(LogConfiguration config, ValueProvider valueProvider) {\n        LogConfiguration.Builder builder = new LogConfiguration.Builder()\n            .color(valueProvider.getString(LOG_COLOR, config == null ? null : config.getColor()))\n            .date(valueProvider.getString(LOG_DATE, config == null ? null : config.getDate()))\n            .file(valueProvider.getString(LOG_FILE, config == null ? null : config.getFileLocation()))\n            .prefix(valueProvider.getString(LOG_PREFIX, config == null ? null : config.getPrefix()))\n            .logDriverName(valueProvider.getString(LOG_DRIVER_NAME, config == null || config.getDriver() == null ? null : config.getDriver().getName()))\n            .logDriverOpts(valueProvider.getMap(LOG_DRIVER_OPTS, config == null || config.getDriver() == null ? null : config.getDriver().getOpts()));\n\n        Boolean configEnabled = config != null ? config.isEnabled() : null;\n        Boolean enabled = valueProvider.getBoolean(LOG_ENABLED, configEnabled);\n        builder.enabled(enabled);\n        return builder.build();\n    }\n\n    private WaitConfiguration extractWaitConfig(WaitConfiguration config, ValueProvider valueProvider) {\n        String url = valueProvider.getString(WAIT_HTTP_URL, config == null ? null : config.getUrl());\n        if (url == null) {\n            // Fallback to deprecated old URL\n            url = valueProvider.getString(WAIT_URL, config == null ? null : config.getUrl());\n        }\n        WaitConfiguration.ExecConfiguration exec = config == null ? null : config.getExec();\n        WaitConfiguration.TcpConfiguration tcp = config == null ? null : config.getTcp();\n        WaitConfiguration.HttpConfiguration http = config == null ? null : config.getHttp();\n\n        return new WaitConfiguration.Builder()\n                .time(valueProvider.getInt(WAIT_TIME, config == null ? null : config.getTime()))\n                .healthy(valueProvider.getBoolean(WAIT_HEALTHY, config == null ? null : config.getHealthy()))\n                .url(url)\n                .preStop(valueProvider.getString(WAIT_EXEC_PRE_STOP, exec == null ? null : exec.getPreStop()))\n                .postStart(valueProvider.getString(WAIT_EXEC_POST_START, exec == null ? null : exec.getPostStart()))\n                .breakOnError(valueProvider.getBoolean(WAIT_EXEC_BREAK_ON_ERROR, exec == null ? null : exec.isBreakOnError()))\n                .method(valueProvider.getString(WAIT_HTTP_METHOD, http == null ? null : http.getMethod()))\n                .status(valueProvider.getString(WAIT_HTTP_STATUS, http == null ? null : http.getStatus()))\n                .log(valueProvider.getString(WAIT_LOG, config == null ? null : config.getLog()))\n                .kill(valueProvider.getInteger(WAIT_KILL, config == null ? null : config.getKill()))\n                .exit(valueProvider.getInteger(WAIT_EXIT, config == null ? null : config.getExit()))\n                .shutdown(valueProvider.getInteger(WAIT_SHUTDOWN, config == null ? null : config.getShutdown()))\n                .tcpHost(valueProvider.getString(WAIT_TCP_HOST, tcp == null ? null : tcp.getHost()))\n                .tcpPorts(valueProvider.getIntList(WAIT_TCP_PORT, tcp == null ? null : tcp.getPorts()))\n                .tcpMode(valueProvider.getString(WAIT_TCP_MODE, tcp == null || tcp.getMode() == null ? null : tcp.getMode().name()))\n                .build();\n    }\n\n    private WatchImageConfiguration extractWatchConfig(ImageConfiguration fromConfig, ValueProvider valueProvider) {\n        WatchImageConfiguration config = fromConfig.getWatchConfiguration();\n\n        return new WatchImageConfiguration.Builder()\n                .interval(valueProvider.getInteger(WATCH_INTERVAL, config == null ? null : config.getIntervalRaw()))\n                .postGoal(valueProvider.getString(WATCH_POSTGOAL, config == null ? null : config.getPostGoal()))\n                .postExec(valueProvider.getString(WATCH_POSTEXEC, config == null ? null : config.getPostExec()))\n                .mode(valueProvider.getString(WATCH_POSTGOAL, config == null || config.getMode() == null ? null : config.getMode().name()))\n                .build();\n    }\n\n    private List<UlimitConfig> extractUlimits(List<UlimitConfig> config, ValueProvider valueProvider) {\n        List<String> other = null;\n        if (config != null) {\n            other = new ArrayList<>();\n            // Convert back to string for potential merge\n            for (UlimitConfig ulimitConfig : config) {\n                other.add(ulimitConfig.serialize());\n            }\n        }\n\n        List<String> ulimits = valueProvider.getList(ConfigKey.ULIMITS, other);\n        if (ulimits == null) {\n            return null;\n        }\n        List<UlimitConfig> ret = new ArrayList<>();\n        for (String ulimit : ulimits) {\n            ret.add(new UlimitConfig(ulimit));\n        }\n        return ret;\n    }\n\n    private RunVolumeConfiguration extractVolumeConfig(RunVolumeConfiguration config, ValueProvider valueProvider) {\n        return new RunVolumeConfiguration.Builder()\n                .bind(valueProvider.getList(BIND, config == null ? null : config.getBind()))\n                .from(valueProvider.getList(VOLUMES_FROM, config == null ? null : config.getFrom()))\n                .build();\n    }\n\n    private static String getPrefix(Map<String, String> externalConfig) {\n        String prefix = externalConfig.get(\"prefix\");\n        if (prefix == null) {\n            prefix = DEFAULT_PREFIX;\n        }\n        return prefix;\n    }\n\n    private static PropertyMode getMode(Map<String, String> externalConfig) {\n        return PropertyMode.parse(externalConfig.get(\"mode\"));\n    }\n\n    public static boolean canCoexistWithOtherPropertyConfiguredImages(Map<String, String> externalConfig) {\n        if(externalConfig == null || externalConfig.isEmpty()) {\n            return false;\n        }\n\n        if(!TYPE_NAME.equals(externalConfig.get(\"type\")))\n        {\n            // This images loads config from something totally different\n            return true;\n        }\n\n        if(externalConfig.get(\"prefix\") != null)\n        {\n            // This image has a specified prefix. If multiple images have explicitly set docker. as prefix we\n            // assume user know what they are doing and allow it.\n            return true;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyMode.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;\n\n/**\n * Identifies how the {@link PropertyConfigHandler} should treat properties vs configuration\n * from POM file in the {@link ValueProvider}.\n *\n * @author Johan Ström\n */\npublic enum PropertyMode {\n    Only,\n    Override,\n    Fallback,\n    Skip;\n\n    /**\n     * Given String, parse to a valid property mode.\n     *\n     * If null, the default Only is given.\n     *\n     * @param name null or a valid name\n     * @return PropertyMode\n     * @throws IllegalArgumentException if empty or invalid names\n     */\n    static PropertyMode parse(String name) {\n        if(name == null) {\n            return PropertyMode.Only;\n        }\n\n        name = name.toLowerCase();\n        for (PropertyMode e : PropertyMode.values()) {\n            if (e.name().toLowerCase().equals(name)) {\n                return e;\n            }\n        }\n        throw new IllegalArgumentException(\"PropertyMode: invalid mode \"+name+\". Valid: \"+values());\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/property/ValueCombinePolicy.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * Dictates how to combine values from different sources. See {@link PropertyConfigHandler} for details.\n */\npublic enum ValueCombinePolicy {\n    /**\n     * The prioritized value fully replaces any other values.\n     */\n    Replace,\n\n    /**\n     * All provided values are merged. This only makes sense for complex types such as lists and maps.\n     */\n    Merge;\n\n    public static ValueCombinePolicy fromString(String valueCombinePolicy) {\n        for (ValueCombinePolicy policy : values()) {\n            if (policy.name().equalsIgnoreCase(valueCombinePolicy)) {\n                return policy;\n            }\n        }\n        throw new IllegalArgumentException(String.format(\"No value combine policy %s known. Valid values are: %s\",\n                valueCombinePolicy, StringUtils.join(values(), \", \")));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/config/handler/property/ValueProvider.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;\n\nimport java.util.*;\n\nimport io.fabric8.maven.docker.util.EnvUtil;\n\nimport static io.fabric8.maven.docker.util.EnvUtil.*;\n\n/**\n * Helper to extract values from a set of Properties, potentially mixing it up with XML-based configuration based on the\n * {@link PropertyMode} setting.\n *\n * Obtaining a value is done via data-type specific methods (such as {@link #getString}). The ConfigKey parameter\n * tells us which property to look for, and how to handle combination of multiple values.\n *\n * For {@link PropertyMode#Only} we only look at the properties, ignoring any config value.\n * For {@link PropertyMode#Skip} we only look at the config, ignoring any properties value.\n * For {@link PropertyMode#Override} we use the property value if it is non-null, else the config value.\n * For {@link PropertyMode#Fallback} we use the config value if it is non-null, else the property value.\n *\n * For Override and Fallback mode, merging may take place as dictated by the {@link ValueCombinePolicy}\n * defined in the {@link ConfigKey}, or as overriden by the property &lt;prefix.someproperty&gt<b>._combine</b>\n * ({@link EnvUtil#PROPERTY_COMBINE_POLICY_SUFFIX}).\n *\n * If {@link ValueCombinePolicy#Replace} is used, only the prioritized value (first non-null) is used.\n * If {@link ValueCombinePolicy#Merge} is used, the merge method depends on the data type.\n * For simple types (string, int, long, boolean) this is not supported and will throw exception.\n * For Lists, the non-null values will be appended to each other (with values from first source added first)\n * For Maps, all maps are merged into one map, with data from the first map taking precedence. *\n *\n * @author Johan Ström\n */\npublic class ValueProvider {\n    private String prefix;\n    private Properties properties;\n    private PropertyMode propertyMode;\n\n    private StringListValueExtractor stringListValueExtractor;\n    private IntListValueExtractor intListValueExtractor;\n    private MapValueExtractor mapValueExtractor;\n    private StringValueExtractor stringValueExtractor;\n    private IntValueExtractor intValueExtractor;\n    private LongValueExtractor longValueExtractor;\n    private BooleanValueExtractor booleanValueExtractor;\n    private DoubleValueExtractor doubleValueExtractor;\n    \n    /**\n     * Initiates ValueProvider which is to work with data from the given properties.\n     *\n     * The PropertyMode controls which source(s) to consult, and in which order.\n     *\n     * @param prefix Only look at properties with this prefix.\n     * @param properties\n     * @param propertyMode Which source to prioritize\n     */\n    public ValueProvider(String prefix, Properties properties, PropertyMode propertyMode) {\n        this.prefix = prefix;\n        this.properties = properties;\n        this.propertyMode = propertyMode;\n\n        stringListValueExtractor = new StringListValueExtractor();\n        intListValueExtractor = new IntListValueExtractor();\n        mapValueExtractor = new MapValueExtractor();\n        stringValueExtractor = new StringValueExtractor();\n        intValueExtractor = new IntValueExtractor();\n        longValueExtractor = new LongValueExtractor();\n        booleanValueExtractor = new BooleanValueExtractor();\n        doubleValueExtractor = new DoubleValueExtractor();\n    }\n\n    public String getString(ConfigKey key, String fromConfig) {\n        return stringValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public Integer getInteger(ConfigKey key, Integer fromConfig) {\n        return intValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public int getInt(ConfigKey key, Integer fromConfig) {\n        Integer integer = getInteger(key, fromConfig);\n        if(integer == null) {\n            return 0;\n        }\n        return integer;\n    }\n\n    public Long getLong(ConfigKey key, Long fromConfig) {\n        return longValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public Boolean getBoolean(ConfigKey key, Boolean fromConfig) {\n        return booleanValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public List<String> getList(ConfigKey key, List<String> fromConfig) {\n        return stringListValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public List<Integer> getIntList(ConfigKey key, List<Integer> fromConfig) {\n        return intListValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public Map<String, String> getMap(ConfigKey key, Map<String, String> fromConfig) {\n        return mapValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public Double getDouble(ConfigKey key, Double fromConfig){\n        return doubleValueExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    public <T> T getObject(ConfigKey key, T fromConfig, final com.google.common.base.Function<String, T> converter) {\n        ValueExtractor<T> arbitraryExtractor = new ValueExtractor<T>() {\n            @Override\n            protected T withPrefix(String prefix, ConfigKey key, Properties properties) {\n                return converter.apply(properties.getProperty(key.asPropertyKey(prefix)));\n            }\n        };\n\n        return arbitraryExtractor.getFromPreferredSource(prefix, key, fromConfig);\n    }\n\n    /**\n     * Helper base class for picking values out of the the Properties class and/or config value.\n     *\n     * If there is only one source defined, we only use that. If multiple source are defined, the first one get's priority.\n     * If more than one value is specified, a merge policy as specified for the ConfigKey\n     */\n    private abstract class ValueExtractor<T> {\n        T getFromPreferredSource(String prefix, ConfigKey key, T fromConfig) {\n            if(propertyMode == PropertyMode.Skip) {\n                return fromConfig;\n            }\n\n            List<T> values = new ArrayList<>();\n\n            // Find all non-null values, put into \"values\" with order based on the given propertyMode\n            T fromProperty = withPrefix(prefix, key, properties);\n\n            // Short-circuit\n            if(fromProperty == null && fromConfig == null) {\n                return null;\n            }\n\n            switch (propertyMode) {\n                case Only:\n                    return fromProperty;\n                case Override:\n                    if(fromProperty != null) {\n                        values.add(fromProperty);\n                    }\n                    if(fromConfig != null) {\n                        values.add(fromConfig);\n                    }\n                    break;\n                case Fallback:\n                    if(fromConfig != null) {\n                        values.add(fromConfig);\n                    }\n                    if(fromProperty != null) {\n                        values.add(fromProperty);\n                    }\n                    break;\n                default:\n                    throw new AssertionError(\"Invalid PropertyMode\");\n            }\n\n            if(values.size() == 1) {\n                return values.get(0);\n            }\n\n            // values now has non-null values from both sources, in preference order.\n            // Let's merge according to the combine policy\n            ValueCombinePolicy combinePolicy = key.getValueCombinePolicy();\n            String overrideCombinePolicy = properties.getProperty(key.asPropertyKey(prefix) + \".\" + EnvUtil.PROPERTY_COMBINE_POLICY_SUFFIX);\n            if(overrideCombinePolicy != null) {\n                combinePolicy = ValueCombinePolicy.fromString(overrideCombinePolicy);\n            }\n\n            switch(combinePolicy) {\n                case Replace:\n                    return values.get(0);\n                case Merge:\n                    return merge(key, values);\n            }\n            return null;\n        }\n\n        /**\n         * Data type-specific extractor to read value from properties.\n         *\n         * @param prefix\n         * @param key\n         * @param properties\n         * @return\n         */\n        protected abstract T withPrefix(String prefix, ConfigKey key, Properties properties);\n\n        protected T merge(ConfigKey key, List<T> values) {\n            throw new IllegalArgumentException(\"Combine policy Merge is not available for \"+key.asPropertyKey(prefix));\n        }\n    }\n\n\n\n\n    private class StringValueExtractor extends ValueExtractor<String> {\n        @Override\n        protected String withPrefix(String prefix, ConfigKey key, Properties properties) {\n            return properties.getProperty(key.asPropertyKey(prefix));\n        }\n    }\n\n    private class IntValueExtractor extends ValueExtractor<Integer> {\n        @Override\n        protected Integer withPrefix(String prefix, ConfigKey key, Properties properties) {\n            String prop = properties.getProperty(key.asPropertyKey(prefix));\n            return prop == null ? null : Integer.valueOf(prop);\n        }\n    }\n\n\n    private class LongValueExtractor extends ValueExtractor<Long> {\n        @Override\n        protected Long withPrefix(String prefix, ConfigKey key, Properties properties) {\n            String prop = properties.getProperty(key.asPropertyKey(prefix));\n            return prop == null ? null : Long.valueOf(prop);\n        }\n    }\n\n    private class BooleanValueExtractor extends ValueExtractor<Boolean> {\n        @Override\n        protected Boolean withPrefix(String prefix, ConfigKey key, Properties properties) {\n            String prop = properties.getProperty(key.asPropertyKey(prefix));\n            return prop == null ? null : Boolean.valueOf(prop);\n        }\n    }\n\n    private class DoubleValueExtractor extends ValueExtractor<Double> {\n        @Override\n        protected Double withPrefix(String prefix, ConfigKey key, Properties properties) {\n            String prop = properties.getProperty(key.asPropertyKey(prefix));\n            return prop == null ? null : Double.valueOf(prop);\n        }\n    }\n\n    private abstract class ListValueExtractor<T> extends ValueExtractor<List<T>> {\n        @Override\n        protected List<T> withPrefix(String prefix, ConfigKey key, Properties properties) {\n            List<String> strings = extractFromPropertiesAsList(key.asPropertyKey(prefix), properties);\n            if(strings == null) {\n                return null;\n            }\n            return process(strings);\n        }\n\n        protected abstract List<T> process(List<String> strings);\n\n        @Override\n        protected List<T> merge(ConfigKey key, List<List<T>> values) {\n            List<T> merged = new ArrayList<>();\n            for (List<T> value : values) {\n                merged.addAll(value);\n            }\n            return merged;\n        }\n    }\n\n    private class StringListValueExtractor extends ListValueExtractor<String> {\n        @Override\n        protected List<String> process(List<String> strings) {\n            return strings;\n        }\n    }\n\n    private class IntListValueExtractor extends ListValueExtractor<Integer> {\n        @Override\n        protected List<Integer> process(List<String> strings) {\n            List<Integer> ints = new ArrayList<>();\n            for (String s : strings) {\n                ints.add(s != null ? Integer.parseInt(s) : 0);\n            }\n            return ints;\n        }\n    }\n\n\n\n    private class MapValueExtractor extends ValueExtractor<Map<String, String>> {\n        @Override\n        protected Map<String, String> withPrefix(String prefix, ConfigKey key, Properties properties) {\n            return extractFromPropertiesAsMap(key.asPropertyKey(prefix), properties);\n        }\n\n        @Override\n        protected Map<String, String> merge(ConfigKey key, List<Map<String, String>> values) {\n            Map<String, String> merged = null;\n\n            // Iterate in reverse, the first entry in values has highest priority\n            for(int i = values.size() - 1; i >= 0; i--) {\n                Map<String, String> value = values.get(i);\n                if(merged == null) {\n                    merged = value;\n                } else {\n                    merged.putAll(value);\n                }\n            }\n            return merged;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/DefaultLogCallback.java",
    "content": "package io.fabric8.maven.docker.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.*;\nimport java.time.ZonedDateTime;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.google.common.io.Files;\nimport io.fabric8.maven.docker.access.log.LogCallback;\n\n/**\n * @author roland\n * @since 26/09/15\n */\npublic class DefaultLogCallback implements LogCallback {\n\n    private static Map<String, SharedPrintStream> printStreamMap = new HashMap<>();\n\n    private final LogOutputSpec outputSpec;\n    private SharedPrintStream sps;\n\n    public DefaultLogCallback(LogOutputSpec outputSpec) {\n        this.outputSpec = outputSpec;\n    }\n\n    @Override\n    public synchronized void open() throws IOException {\n        if (this.sps == null) {\n            String file = outputSpec.getFile();\n            if (outputSpec.isLogStdout() || file == null) {\n                this.sps = new SharedPrintStream(System.out);\n            } else {\n                SharedPrintStream cachedPs = printStreamMap.get(file);\n                if (cachedPs == null) {\n                    Files.createParentDirs(new File(file));\n                    PrintStream ps = new PrintStream(new FileOutputStream(file), true);\n                    cachedPs = new SharedPrintStream(ps);\n                    printStreamMap.put(file, cachedPs);\n                } else {\n                    cachedPs.allocate();\n                }\n                this.sps = cachedPs;\n            }\n        }\n    }\n\n    @Override\n    public synchronized void close() {\n        if (this.sps != null) {\n            if (sps.close()) {\n                String file = outputSpec.getFile();\n                if (file != null) {\n                    printStreamMap.remove(file);\n                }\n                this.sps = null;\n            }\n        }\n    }\n\n    private PrintStream ps() {\n        return sps.getPrintStream();\n    }\n\n    @Override\n    public void log(int type, ZonedDateTime timestamp, String txt) {\n        addLogEntry(ps(), new LogEntry(type, timestamp, txt));\n    }\n\n    @Override\n    public void error(String error) {\n        ps().println(error);\n    }\n\n    private void addLogEntry(PrintStream ps, LogEntry logEntry) {\n        // TODO: Add the entry to a queue, and let the queue be picked up with a small delay from an extra\n        // thread which then can sort the entries by time before printing it out in order to avoid race conditions.\n\n        LogOutputSpec spec = outputSpec;\n        if (spec == null) {\n            spec = LogOutputSpec.DEFAULT;\n        }\n        String text = logEntry.getText();\n        ps.println(spec.getPrompt(spec.isUseColor(),logEntry.getTimestamp()) + text);\n    }\n\n        // A single log-entry\n    private static class LogEntry implements Comparable<LogEntry> {\n        private final int type;\n        private final ZonedDateTime timestamp;\n        private final String text;\n\n        public LogEntry(int type, ZonedDateTime timestamp, String text) {\n            this.type = type;\n            this.timestamp = timestamp;\n            this.text = text;\n        }\n\n        public int getType() {\n            return type;\n        }\n\n        public ZonedDateTime getTimestamp() {\n            return timestamp;\n        }\n\n        public String getText() {\n            return text;\n        }\n\n        @Override\n        public int compareTo(LogEntry entry) {\n            return timestamp.compareTo(entry.timestamp);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/LogDispatcher.java",
    "content": "package io.fabric8.maven.docker.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.log.LogGetHandle;\n\n/**\n * @author roland\n * @since 25/11/14\n */\npublic class LogDispatcher {\n\n    private Map<String,LogGetHandle> logHandles;\n\n    private DockerAccess dockerAccess;\n\n    public LogDispatcher(DockerAccess dockerAccess) {\n        this.dockerAccess = dockerAccess;\n        logHandles = new HashMap<>();\n    }\n\n    public synchronized void trackContainerLog(String containerId, LogOutputSpec spec)  {\n        LogGetHandle handle = dockerAccess.getLogAsync(containerId, new DefaultLogCallback(spec));\n        logHandles.put(containerId, handle);\n    }\n\n    public synchronized void fetchContainerLog(String containerId, LogOutputSpec spec) {\n        dockerAccess.getLogSync(containerId, new DefaultLogCallback(spec));\n    }\n\n    public synchronized void untrackAllContainerLogs() {\n        for (String key : logHandles.keySet()) {\n            LogGetHandle handle = logHandles.get(key);\n            handle.finish();\n        }\n        logHandles.clear();\n    }\n\n    // =======================================================================================\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/LogOutput.java",
    "content": "package io.fabric8.maven.docker.log;/*\n * \n * Copyright 2014 Roland Huss\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\n/**\n * @author roland\n * @since 25/11/14\n */\npublic interface LogOutput {\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/LogOutputSpec.java",
    "content": "package io.fabric8.maven.docker.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport org.fusesource.jansi.Ansi;\n\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\n\nimport static org.fusesource.jansi.Ansi.Color.*;\nimport static org.fusesource.jansi.Ansi.ansi;\n\n/**\n * @author roland\n * @since 25/11/14\n */\npublic class LogOutputSpec {\n\n    public static final LogOutputSpec DEFAULT = new LogOutputSpec(\"\", YELLOW, false , null, null, true, true);\n\n    private final boolean useColor;\n    private final boolean logStdout;\n    private final boolean fgBright;\n    private String prefix;\n    private Ansi.Color color;\n    private DateTimeFormatter timeFormatter;\n    private String file;\n\n    // Palette used for prefixing the log output\n    private final static Ansi.Color COLOR_PALETTE[] = {\n            YELLOW,CYAN,MAGENTA,GREEN,RED,BLUE\n    };\n    private static int globalColorIdx = 0;\n\n    private LogOutputSpec(String prefix, Ansi.Color color, boolean fgBright, DateTimeFormatter timeFormatter, String file, boolean useColor, boolean logStdout) {\n        this.prefix = prefix;\n        this.color = color;\n        this.fgBright = fgBright;\n        this.timeFormatter = timeFormatter;\n        this.file = file;\n        this.useColor = useColor;\n        this.logStdout = logStdout;\n    }\n\n    public boolean isUseColor() {\n        return useColor && (file == null || logStdout);\n    }\n\n    public boolean isLogStdout() {\n        return logStdout;\n    }\n\n    public String getPrompt(boolean withColor, ZonedDateTime timestamp) {\n        return formatTimestamp(timestamp,withColor) + formatPrefix(prefix, withColor);\n    }\n\n    public String getFile(){\n        return file;\n    }\n\n    private String formatTimestamp(ZonedDateTime timestamp, boolean withColor) {\n        if (timeFormatter == null) {\n            return \"\";\n        }\n        String date = timeFormatter.format(timestamp);\n        return (withColor ?\n                ansi().fgBright(BLACK).a(date).reset().toString() :\n                date) + \" \";\n    }\n\n    private String formatPrefix(String prefix,boolean withColor) {\n        if (withColor) {\n            Ansi ansi = ansi();\n            if (fgBright) {\n                ansi.fgBright(color);\n            } else {\n                ansi.fg(color);\n            }\n            return ansi.a(prefix).reset().toString();\n        } else {\n            return prefix;\n        }\n    }\n\n    public static class Builder {\n        private String prefix;\n        private Ansi.Color color;\n        private DateTimeFormatter timeFormatter;\n        private String file;\n        private boolean useColor;\n        private boolean logStdout;\n        private boolean fgBright;\n\n        public Builder prefix(String prefix) {\n            this.prefix = prefix;\n            return this;\n        }\n\n        public Builder color(String color) {\n            return color(color, false);\n        }\n\n        public Builder color(String color, boolean fgBright) {\n            if (color == null) {\n                this.color = COLOR_PALETTE[globalColorIdx++ % COLOR_PALETTE.length];\n            } else {\n                try {\n                    this.color = Ansi.Color.valueOf(color.toUpperCase());\n                    this.fgBright = fgBright;\n                } catch (IllegalArgumentException exp) {\n                    throw new IllegalArgumentException(\n                            \"Invalid color '\" + color +\n                            \"'. Color must be one of YELLOW, CYAN, MAGENTA, GREEN, RED, BLUE or BLACK\");\n                }\n            }\n            return this;\n        }\n\n        public Builder file(String file){\n            this.file = file;\n            return this;\n        }\n\n        public Builder timeFormatter(String formatOrConstant) {\n            if (formatOrConstant == null || formatOrConstant.equalsIgnoreCase(\"NONE\")\n                || formatOrConstant.equalsIgnoreCase(\"FALSE\")) {\n                timeFormatter = null;\n            } else if (formatOrConstant.length() == 0 || formatOrConstant.equalsIgnoreCase(\"DEFAULT\")) {\n                timeFormatter = DateTimeFormatter.ofPattern(\"HH:mm:ss.SSS\");\n            } else if (formatOrConstant.equalsIgnoreCase(\"ISO8601\")) {\n                timeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;\n            } else if (formatOrConstant.equalsIgnoreCase(\"SHORT\")) {\n                timeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);\n            } else if (formatOrConstant.equalsIgnoreCase(\"MEDIUM\")) {\n                timeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);\n            } else if (formatOrConstant.equalsIgnoreCase(\"LONG\")) {\n                timeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);\n            } else {\n                try {\n                    timeFormatter = DateTimeFormatter.ofPattern(formatOrConstant);\n                } catch (IllegalArgumentException exp) {\n                    throw new IllegalArgumentException(\n                            \"Cannot parse log date specification '\" + formatOrConstant + \"'.\" +\n                            \"Must be either DEFAULT, NONE, ISO8601, SHORT, MEDIUM, LONG or a \" +\n                            \"format string parseable by DateTimeFormatter. See \" +\n                            \"https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html\");\n                }\n            }\n            return this;\n        }\n\n        public Builder useColor(boolean useColor) {\n            this.useColor = useColor;\n            return this;\n        }\n\n        public Builder logStdout(boolean logStdout) {\n            this.logStdout = logStdout;\n            return this;\n        }\n\n        public LogOutputSpec build() {\n            return new LogOutputSpec(prefix, color, fgBright, timeFormatter, file, useColor, logStdout);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/LogOutputSpecFactory.java",
    "content": "package io.fabric8.maven.docker.log;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.util.FormatParameterReplacer;\n\n/**\n * @author roland\n * @since 26/09/15\n */\npublic class LogOutputSpecFactory {\n    private static final String DEFAULT_PREFIX_FORMAT = \"%a> \";\n    private boolean useColor;\n    private boolean logStdout;\n    private String logDate;\n\n    public LogOutputSpecFactory(boolean useColor, boolean logStdout, String logDate) {\n        this.useColor = useColor;\n        this.logStdout = logStdout;\n        this.logDate = logDate;\n    }\n\n    // ================================================================================================\n\n    public LogOutputSpec createSpec(String containerId, ImageConfiguration imageConfiguration) {\n        LogOutputSpec.Builder builder = new LogOutputSpec.Builder();\n        LogConfiguration logConfig = extractLogConfiguration(imageConfiguration);\n\n        addLogFormat(builder, logConfig);\n        addPrefix(builder, logConfig.getPrefix(), imageConfiguration, containerId);\n        builder.file(logConfig.getFileLocation())\n               .useColor(useColor)\n               .logStdout(logStdout)\n               .color(logConfig.getColor());\n\n        return builder.build();\n    }\n\n    private void addPrefix(LogOutputSpec.Builder builder, String logPrefix, ImageConfiguration imageConfig, String containerId) {\n        String prefixFormat = logPrefix;\n        if (prefixFormat == null) {\n            prefixFormat = DEFAULT_PREFIX_FORMAT;\n        }\n        FormatParameterReplacer formatParameterReplacer = new FormatParameterReplacer(getPrefixFormatParameterLookups(imageConfig, containerId));\n        builder.prefix(formatParameterReplacer.replace(prefixFormat));\n    }\n\n    private Map<String, FormatParameterReplacer.Lookup> getPrefixFormatParameterLookups(final ImageConfiguration imageConfig, final String containerId) {\n        Map<String, FormatParameterReplacer.Lookup> ret = new HashMap<>();\n\n        ret.put(\"z\", () -> \"\");\n        ret.put(\"c\", () -> containerId.substring(0, 6));\n        ret.put(\"C\", () -> containerId);\n        ret.put(\"a\", () -> {\n            String alias = imageConfig.getAlias();\n            if (alias != null) {\n                return alias;\n            }\n            return containerId.substring(0, 6);\n        });\n        ret.put(\"n\", imageConfig::getName);\n\n        return ret;\n    }\n\n    private void addLogFormat(LogOutputSpec.Builder builder, LogConfiguration logConfig) {\n        String logFormat = logConfig.getDate() != null ? logConfig.getDate() : logDate;\n        if (logFormat != null && logFormat.equalsIgnoreCase(\"true\")) {\n            logFormat = \"DEFAULT\";\n        }\n        if (logFormat != null) {\n            builder.timeFormatter(logFormat);\n        }\n    }\n\n    private LogConfiguration extractLogConfiguration(ImageConfiguration imageConfiguration) {\n        RunImageConfiguration runConfig = imageConfiguration.getRunConfiguration();\n        LogConfiguration logConfig = null;\n        if (runConfig != null) {\n            logConfig = runConfig.getLogConfiguration();\n        }\n        if (logConfig == null) {\n            logConfig = LogConfiguration.DEFAULT;\n        }\n        return logConfig;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/log/SharedPrintStream.java",
    "content": "package io.fabric8.maven.docker.log;\n\nimport java.io.PrintStream;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nclass SharedPrintStream {\n    private PrintStream printStream;\n\n    private AtomicInteger numUsers;\n\n    SharedPrintStream(PrintStream ps) {\n        this.printStream = ps;\n        this.numUsers = new AtomicInteger(1);\n    }\n\n    PrintStream getPrintStream() {\n        return printStream;\n    }\n\n    void allocate() {\n        numUsers.incrementAndGet();\n    }\n\n    boolean close() {\n        int nrUsers = numUsers.decrementAndGet();\n        if (nrUsers == 0 && printStream != System.out) {\n            printStream.close();\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/Container.java",
    "content": "package io.fabric8.maven.docker.model;\n/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.Map;\n\n/**\n * Interface representing a container\n *\n * @author roland\n * @since 16/07/15\n */\npublic interface Container {\n\n    long getCreated();\n\n    String getId();\n\n    String getImage();\n\n    Map<String, String> getLabels();\n\n    String getName();\n\n    String getNetworkMode();\n\n    Map<String, PortBinding> getPortBindings();\n\n    boolean isRunning();\n\n    /**\n     * IP Adress of the container if provided\n     *\n     * @return the IP address of the container or <code>null</code> if not provided.\n     */\n    String getIPAddress();\n\n    /**\n     * Return IP Addresses of custom networks, mapped to the network name as the key.\n     * @return The mapping of network names to IP addresses, or null it none provided.\n     */\n    Map<String, String> getCustomNetworkIpAddresses();\n\n    /**\n     * Exit code of the container if it already has excited\n     *\n     * @return exit code if the container has excited, <code>null</code> if it is still running. Also null,\n     * if the implementation doesn't support an exit code.\n     */\n    Integer getExitCode();\n\n    class PortBinding {\n        private final String hostIp;\n        private final Integer hostPort;\n\n        public PortBinding(Integer hostPort, String hostIp) {\n            this.hostPort = hostPort;\n            this.hostIp = hostIp;\n        }\n\n        public String getHostIp() {\n            return hostIp;\n        }\n\n        public Integer getHostPort() {\n            return hostPort;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ContainerDetails.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.google.common.base.Joiner;\nimport com.google.gson.JsonObject;\n\n\npublic class ContainerDetails implements Container {\n\n    static final String CONFIG = \"Config\";\n    static final String CREATED = \"Created\";\n    static final String HOST_IP = \"HostIp\";\n    static final String HOST_PORT = \"HostPort\";\n    static final String ID = \"Id\";\n    static final String IMAGE = \"Image\";\n    static final String LABELS = \"Labels\";\n    static final String NAME = \"Name\";\n    static final String IP = \"IPAddress\";\n    static final String HOST_CONFIG = \"HostConfig\";\n    static final String NETWORK_MODE = \"NetworkMode\";\n    static final String NETWORK_SETTINGS = \"NetworkSettings\";\n    static final String NETWORKS = \"Networks\";\n    static final String PORTS = \"Ports\";\n    static final String SLASH = \"/\";\n    static final String STATE = \"State\";\n    static final String HEALTH = \"Health\";\n    static final String STATUS = \"Status\";\n    static final String HEALTH_STATUS_HEALTHY = \"healthy\";\n    static final String HEALTHCHECK = \"Healthcheck\";\n    static final String TEST = \"Test\";\n\n    private static final String EXIT_CODE = \"ExitCode\";\n    private static final String RUNNING = \"Running\";\n\n    private final JsonObject json;\n\n    public ContainerDetails(JsonObject json) {\n        this.json = json;\n    }\n\n    @Override\n    public long getCreated() {\n        String date = json.get(CREATED).getAsString();\n        Instant instant = Instant.parse(date);\n        return instant.toEpochMilli();\n    }\n\n    @Override\n    public String getId() {\n        // only need first 12 to id a container\n        return json.get(ID).getAsString().substring(0, 12);\n    }\n\n    @Override\n    public String getImage() {\n        // ID: json.getString(\"Image\");\n        return json.getAsJsonObject(CONFIG).get(IMAGE).getAsString();\n    }\n\n    @Override\n    public Map<String, String> getLabels() {\n        JsonObject config = json.getAsJsonObject(CONFIG);\n        return config.has(LABELS) ?\n                mapLabels(config.getAsJsonObject(LABELS)) :\n                Collections.<String, String>emptyMap();\n    }\n\n    @Override\n    public String getName() {\n        String name = json.get(NAME).getAsString();\n\n        if (name.startsWith(SLASH)) {\n            name = name.substring(1);\n        }\n        return name;\n    }\n\n    @Override\n    public String getIPAddress() {\n        if (json.has(NETWORK_SETTINGS) && !json.get(NETWORK_SETTINGS).isJsonNull()) {\n            JsonObject networkSettings = json.getAsJsonObject(NETWORK_SETTINGS);\n            if (!networkSettings.get(IP).isJsonNull()) {\n                return networkSettings.get(IP).getAsString();\n            }\n        }\n        return null;\n    }\n\n\t\t@Override\n\t\tpublic String getNetworkMode() {\n\t\t\tif (json.has(HOST_CONFIG) && !json.get(HOST_CONFIG).isJsonNull()) {\n\t\t\t\tfinal JsonObject hostConfig = json.getAsJsonObject(HOST_CONFIG);\n\t\t\t\tif (!hostConfig.get(NETWORK_MODE).isJsonNull()) {\n          return hostConfig.get(NETWORK_MODE).getAsString();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n    @Override\n    public Map<String, String> getCustomNetworkIpAddresses() {\n        if (json.has(NETWORK_SETTINGS) && !json.get(NETWORK_SETTINGS).isJsonNull()) {\n            JsonObject networkSettings = json.getAsJsonObject(NETWORK_SETTINGS);\n            if (networkSettings.has(NETWORKS) && !networkSettings.get(NETWORKS).isJsonNull()) {\n                return extractNetworks(networkSettings);\n            }\n        }\n        return null;\n    }\n\n    private Map<String, String> extractNetworks(JsonObject networkSettings) {\n        JsonObject networks = networkSettings.getAsJsonObject(NETWORKS);\n        Set<String> keys = networks.keySet();\n        if (keys == null || keys.isEmpty()) {\n            return null;\n        }\n        Map<String, String> results = new HashMap<>();\n        for (String key : keys) {\n            JsonObject net = networks.getAsJsonObject(key);\n            if (net.has(IP) && !net.get(IP).isJsonNull()) {\n                results.put(key, net.get(IP).getAsString());\n            }\n        }\n\n        return results;\n    }\n\n    @Override\n    public Map<String, PortBinding> getPortBindings() {\n        if (json.has(NETWORK_SETTINGS) && !json.get(NETWORK_SETTINGS).isJsonNull()) {\n            JsonObject networkSettings = json.getAsJsonObject(NETWORK_SETTINGS);\n            if (networkSettings.has(PORTS) && !networkSettings.get(PORTS).isJsonNull()) {\n                return createPortBindings(networkSettings.getAsJsonObject(PORTS));\n            }\n        }\n\n        return new HashMap<>();\n    }\n\n    @Override\n    public boolean isRunning() {\n        JsonObject state = json.getAsJsonObject(STATE);\n        return state.get(RUNNING).getAsBoolean();\n    }\n\n    @Override\n    public Integer getExitCode() {\n        if (isRunning()) {\n            return null;\n        }\n        JsonObject state = json.getAsJsonObject(STATE);\n        return state.get(EXIT_CODE).getAsInt();\n    }\n\n    public boolean isHealthy() {\n        final JsonObject state = json.getAsJsonObject(STATE);\n        // always indicate healthy for docker hosts that do not support health checks.\n        return !state.has(HEALTH) || HEALTH_STATUS_HEALTHY.equals(state.getAsJsonObject(HEALTH).get(STATUS).getAsString());\n    }\n\n    public String getHealthcheck() {\n        if (!json.getAsJsonObject(CONFIG).has(HEALTHCHECK) ||\n            !json.getAsJsonObject(CONFIG).getAsJsonObject(HEALTHCHECK).has(TEST)) {\n            return null;\n        }\n\n        return Joiner.on(\", \").join(json.getAsJsonObject(CONFIG).getAsJsonObject(HEALTHCHECK).getAsJsonArray(TEST));\n    }\n\n    private void addPortMapping(String port, JsonObject hostConfig, Map<String, PortBinding> portBindings) {\n        String hostIp = hostConfig.get(HOST_IP).getAsString();\n        Integer hostPort = Integer.valueOf(hostConfig.get(HOST_PORT).getAsInt());\n\n        addPortMapping(port, new PortBinding(hostPort, hostIp), portBindings);\n    }\n\n    private void addPortMapping(String port, PortBinding binding, Map<String, PortBinding> portBindings) {\n        if (port.indexOf('/') == -1) {\n            port = port + \"/tcp\";\n        }\n\n        portBindings.put(port, binding);\n    }\n\n    private Map<String, PortBinding> createPortBindings(JsonObject ports) {\n        Map<String, PortBinding> portBindings = new HashMap<>();\n\n        for (Object obj : ports.keySet()) {\n            String port = obj.toString();\n            if (ports.get(port).isJsonNull()) {\n                addPortMapping(port, (PortBinding) null, portBindings);\n            } else {\n                // use the first entry in the array\n                JsonObject hostConfig = ports.getAsJsonArray(port).get(0).getAsJsonObject();\n                addPortMapping(port, hostConfig, portBindings);\n            }\n        }\n\n        return portBindings;\n    }\n\n    private Map<String, String> mapLabels(JsonObject labels) {\n        int length = labels.size();\n        Map<String, String> mapped = new HashMap<>(length);\n\n        Iterator<String> iterator = labels.keySet().iterator();\n        while (iterator.hasNext()) {\n            String key = iterator.next();\n            mapped.put(key, labels.get(key).getAsString());\n        }\n\n        return mapped;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ContainersListElement.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\npublic class ContainersListElement implements Container {\n\n    static final String CREATED = \"Created\";\n    public static final String ID = \"Id\";\n    public static final String IMAGE = \"Image\";\n    static final String IP = \"IP\";\n    static final String LABELS = \"Labels\";\n    static final String PORTS = \"Ports\";\n    static final String PUBLIC_PORT = \"PublicPort\";\n    static final String STATUS = \"Status\";\n    static final String TYPE = \"Type\";\n\n    private static final String NAMES = \"Names\";\n    private static final String PRIVATE_PORT = \"PrivatePort\";\n    private static final String SLASH = \"/\";\n    private static final String UP = \"up\";\n\n    private final JsonObject json;\n\n    public ContainersListElement(JsonObject json) {\n        this.json = json;\n    }\n\n    @Override\n    public long getCreated() {\n        return json.get(CREATED).getAsLong();\n    }\n\n    @Override\n    public String getId() {\n        // only need first 12 to id a container\n        return json.get(ID).getAsString().substring(0, 12);\n    }\n\n    @Override\n    public String getImage() {\n        return json.get(IMAGE).getAsString();\n    }\n\n    @Override\n    public Map<String, String> getLabels() {\n       if (!json.has(LABELS) || json.get(LABELS).isJsonNull()) {\n           return Collections.emptyMap();\n       }\n\n        return mapLabels(json.getAsJsonObject(LABELS));\n    }\n\n    @Override\n    public String getName() {\n        if (json.has(NAMES)) {\n            JsonArray names = json.getAsJsonArray(NAMES);\n            for (int i = 0; i < names.size(); i++) {\n                String name = names.get(i).getAsString();\n                if (name.startsWith(SLASH)) {\n                    name = name.substring(1);\n                }\n                if (!name.contains(SLASH)) {\n                    return name;\n                }\n            }\n            // this should never happen\n            throw new IllegalStateException(\"Unable to determine container name from 'Names' \" + names);\n        } else {\n            throw new UnsupportedOperationException(\"Missing 'Names' attribute from a container list element \" + json);\n        }\n    }\n\n    @Override\n    public String getNetworkMode() {\n      // HostConfig.NetworkMode is not provided by container list action.\n      return null;\n    }\n\n    @Override\n    public Map<String, PortBinding> getPortBindings() {\n        if (json.get(PORTS).isJsonNull()) {\n            return Collections.emptyMap();\n        }\n\n        return mapPortBindings(json.getAsJsonArray(PORTS));\n    }\n\n    @Override\n    public String getIPAddress() {\n        // IP address is not provided by container list action.\n        return null;\n    }\n\n    @Override\n    public Map<String, String> getCustomNetworkIpAddresses() {\n        // IP address is not provided by container list action.\n        return null;\n    }\n\n    @Override\n    public boolean isRunning() {\n        String status = json.get(STATUS).getAsString();\n        return status.toLowerCase().contains(UP);\n    }\n\n    @Override\n    public Integer getExitCode() {\n        // exit code is not provided by container list action.\n        return null;\n    }\n\n    private PortBinding createPortBinding(JsonObject object) {\n        PortBinding binding = null;\n\n        if (object.has(PUBLIC_PORT) && object.has(IP)) {\n            binding = new PortBinding(object.get(PUBLIC_PORT).getAsInt(), object.get(IP).getAsString());\n        }\n\n        return binding;\n    }\n\n    private String createPortKey(JsonObject object) {\n        return String.format(\"%s/%s\", object.get(PRIVATE_PORT).getAsInt(), object.get(TYPE).getAsString());\n    }\n\n    private Map<String, String> mapLabels(JsonObject labels) {\n        int length = labels.size();\n        Map<String, String> mapped = new HashMap<>(length);\n\n        Iterator<String> iterator = labels.keySet().iterator();\n        while (iterator.hasNext()) {\n            String key = iterator.next();\n            mapped.put(key, labels.get(key).getAsString());\n        }\n\n        return mapped;\n    }\n\n    private Map<String, PortBinding> mapPortBindings(JsonArray ports) {\n        int length = ports.size();\n        Map<String, PortBinding> portBindings = new HashMap<>(length);\n\n        for (int i = 0; i < length; i++) {\n            JsonObject object = ports.get(i).getAsJsonObject();\n            portBindings.put(createPortKey(object), createPortBinding(object));\n        }\n\n        return portBindings;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ExecDetails.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\n/**\n * Model class holding details of the result of an exec command on a running container.\n */\npublic class ExecDetails {\n    private static final String EXIT_CODE = \"ExitCode\";\n    private static final String RUNNING = \"Running\";\n    private static final String ENTRY_POINT = \"entrypoint\";\n    private static final String ARGUMENTS = \"arguments\";\n\n    private static final String PROCESS_CONFIG = \"ProcessConfig\";\n\n    private final JsonObject json;\n\n    public ExecDetails(JsonObject json) {\n        this.json = json;\n    }\n\n    public boolean isRunning() {\n        return json.get(RUNNING).getAsBoolean();\n    }\n\n    public Integer getExitCode() {\n        if (isRunning()) {\n            return null;\n        }\n        return json.get(EXIT_CODE).getAsInt();\n    }\n\n    public String getEntryPoint() {\n        if (!json.has(PROCESS_CONFIG)) {\n            return null;\n        }\n\n        JsonObject processConfig = json.getAsJsonObject(PROCESS_CONFIG);\n        if (!processConfig.has(ENTRY_POINT)) {\n            return null;\n        }\n\n        return processConfig.get(ENTRY_POINT).getAsString();\n    }\n\n    public String[] getArguments() {\n        if (!json.has(PROCESS_CONFIG)) {\n            return null;\n        }\n        JsonObject processConfig = json.getAsJsonObject(PROCESS_CONFIG);\n        if (!processConfig.has(ARGUMENTS)) {\n            return null;\n        }\n        JsonArray arguments = processConfig.getAsJsonArray(ARGUMENTS);\n        String[] result = new String[arguments.size()];\n        for (int i = 0; i < arguments.size(); i++) {\n            result[i] = arguments.get(i).getAsString();\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/Image.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Interface representing an image on the server.\n */\npublic interface Image {\n    /**\n     * @return the image ID\n     */\n    String getId();\n\n    /**\n     * @return the image ID, or null if not present\n     */\n    String getParentId();\n\n    /**\n     * @return Image create timestamp\n     */\n    long getCreated();\n\n    /**\n     * @return the image size\n     */\n    long getSize();\n\n    /**\n     * @return the image virtual size\n     */\n    long getVirtualSize();\n\n    /**\n     * @return the labels assigned to the image\n     */\n    Map<String, String> getLabels();\n\n    /**\n     * @return the names associated with the image (formatted as repository:tag)\n     */\n    List<String> getRepoTags();\n\n    /**\n     * @return the digests associated with the image (formatted as repository:tag@sha256:digest)\n     */\n    List<String> getRepoDigests();\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ImageArchiveManifest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.List;\n\nimport com.google.gson.JsonObject;\n\npublic interface ImageArchiveManifest {\n    /**\n     * @return the list of images in the archive.\n     */\n    List<ImageArchiveManifestEntry> getEntries();\n\n    /**\n     * Return the JSON object for the named config\n     * @param configName\n     * @return\n     */\n    JsonObject getConfig(String configName);\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ImageArchiveManifestAdapter.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\npublic class ImageArchiveManifestAdapter implements ImageArchiveManifest {\n    private List<ImageArchiveManifestEntry> entries;\n\n    private Map<String, JsonObject> config;\n\n    public ImageArchiveManifestAdapter(JsonElement json) {\n        this.entries = new ArrayList<>();\n\n        if(json.isJsonArray()) {\n            for(JsonElement entryJson : json.getAsJsonArray()) {\n                if(entryJson.isJsonObject()) {\n                    this.entries.add(new ImageArchiveManifestEntryAdapter(entryJson.getAsJsonObject()));\n                }\n            }\n        }\n\n        this.config = new LinkedHashMap<>();\n    }\n\n    @Override\n    public List<ImageArchiveManifestEntry> getEntries() {\n        return this.entries;\n    }\n\n    @Override\n    public JsonObject getConfig(String configName) {\n        return this.config.get(configName);\n    }\n\n    public JsonObject putConfig(String configName, JsonObject config) {\n        return this.config.put(configName, config);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ImageArchiveManifestEntry.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.List;\n\n/**\n * Interface representing an entry in an image archive manifest.\n */\npublic interface ImageArchiveManifestEntry {\n    /**\n     * @return the image id for this manifest entry\n     */\n    String getId();\n\n    /**\n     * @return the configuration JSON path for this manifest entry\n     */\n    String getConfig();\n\n    /**\n     * @return the repository tags associated with this manifest entry\n     */\n    List<String> getRepoTags();\n\n    /**\n     * @return the layer archive paths for this manifest entry\n     */\n    List<String> getLayers();\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ImageArchiveManifestEntryAdapter.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\n/**\n * Adapter to convert from JSON representation to model.\n */\npublic class ImageArchiveManifestEntryAdapter implements ImageArchiveManifestEntry {\n    public static final String CONFIG = \"Config\";\n    public static final String REPO_TAGS = \"RepoTags\";\n    public static final String LAYERS = \"Layers\";\n    public static final String CONFIG_JSON_SUFFIX = \".json\";\n\n    private String config;\n    private List<String> repoTags;\n    private List<String> layers;\n\n    public ImageArchiveManifestEntryAdapter(JsonObject json) {\n        JsonElement field;\n\n        if((field = json.get(CONFIG)) != null && field.isJsonPrimitive()) {\n            this.config = field.getAsString();\n        }\n\n        this.repoTags = new ArrayList<>();\n        if ((field = json.get(REPO_TAGS)) != null && field.isJsonArray()) {\n            for(JsonElement item : field.getAsJsonArray()) {\n                if(item.isJsonPrimitive()) {\n                    this.repoTags.add(item.getAsString());\n                }\n            }\n        }\n\n        this.layers = new ArrayList<>();\n        if ((field = json.get(LAYERS)) != null && field.isJsonArray()) {\n            for(JsonElement item : field.getAsJsonArray()) {\n                if(item.isJsonPrimitive()) {\n                    this.layers.add(item.getAsString());\n                }\n            }\n        }\n    }\n\n    @Override\n    public String getConfig() {\n        return config;\n    }\n\n    @Override\n    public String getId() {\n        return this.config == null || !this.config.endsWith(CONFIG_JSON_SUFFIX) ? this.config : this.config.substring(0, this.config.length() - CONFIG_JSON_SUFFIX.length());\n    }\n\n    @Override\n    public List<String> getRepoTags() {\n        return repoTags;\n    }\n\n    @Override\n    public List<String> getLayers() {\n        return layers;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/ImageDetails.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\npublic class ImageDetails implements Image {\n    public static final String CREATED = \"Created\";\n    public static final String ID = \"Id\";\n    public static final String LABELS = \"Labels\";\n    public static final String PARENT_ID = \"ParentId\";\n    public static final String REPO_TAGS = \"RepoTags\";\n    public static final String REPO_DIGESTS = \"RepoDigests\";\n    public static final String SIZE = \"Size\";\n    public static final String VIRTUAL_SIZE = \"VirtualSize\";\n\n    private final JsonObject json;\n\n    public ImageDetails(JsonObject json) {\n        this.json = json;\n    }\n\n    @Override\n    public String getId() {\n        return json.get(ID).getAsString();\n    }\n\n    @Override\n    public String getParentId() {\n        return json.has(PARENT_ID) ? json.get(PARENT_ID).getAsString() : null;\n    }\n\n    @Override\n    public long getCreated() {\n        return json.get(CREATED).getAsLong();\n    }\n\n    @Override\n    public long getSize() {\n        return json.get(SIZE).getAsLong();\n    }\n\n    @Override\n    public long getVirtualSize() {\n        return json.get(VIRTUAL_SIZE).getAsLong();\n    }\n\n    @Override\n    public Map<String, String> getLabels() {\n        return json.has(LABELS) ?\n                mapLabels(json.getAsJsonObject(LABELS)) :\n                Collections.emptyMap();\n    }\n\n    @Override\n    public List<String> getRepoTags() {\n        List<String> repoTags = new ArrayList<>();\n\n        if (json.has(REPO_TAGS) && json.get(REPO_TAGS) instanceof JsonArray) {\n            for (JsonElement item : json.getAsJsonArray(REPO_TAGS)) {\n                repoTags.add(item.getAsString());\n            }\n        }\n\n        return repoTags;\n    }\n\n    @Override\n    public List<String> getRepoDigests() {\n        List<String> repoDigests = new ArrayList<>();\n\n        if (json.has(REPO_DIGESTS)) {\n            for(JsonElement item : json.getAsJsonArray(REPO_DIGESTS)) {\n                repoDigests.add(item.getAsString());\n            }\n        }\n\n        return repoDigests;\n    }\n\n    private Map<String, String> mapLabels(JsonObject labels) {\n        int length = labels.size();\n        Map<String, String> mapped = new LinkedHashMap<>(length);\n\n        Iterator<String> iterator = labels.keySet().iterator();\n        while (iterator.hasNext()) {\n            String key = iterator.next();\n            mapped.put(key, labels.get(key).getAsString());\n        }\n\n        return mapped;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/Network.java",
    "content": "package io.fabric8.maven.docker.model;\n\npublic interface Network {\n\n    String getName();\n\n    String getId();\n\n    String getScope();\n\n    String getDriver();\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/model/NetworksListElement.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport com.google.gson.JsonObject;\n\npublic class NetworksListElement implements Network {\n\n    static final String NAME = \"Name\";\n    static final String ID = \"Id\";\n    static final String SCOPE = \"Scope\";\n    static final String DRIVER = \"Driver\";\n\n    private final JsonObject json;\n\n    public NetworksListElement(JsonObject json) {\n        this.json = json;\n    }\n\n    @Override\n    public String getName() {\n        return json.get(NAME).getAsString();\n    }\n\n    @Override\n    public String getDriver() {\n        return json.get(DRIVER).getAsString();\n    }\n\n    @Override\n    public String getScope() {\n        return json.get(SCOPE).getAsString();\n    }\n\n    @Override\n    public String getId() {\n        // only need first 12 to id a network\n        return json.get(ID).getAsString().substring(0, 12);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/ArchiveService.java",
    "content": "package io.fabric8.maven.docker.service;/*\n * \n * Copyright 2015 Roland Huss\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\nimport io.fabric8.maven.docker.assembly.ArchiverCustomizer;\nimport io.fabric8.maven.docker.assembly.AssemblyFiles;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;\nimport org.apache.maven.plugins.assembly.archive.ArchiveCreationException;\nimport org.apache.maven.plugins.assembly.format.AssemblyFormattingException;\n\nimport java.io.File;\nimport java.util.List;\n\n/**\n * @author roland\n * @since 30/11/15\n */\npublic class ArchiveService {\n\n\n    private final Logger log;\n    private DockerAssemblyManager dockerAssemblyManager;\n\n\n    public ArchiveService(DockerAssemblyManager dockerAssemblyManager,Logger log) {\n        this.log = log;\n        this.dockerAssemblyManager = dockerAssemblyManager;\n    }\n\n    /**\n     * Create the tar file container the source for building an image. This tar can be used directly for\n     * uploading to a Docker daemon for creating the image\n     *\n     * @param imageConfig the image configuration\n     * @param params mojo params for the project\n     * @return file for holding the sources\n     * @throws MojoExecutionException if during creation of the tar an error occurs.\n     */\n    public File createDockerBuildArchive(ImageConfiguration imageConfig, MojoParameters params)\n            throws MojoExecutionException {\n        return createDockerBuildArchive(imageConfig, params, null);\n    }\n\n    /**\n     * Create the tar file container the source for building an image. This tar can be used directly for\n     * uploading to a Docker daemon for creating the image\n     *\n     * @param imageConfig the image configuration\n     * @param params mojo params for the project\n     * @param customizer final customizer to be applied to the tar before being generated\n     * @return file for holding the sources\n     * @throws MojoExecutionException if during creation of the tar an error occurs.\n     */\n    public File createDockerBuildArchive(ImageConfiguration imageConfig, MojoParameters params, ArchiverCustomizer customizer)\n            throws MojoExecutionException {\n        File ret = createArchive(imageConfig.getName(), imageConfig.getBuildConfiguration(), params, log, customizer);\n        log.info(\"%s: Created docker source tar %s\",imageConfig.getDescription(), ret);\n        return ret;\n    }\n\n    /**\n     * Get a mapping of original to destination files which a covered by an assembly. This can be used\n     * to watch the source files for changes in order to update the target (either by recreating a docker image\n     * or by copying it into a running container)\n     *\n     * @param imageConfig image config for which to get files. The build- and assembly configuration in this image\n     *                    config must not be null.\n     * @param mojoParameters needed for tracking the assembly\n     * @return mapping of assembly files\n     * @throws MojoExecutionException\n     */\n    public AssemblyFiles getAssemblyFiles(ImageConfiguration imageConfig, MojoParameters mojoParameters)\n        throws MojoExecutionException {\n\n        String name = imageConfig.getName();\n        try {\n            return dockerAssemblyManager.getAssemblyFiles(name, imageConfig.getBuildConfiguration(), mojoParameters, log);\n        } catch (InvalidAssemblerConfigurationException | ArchiveCreationException | AssemblyFormattingException e) {\n            throw new MojoExecutionException(\"Cannot extract assembly files for image \" + name + \": \" + e, e);\n        }\n    }\n\n    /**\n     * Create an tar archive from a set of assembly files. Only files which changed since the last call are included.\n     * @param entries changed files. List must not be empty or null\n     * @param imageName image's name\n     * @param mojoParameters\n     * @return created archive\n     */\n    public File createChangedFilesArchive(List<AssemblyFiles.Entry> entries, File assemblyDir,\n                                          String imageName, MojoParameters mojoParameters) throws MojoExecutionException {\n        return dockerAssemblyManager.createChangedFilesArchive(entries, assemblyDir, imageName, mojoParameters);\n    }\n\n    // =============================================\n\n    File createArchive(String imageName, BuildImageConfiguration buildConfig, MojoParameters params, Logger log)\n            throws MojoExecutionException {\n        return createArchive(imageName, buildConfig, params, log, null);\n    }\n\n    File createArchive(String imageName, BuildImageConfiguration buildConfig, MojoParameters params, Logger log, ArchiverCustomizer customizer)\n            throws MojoExecutionException {\n        return dockerAssemblyManager.createDockerTarArchive(imageName, params, buildConfig, log, customizer);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/BuildService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.nio.file.Files;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.regex.PatternSyntaxException;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\nimport io.fabric8.maven.docker.access.BuildOptions;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.CleanupMode;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.model.ImageArchiveManifest;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestEntry;\nimport io.fabric8.maven.docker.util.DockerFileUtil;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageArchiveUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport io.fabric8.maven.docker.util.NamePatternUtil;\n\npublic class BuildService {\n\n    private final String argPrefix = \"docker.buildArg.\";\n\n    private final DockerAccess docker;\n    private final QueryService queryService;\n    private final ArchiveService archiveService;\n    private final RegistryService registryService;\n    private final Logger log;\n\n    BuildService(DockerAccess docker, QueryService queryService, RegistryService registryService, ArchiveService archiveService, Logger log) {\n        this.docker = docker;\n        this.queryService = queryService;\n        this.registryService = registryService;\n        this.archiveService = archiveService;\n        this.log = log;\n    }\n\n    /**\n     * Pull the base image if needed and run the build.\n     *\n     * @param imageConfig the image configuration\n     * @param buildContext the build context\n     * @throws DockerAccessException\n     * @throws MojoExecutionException\n     */\n    public void buildImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext, File buildArchiveFile)\n            throws DockerAccessException, MojoExecutionException {\n\n        if (imagePullManager != null) {\n            autoPullBaseImage(imageConfig, imagePullManager, buildContext);\n        }\n\n        buildImage(imageConfig, buildContext.getMojoParameters(), checkForNocache(imageConfig), checkForSquash(imageConfig), addBuildArgs(buildContext), buildArchiveFile);\n    }\n\n    /**\n     * Create docker archive for building image\n     *\n     * @param imageConfiguration image configuration\n     * @param buildContext docker build context\n     * @param archivePath build archive only flag, it can have values TRUE or FALSE and also\n     *                               it can hold path to archive where it might get copied over\n     * @return tarball for docker image\n     * @throws MojoExecutionException in case any exception comes during building tarball\n     */\n    public File buildArchive(ImageConfiguration imageConfiguration, BuildContext buildContext, String archivePath)\n            throws MojoExecutionException {\n        String imageName = imageConfiguration.getName();\n        ImageName.validate(imageName);\n        BuildImageConfiguration buildConfig = imageConfiguration.getBuildConfiguration();\n        MojoParameters params = buildContext.getMojoParameters();\n\n        if (buildConfig.getDockerArchive() != null) {\n            return buildConfig.getAbsoluteDockerTarPath(params);\n        }\n        long time = System.currentTimeMillis();\n\n        File dockerArchive = archiveService.createArchive(imageName, buildConfig, params, log);\n        log.info(\"%s: Created %s in %s\", imageConfiguration.getDescription(), dockerArchive.getName(), EnvUtil.formatDurationTill(time));\n\n        // Copy created tarball to directory if specified\n        try {\n            copyDockerArchive(imageConfiguration, dockerArchive, archivePath);\n        } catch (IOException exception) {\n            throw new MojoExecutionException(\"Error while copying created tar to specified buildArchive path: \" + archivePath,\n                    exception);\n        }\n        return dockerArchive;\n    }\n\n    public void copyDockerArchive(ImageConfiguration imageConfiguration, File dockerArchive, String archivePath) throws IOException {\n        if (archivePath != null && !archivePath.isEmpty()) {\n            Files.copy(dockerArchive.toPath(), new File(archivePath, dockerArchive.getName()).toPath());\n            log.info(\"%s: Copied created tarball to %s\", imageConfiguration.getDescription(), archivePath);\n        }\n    }\n\n    public void tagImage(ImageConfiguration imageConfig) throws DockerAccessException {\n\n        List<String> tags = imageConfig.getBuildConfiguration().getTags();\n        if (!tags.isEmpty()) {\n            String imageName = imageConfig.getName();\n            log.info(\"%s: Tag with %s\", imageConfig.getDescription(), EnvUtil.stringJoin(tags, \",\"));\n\n            for (String tag : tags) {\n                tagImage(imageName, tag, null);\n            }\n        }\n    }\n\n    /**\n     * Build an image\n     *\n     * @param imageConfig the image configuration\n     * @param params mojo params for the project\n     * @param noCache if not null, dictate the caching behaviour. Otherwise its taken from the build configuration\n     * @param buildArgs docker build args\n     * @throws DockerAccessException\n     * @throws MojoExecutionException\n     */\n    protected void buildImage(ImageConfiguration imageConfig, MojoParameters params, boolean noCache, boolean squash, Map<String, String> buildArgs, File dockerArchive)\n            throws DockerAccessException, MojoExecutionException {\n\n        String imageName = imageConfig.getName();\n        ImageName.validate(imageName);\n\n        BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n\n        String oldImageId = null;\n\n        CleanupMode cleanupMode = buildConfig.cleanupMode();\n        if (cleanupMode.isRemove()) {\n            oldImageId = queryService.getImageId(imageName);\n        }\n\n        if (buildConfig.getDockerArchive() != null) {\n            File tarArchive = buildConfig.getAbsoluteDockerTarPath(params);\n            String archiveImageName = getArchiveImageName(buildConfig, tarArchive);\n\n            long time = System.currentTimeMillis();\n\n            docker.loadImage(imageName, tarArchive);\n            log.info(\"%s: Loaded tarball in %s\", buildConfig.getDockerArchive(), EnvUtil.formatDurationTill(time));\n\n            if(archiveImageName != null && !archiveImageName.equals(imageName)) {\n                docker.tag(archiveImageName, imageName, true);\n            }\n\n            return;\n        }\n\n        Map<String, String> mergedBuildMap = prepareBuildArgs(buildArgs, buildConfig);\n\n        // auto is now supported by docker, consider switching?\n        BuildOptions opts =\n                new BuildOptions(buildConfig.getBuildOptions())\n                        .dockerfile(getDockerfileName(buildConfig))\n                        .forceRemove(cleanupMode.isRemove())\n                        .noCache(noCache)\n                        .squash(squash)\n                        .cacheFrom(buildConfig.getCacheFrom())\n                        .network(buildConfig.getNetwork())\n                        .buildArgs(mergedBuildMap);\n        String newImageId = doBuildImage(imageName, dockerArchive, opts);\n        log.info(\"%s: Built image %s\", imageConfig.getDescription(), newImageId);\n\n        if (oldImageId != null && !oldImageId.equals(newImageId)) {\n            try {\n                docker.removeImage(oldImageId, true);\n                log.info(\"%s: Removed old image %s\", imageConfig.getDescription(), oldImageId);\n            } catch (DockerAccessException exp) {\n                if (cleanupMode == CleanupMode.TRY_TO_REMOVE) {\n                    log.warn(\"%s: %s (old image)%s\", imageConfig.getDescription(), exp.getMessage(),\n                            (exp.getCause() != null ? \" [\" + exp.getCause().getMessage() + \"]\" : \"\"));\n                } else {\n                    throw exp;\n                }\n            }\n        }\n    }\n\n    public void tagImage(String imageName, String tag, String repo) throws DockerAccessException {\n        if (tag != null) {\n            String fullImageName = new ImageName(imageName, tag).getNameWithOptionalRepository(repo);\n            docker.tag(imageName, fullImageName, true);\n            log.info(\"Tagging image %s successful!\", fullImageName);\n        }\n    }\n\n    private Map<String, String> prepareBuildArgs(Map<String, String> buildArgs, BuildImageConfiguration buildConfig) {\n        ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder().putAll(buildArgs);\n        if (buildConfig.getArgs() != null) {\n            builder.putAll(buildConfig.getArgs());\n        }\n        return builder.build();\n    }\n\n    private String getArchiveImageName(BuildImageConfiguration buildConfig, File tarArchive) throws MojoExecutionException {\n        if(buildConfig.getLoadNamePattern() == null || buildConfig.getLoadNamePattern().length() == 0) {\n            return null;\n        }\n\n        ImageArchiveManifest manifest;\n        try {\n            manifest = readArchiveManifest(tarArchive);\n        } catch (IOException | JsonParseException e) {\n            throw new MojoExecutionException(\"Unable to read image manifest in archive \" + buildConfig.getDockerArchive(), e);\n        }\n\n        String archiveImageName;\n\n        try {\n            archiveImageName = matchArchiveImagesToPattern(buildConfig.getLoadNamePattern(), manifest);\n        } catch(PatternSyntaxException e) {\n            throw new MojoExecutionException(\"Unable to interpret loadNamePattern \" + buildConfig.getLoadNamePattern(), e);\n        }\n\n        if(archiveImageName == null) {\n            throw new MojoExecutionException(\"No image in the archive has a tag that matches pattern \" + buildConfig.getLoadNamePattern());\n        }\n\n        return archiveImageName;\n    }\n\n    private ImageArchiveManifest readArchiveManifest(File tarArchive) throws IOException, JsonParseException {\n        long time = System.currentTimeMillis();\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(tarArchive);\n\n        log.info(\"%s: Read archive manifest in %s\", tarArchive, EnvUtil.formatDurationTill(time));\n\n        // Show the results of reading the manifest to users trying to debug their configuration\n        if(log.isDebugEnabled()) {\n            for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n                log.debug(\"Entry ID: %s has %d repo tag(s)\", entry.getId(), entry.getRepoTags().size());\n                for(String repoTag : entry.getRepoTags()) {\n                    log.debug(\"Repo Tag: %s\", repoTag);\n                }\n            }\n        }\n\n        return manifest;\n    }\n\n    private String matchArchiveImagesToPattern(String imageNamePattern, ImageArchiveManifest manifest) {\n        String imageNameRegex = NamePatternUtil.convertNamePattern(imageNamePattern);\n        log.debug(\"Image name regex is %s\", imageNameRegex);\n\n        Map<String, ImageArchiveManifestEntry> entries = ImageArchiveUtil.findEntriesByRepoTagPattern(imageNameRegex, manifest);\n\n        // Show the matches from the manifest to users trying to debug their configuration\n        if(log.isDebugEnabled()) {\n            for(Map.Entry<String, ImageArchiveManifestEntry> entry : entries.entrySet()) {\n                log.debug(\"Repo tag pattern matched %s referring to image %s\", entry.getKey(), entry.getValue().getId());\n            }\n        }\n\n        if(!entries.isEmpty()) {\n            Map.Entry<String, ImageArchiveManifestEntry> matchedEntry = entries.entrySet().iterator().next();\n\n            if(ImageArchiveUtil.mapEntriesById(entries.values()).size() > 1) {\n                log.warn(\"Multiple image ids matched pattern %s: using tag %s associated with id %s\",\n                        imageNamePattern, matchedEntry.getKey(), matchedEntry.getValue().getId());\n            } else {\n                log.info(\"Using image tag %s from archive\", matchedEntry.getKey());\n            }\n\n            return matchedEntry.getKey();\n        }\n\n        return null;\n    }\n\n    private String getDockerfileName(BuildImageConfiguration buildConfig) {\n        if (buildConfig.isDockerFileMode()) {\n            return buildConfig.getDockerFile().getName();\n        } else {\n            return null;\n        }\n    }\n\n    private String doBuildImage(String imageName, File dockerArchive, BuildOptions options)\n            throws DockerAccessException, MojoExecutionException {\n        docker.buildImage(imageName, dockerArchive, options);\n        return queryService.getImageId(imageName);\n    }\n\n    private Map<String, String> addBuildArgs(BuildContext buildContext) {\n        Map<String, String> buildArgsFromProject = addBuildArgsFromProperties(buildContext.getMojoParameters().getProject().getProperties());\n        Map<String, String> buildArgsFromSystem = addBuildArgsFromProperties(System.getProperties());\n        Map<String, String> buildArgsFromDockerConfig = addBuildArgsFromDockerConfig();\n        return ImmutableMap.<String, String>builder()\n                .putAll(buildArgsFromDockerConfig)\n                .putAll(buildContext.getBuildArgs() != null ? buildContext.getBuildArgs() : Collections.<String, String>emptyMap())\n                .putAll(buildArgsFromProject)\n                .putAll(buildArgsFromSystem)\n                .build();\n    }\n\n    private Map<String, String> addBuildArgsFromProperties(Properties properties) {\n        Map<String, String> buildArgs = new HashMap<>();\n        for (Object keyObj : properties.keySet()) {\n            String key = (String) keyObj;\n            if (key.startsWith(argPrefix)) {\n                String argKey = key.replaceFirst(argPrefix, \"\");\n                String value = properties.getProperty(key);\n\n                if (!isEmpty(value)) {\n                    buildArgs.put(argKey, value);\n                }\n            }\n        }\n        log.debug(\"Build args set %s\", buildArgs);\n        return buildArgs;\n    }\n\n    private Map<String, String> addBuildArgsFromDockerConfig() {\n        JsonObject dockerConfig = DockerFileUtil.readDockerConfig();\n        if (dockerConfig == null) {\n            return Collections.emptyMap();\n        }\n\n        // add proxies\n        Map<String, String> buildArgs = new HashMap<>();\n        if (dockerConfig.has(\"proxies\")) {\n            JsonObject proxies = dockerConfig.getAsJsonObject(\"proxies\");\n            if (proxies.has(\"default\")) {\n                JsonObject defaultProxyObj = proxies.getAsJsonObject(\"default\");\n                String[] proxyMapping = new String[] {\n                        \"httpProxy\", \"http_proxy\",\n                        \"httpsProxy\", \"https_proxy\",\n                        \"noProxy\", \"no_proxy\",\n                        \"ftpProxy\", \"ftp_proxy\"\n                };\n\n                for(int index = 0; index < proxyMapping.length; index += 2) {\n                    if (defaultProxyObj.has(proxyMapping[index])) {\n                        buildArgs.put(proxyMapping[index+1], defaultProxyObj.get(proxyMapping[index]).getAsString());\n                    }\n                }\n            }\n        }\n        log.debug(\"Build args set %s\", buildArgs);\n        return buildArgs;\n    }\n\n    private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext)\n            throws DockerAccessException, MojoExecutionException {\n        BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n\n        if (buildConfig.getDockerArchive() != null) {\n            // No auto pull needed in archive mode\n            return;\n        }\n\n        List<String> fromImages;\n        if (buildConfig.isDockerFileMode()) {\n            fromImages = extractBaseFromDockerfile(buildConfig, buildContext);\n        } else {\n            fromImages = new LinkedList<>();\n            String baseImage = extractBaseFromConfiguration(buildConfig);\n            if (baseImage!=null) {\n                fromImages.add(extractBaseFromConfiguration(buildConfig));\n            }\n        }\n        for (String fromImage : fromImages) {\n            if (fromImage != null && !DockerAssemblyManager.SCRATCH_IMAGE.equals(fromImage)) {\n                registryService.pullImageWithPolicy(fromImage, imagePullManager, buildContext.getRegistryConfig(), queryService.hasImage(fromImage));\n            }\n        }\n    }\n\n    private String extractBaseFromConfiguration(BuildImageConfiguration buildConfig) {\n        String fromImage;\n        fromImage = buildConfig.getFrom();\n        if (fromImage == null) {\n            AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n            if (assemblyConfig == null) {\n                fromImage = DockerAssemblyManager.DEFAULT_DATA_BASE_IMAGE;\n            }\n        }\n        return fromImage;\n    }\n\n    private List<String> extractBaseFromDockerfile(BuildImageConfiguration buildConfig, BuildContext buildContext) {\n        List<String> fromImage;\n        try {\n            File fullDockerFilePath = buildConfig.getAbsoluteDockerFilePath(buildContext.getMojoParameters());\n            fromImage = DockerFileUtil.extractBaseImages(\n                fullDockerFilePath,\n                DockerFileUtil.createInterpolator(buildContext.getMojoParameters(), buildConfig.getFilter()));\n        } catch (IOException e) {\n            // Cant extract base image, so we wont try an auto pull. An error will occur later anyway when\n            // building the image, so we are passive here.\n            return Collections.emptyList();\n        }\n        return fromImage;\n    }\n\n    private boolean checkForNocache(ImageConfiguration imageConfig) {\n        String noCache = System.getProperty(\"docker.noCache\");\n        if (noCache == null) {\n            noCache = System.getProperty(\"docker.nocache\");\n        }\n        if (noCache != null) {\n            return noCache.length() == 0 || Boolean.valueOf(noCache);\n        } else {\n            BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n            return buildConfig.noCache();\n        }\n    }\n\n    private boolean checkForSquash(ImageConfiguration imageConfig) {\n        String squash = System.getProperty(\"docker.squash\");\n        if (squash != null) {\n            return squash.length() == 0 || Boolean.valueOf(squash);\n        } else {\n            BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n            return buildConfig.squash();\n        }\n    }\n\n    private boolean isEmpty(String str) {\n        return str == null || str.isEmpty();\n    }\n\n\n    // ===========================================\n\n\n    public static class BuildContext implements Serializable {\n\n        private MojoParameters mojoParameters;\n\n        private Map<String, String> buildArgs;\n\n        private RegistryService.RegistryConfig registryConfig;\n\n        public BuildContext() {\n        }\n\n        public MojoParameters getMojoParameters() {\n            return mojoParameters;\n        }\n\n        public Map<String, String> getBuildArgs() {\n            return buildArgs;\n        }\n\n        public RegistryService.RegistryConfig getRegistryConfig() {\n            return registryConfig;\n        }\n\n        public static class Builder {\n\n            private BuildContext context;\n\n            public Builder() {\n                this.context = new BuildContext();\n            }\n\n            public Builder(BuildContext context) {\n                this.context = context;\n            }\n\n            public Builder mojoParameters(MojoParameters mojoParameters) {\n                context.mojoParameters = mojoParameters;\n                return this;\n            }\n\n            public Builder buildArgs(Map<String, String> buildArgs) {\n                context.buildArgs = buildArgs;\n                return this;\n            }\n\n            public Builder registryConfig(RegistryService.RegistryConfig registryConfig) {\n                context.registryConfig = registryConfig;\n                return this;\n            }\n\n            public BuildContext build() {\n                return context;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/ContainerTracker.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.StopMode;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.util.GavLabel;\n\n/**\n * Tracker class for tracking started containers so that they can be shut down at the end when\n * <code>docker:start</code> and <code>docker:stop</code> are used in the same run\n */\npublic class ContainerTracker {\n\n    // Map holding associations between started containers and their images via name and aliases\n    // Key: Image, Value: Container\n    private final Map<String, String> imageToContainerMap = new HashMap<>();\n\n    // Key: Alias, Value: container\n    private final Map<String, String> aliasToContainerMap = new HashMap<>();\n\n    // Maps holding actions to be used when doing a shutdown\n    private final Map<String, ContainerShutdownDescriptor> shutdownDescriptorPerContainerMap = new LinkedHashMap<>();\n    private final Map<GavLabel,List<ContainerShutdownDescriptor>> shutdownDescriptorPerPomLabelMap = new HashMap<>();\n\n    /**\n     * Register a started container to this tracker\n     *\n     * @param containerId container id to register\n     * @param imageConfig configuration of associated image\n     * @param gavLabel pom label to identifying the reactor project where the container was created\n     */\n    public synchronized void registerContainer(String containerId,\n                                               ImageConfiguration imageConfig,\n                                               GavLabel gavLabel) {\n        ContainerShutdownDescriptor descriptor = new ContainerShutdownDescriptor(imageConfig, containerId);\n        shutdownDescriptorPerContainerMap.put(containerId, descriptor);\n        updatePomLabelMap(gavLabel, descriptor);\n        updateImageToContainerMapping(imageConfig, containerId);\n    }\n\n    /**\n     * Remove a container from this container (if stored) and return its descriptor\n     *\n     * @param containerId id to remove\n     * @return descriptor of the container removed or <code>null</code>\n     */\n    public synchronized ContainerShutdownDescriptor removeContainer(String containerId) {\n        ContainerShutdownDescriptor descriptor = shutdownDescriptorPerContainerMap.remove(containerId);\n        if (descriptor != null) {\n            removeContainerIdFromLookupMaps(containerId);\n            removeDescriptorFromPomLabelMap(descriptor);\n        }\n        return descriptor;\n    }\n\n    /**\n     * Lookup a container by name or alias from the tracked containers\n     *\n     * @param lookup name or alias of the container to lookup\n     * @return container id found or <code>null</code>\n     */\n    public synchronized String lookupContainer(String lookup) {\n        if (aliasToContainerMap.containsKey(lookup)) {\n            return aliasToContainerMap.get(lookup);\n        }\n        return imageToContainerMap.get(lookup);\n    }\n\n    /**\n     * Get all shutdown descriptors for a given pom label and remove it from the tracker. The descriptors\n     * are returned in reverse order of their registration.\n     *\n     * If no pom label is given, then all descriptors are returned.\n     *\n     * @param gavLabel the label for which to get the descriptors or <code>null</code> for all descriptors\n     * @return the descriptors for the given label or an empty collection\n     */\n    public synchronized Collection<ContainerShutdownDescriptor> removeShutdownDescriptors(GavLabel gavLabel) {\n        List<ContainerShutdownDescriptor> descriptors;\n        if (gavLabel != null) {\n            descriptors = removeFromPomLabelMap(gavLabel);\n            removeFromPerContainerMap(descriptors);\n        } else {\n            // All entries are requested\n            descriptors = new ArrayList<>(shutdownDescriptorPerContainerMap.values());\n            clearAllMaps();\n        }\n\n        Collections.reverse(descriptors);\n        return descriptors;\n    }\n\n    // ========================================================\n\n    private void updatePomLabelMap(GavLabel gavLabel, ContainerShutdownDescriptor descriptor) {\n        if (gavLabel != null) {\n            List<ContainerShutdownDescriptor> descList = shutdownDescriptorPerPomLabelMap.get(gavLabel);\n            if (descList == null) {\n                descList = new ArrayList<>();\n                shutdownDescriptorPerPomLabelMap.put(gavLabel, descList);\n            }\n            descList.add(descriptor);\n        }\n    }\n\n    private void removeDescriptorFromPomLabelMap(ContainerShutdownDescriptor descriptor) {\n        Iterator<Map.Entry<GavLabel, List<ContainerShutdownDescriptor>>> mapIt = shutdownDescriptorPerPomLabelMap.entrySet().iterator();\n        while(mapIt.hasNext()) {\n            Map.Entry<GavLabel,List<ContainerShutdownDescriptor>> mapEntry = mapIt.next();\n            List<ContainerShutdownDescriptor> descs = mapEntry.getValue();\n            Iterator<ContainerShutdownDescriptor> it = descs.iterator();\n            while (it.hasNext()) {\n                ContainerShutdownDescriptor desc = it.next();\n                if (descriptor.equals(desc)) {\n                    it.remove();\n                }\n            }\n            if (descs.size() == 0) {\n                mapIt.remove();\n            }\n        }\n    }\n\n    private void removeContainerIdFromLookupMaps(String containerId) {\n        removeValueFromMap(imageToContainerMap,containerId);\n        removeValueFromMap(aliasToContainerMap,containerId);\n    }\n\n    private void removeValueFromMap(Map<String, String> map, String value) {\n        Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();\n        while (it.hasNext()) {\n            Map.Entry<String,String> entry = it.next();\n            if (entry.getValue().equals(value)) {\n                it.remove();\n            }\n        }\n    }\n\n    private void updateImageToContainerMapping(ImageConfiguration imageConfig, String id) {\n        // Register name -> containerId and alias -> name\n        imageToContainerMap.put(imageConfig.getName(), id);\n        if (imageConfig.getAlias() != null) {\n            aliasToContainerMap.put(imageConfig.getAlias(), id);\n        }\n    }\n\n    private void removeFromPerContainerMap(List<ContainerShutdownDescriptor> descriptors) {\n        Iterator<Map.Entry<String, ContainerShutdownDescriptor>> it = shutdownDescriptorPerContainerMap.entrySet().iterator();\n        while (it.hasNext()) {\n            Map.Entry<String, ContainerShutdownDescriptor> entry = it.next();\n            if (descriptors.contains(entry.getValue())) {\n                removeContainerIdFromLookupMaps(entry.getKey());\n                it.remove();\n            }\n        }\n    }\n\n    private List<ContainerShutdownDescriptor> removeFromPomLabelMap(GavLabel gavLabel) {\n        List<ContainerShutdownDescriptor> descriptors;\n        descriptors = shutdownDescriptorPerPomLabelMap.remove(gavLabel);\n        if (descriptors == null) {\n            descriptors = new ArrayList<>();\n        } return descriptors;\n    }\n\n    private void clearAllMaps() {\n        shutdownDescriptorPerContainerMap.clear();\n        shutdownDescriptorPerPomLabelMap.clear();\n        imageToContainerMap.clear();\n        aliasToContainerMap.clear();\n    }\n\n    // =======================================================\n\n    static class ContainerShutdownDescriptor {\n\n        // The image's configuration\n        private final ImageConfiguration imageConfig;\n\n        // Alias of the image\n        private final String containerId;\n\n        // How long to wait after shutdown (in milliseconds)\n        private final int shutdownGracePeriod;\n\n        // How long to wait after stop to kill container (in seconds)\n        private final int killGracePeriod;\n\n        // Whether to kill or stop gracefully\n        private final StopMode stopMode;\n\n\n        // Command to call before stopping container and whether to stop the build\n        private String preStop;\n        private boolean breakOnError = false;\n\n        ContainerShutdownDescriptor(ImageConfiguration imageConfig, String containerId) {\n            this.imageConfig = imageConfig;\n            this.containerId = containerId;\n\n            RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n            WaitConfiguration waitConfig = runConfig != null ? runConfig.getWaitConfiguration() : null;\n            this.shutdownGracePeriod = waitConfig != null && waitConfig.getShutdown() != null ? waitConfig.getShutdown() : 0;\n            this.stopMode = runConfig != null ? runConfig.getStopMode()  : StopMode.graceful;\n            this.killGracePeriod = waitConfig != null && waitConfig.getKill() != null ? waitConfig.getKill() : 0;\n            if (waitConfig != null && waitConfig.getExec() != null) {\n                this.preStop = waitConfig.getExec().getPreStop();\n                this.breakOnError = waitConfig.getExec().isBreakOnError();\n            }\n        }\n\n        public ImageConfiguration getImageConfiguration() {\n            return imageConfig;\n        }\n\n        public String getImage() {\n            return imageConfig.getName();\n        }\n\n        public String getContainerId() {\n            return containerId;\n        }\n\n        public String getDescription() {\n            return imageConfig.getDescription();\n        }\n\n        public int getShutdownGracePeriod() {\n            return shutdownGracePeriod;\n        }\n\n        public int getKillGracePeriod() {\n            return killGracePeriod;\n        }\n\n        public String getPreStop() {\n            return preStop;\n        }\n\n        public boolean isBreakOnError() {\n            return breakOnError;\n        }\n\n        public StopMode getStopMode() {\n            return stopMode;\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            ContainerShutdownDescriptor that = (ContainerShutdownDescriptor) o;\n\n            return containerId.equals(that.containerId);\n\n        }\n\n        @Override\n        public int hashCode() {\n            return containerId.hashCode();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/DockerAccessFactory.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerConnectionDetector;\nimport io.fabric8.maven.docker.access.DockerMachine;\nimport io.fabric8.maven.docker.access.hc.DockerAccessWithHcClient;\nimport io.fabric8.maven.docker.config.DockerMachineConfiguration;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.codehaus.plexus.component.annotations.Component;\n\n/**\n *\n */\n@Component(role = DockerAccessFactory.class, instantiationStrategy = \"singleton\")\npublic class DockerAccessFactory {\n\n    public DockerAccess createDockerAccess(DockerAccessContext dockerAccessContext) throws MojoExecutionException, MojoFailureException {\n\n        try {\n            DockerConnectionDetector dockerConnectionDetector = createDockerConnectionDetector(dockerAccessContext, dockerAccessContext.getLog());\n            DockerConnectionDetector.ConnectionParameter connectionParam =\n                    dockerConnectionDetector.detectConnectionParameter(dockerAccessContext.getDockerHost(), dockerAccessContext.getCertPath());\n            DockerAccess access = new DockerAccessWithHcClient(connectionParam.getUrl(),\n                    connectionParam.getCertPath(),\n                    dockerAccessContext.getMaxConnections(),\n                    dockerAccessContext.getLog());\n            access.start();\n            setDockerHostAddressProperty(dockerAccessContext, connectionParam.getUrl());\n            return access;\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Cannot create docker access object \", e);\n        }\n\n    }\n\n    private DockerConnectionDetector createDockerConnectionDetector(DockerAccessContext dockerAccessContext, Logger log) {\n        return new DockerConnectionDetector(getDockerHostProviders(dockerAccessContext, log));\n    }\n\n    private List<DockerConnectionDetector.DockerHostProvider> getDockerHostProviders(DockerAccessContext dockerAccessContext, Logger log) {\n        if (dockerAccessContext.getDockerHostProviders() != null) {\n            return dockerAccessContext.getDockerHostProviders();\n        }\n\n        return getDefaultDockerHostProviders(dockerAccessContext, log);\n    }\n\n    /**\n     * Return a list of providers which could delive connection parameters from\n     * calling external commands. For this plugin this is docker-machine, but can be overridden\n     * to add other config options, too.\n     *\n     * @return list of providers or <code>null</code> if none are applicable\n     */\n    private List<DockerConnectionDetector.DockerHostProvider> getDefaultDockerHostProviders(DockerAccessContext dockerAccessContext, Logger log) {\n\n        DockerMachineConfiguration config = dockerAccessContext.getMachine();\n        if (dockerAccessContext.isSkipMachine()) {\n            config = null;\n        } else if (config == null) {\n            Properties projectProps = dockerAccessContext.getProjectProperties();\n            if (projectProps.containsKey(DockerMachineConfiguration.DOCKER_MACHINE_NAME_PROP)) {\n                config = new DockerMachineConfiguration(\n                    projectProps.getProperty(DockerMachineConfiguration.DOCKER_MACHINE_NAME_PROP),\n                    projectProps.getProperty(DockerMachineConfiguration.DOCKER_MACHINE_AUTO_CREATE_PROP),\n                    projectProps.getProperty(DockerMachineConfiguration.DOCKER_MACHINE_REGENERATE_CERTS_AFTER_START_PROP));\n            }\n        }\n\n        List<DockerConnectionDetector.DockerHostProvider> ret = new ArrayList<>();\n        ret.add(new DockerMachine(log, config));\n        return ret;\n    }\n\n    // Registry for managed containers\n    private void setDockerHostAddressProperty(DockerAccessContext dockerAccessContext, String dockerUrl) throws MojoFailureException {\n        Properties props = dockerAccessContext.getProjectProperties();\n        if (props.getProperty(\"docker.host.address\") == null) {\n            final String host;\n            try {\n                URI uri = new URI(dockerUrl);\n                if (uri.getHost() == null && (uri.getScheme().equals(\"unix\") || uri.getScheme().equals(\"npipe\"))) {\n                    host = \"localhost\";\n                } else {\n                    host = uri.getHost();\n                }\n            } catch (URISyntaxException e) {\n                throw new MojoFailureException(\"Cannot parse \" + dockerUrl + \" as URI: \" + e.getMessage(), e);\n            }\n            props.setProperty(\"docker.host.address\", host == null ? \"\" : host);\n        }\n    }\n\n    // ===========================================\n\n    public static class DockerAccessContext implements Serializable {\n\n        private Properties projectProperties;\n\n        private DockerMachineConfiguration machine;\n\n        private List<DockerConnectionDetector.DockerHostProvider> dockerHostProviders;\n\n        private boolean skipMachine;\n\n        private String minimalApiVersion;\n\n        private String dockerHost;\n\n        private String certPath;\n\n        private int maxConnections;\n\n        private Logger log;\n\n        public DockerAccessContext() {\n        }\n\n        public Properties getProjectProperties() {\n            return projectProperties;\n        }\n\n        public DockerMachineConfiguration getMachine() {\n            return machine;\n        }\n\n        public List<DockerConnectionDetector.DockerHostProvider> getDockerHostProviders() {\n            return dockerHostProviders;\n        }\n\n        public boolean isSkipMachine() {\n            return skipMachine;\n        }\n\n        public String getMinimalApiVersion() {\n            return minimalApiVersion;\n        }\n\n        public String getDockerHost() {\n            return dockerHost;\n        }\n\n        public String getCertPath() {\n            return certPath;\n        }\n\n        public int getMaxConnections() {\n            return maxConnections;\n        }\n\n        public Logger getLog() {\n            return log;\n        }\n\n        public static class Builder {\n\n            private DockerAccessContext context = new DockerAccessContext();\n\n            public Builder() {\n                this.context = new DockerAccessContext();\n            }\n\n            public Builder(DockerAccessContext context) {\n                this.context = context;\n            }\n\n            public Builder projectProperties(Properties projectProperties) {\n                context.projectProperties = projectProperties;\n                return this;\n            }\n\n            public Builder machine(DockerMachineConfiguration machine) {\n                context.machine = machine;\n                return this;\n            }\n\n            public Builder dockerHostProviders(List<DockerConnectionDetector.DockerHostProvider> dockerHostProviders) {\n                context.dockerHostProviders = dockerHostProviders;\n                return this;\n            }\n\n            public Builder skipMachine(boolean skipMachine) {\n                context.skipMachine = skipMachine;\n                return this;\n            }\n\n            public Builder minimalApiVersion(String minimalApiVersion) {\n                context.minimalApiVersion = minimalApiVersion;\n                return this;\n            }\n\n            public Builder dockerHost(String dockerHost) {\n                context.dockerHost = dockerHost;\n                return this;\n            }\n\n            public Builder certPath(String certPath) {\n                context.certPath = certPath;\n                return this;\n            }\n\n            public Builder maxConnections(int maxConnections) {\n                context.maxConnections = maxConnections;\n                return this;\n            }\n\n            public Builder log(Logger log) {\n                context.log = log;\n                return this;\n            }\n\n            public DockerAccessContext build() {\n                return context;\n            }\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/ImagePullManager.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.config.ImagePullPolicy;\nimport io.fabric8.maven.docker.util.AutoPullMode;\nimport io.fabric8.maven.docker.util.JsonFactory;\n\n/**\n * Simple interface for a ImagePullCache manager, to load and persist the cache.\n */\npublic class ImagePullManager {\n\n    // Key for the previously used image cache\n    private static final String CONTEXT_KEY_PREVIOUSLY_PULLED = \"CONTEXT_KEY_PREVIOUSLY_PULLED\";\n\n    // image pull policy\n    private final ImagePullPolicy imagePullPolicy;\n\n    private CacheStore cacheStore;\n\n    public ImagePullManager(CacheStore cacheStore, String imagePullPolicy, String autoPull) {\n        this.cacheStore = cacheStore;\n        this.imagePullPolicy = createPullPolicy(imagePullPolicy, autoPull);\n    }\n\n    ImagePullPolicy getImagePullPolicy() {\n        return imagePullPolicy;\n    }\n\n    public ImagePullPolicy createPullPolicy(String imagePullPolicy, String autoPull) {\n        if (imagePullPolicy != null) {\n            return ImagePullPolicy.fromString(imagePullPolicy);\n        }\n        if (autoPull != null) {\n            AutoPullMode autoPullMode = AutoPullMode.fromString(autoPull);\n            switch(autoPullMode) {\n                case OFF:\n                    return ImagePullPolicy.Never;\n                case ALWAYS:\n                    return ImagePullPolicy.Always;\n                case ON:\n                case ONCE:\n                    return ImagePullPolicy.IfNotPresent;\n            }\n        }\n        return ImagePullPolicy.IfNotPresent;\n    }\n\n    public boolean hasAlreadyPulled(String image) {\n        return load().has(image);\n    }\n\n    public void pulled(String image) {\n        save(load().add(image));\n    }\n\n\n    public interface CacheStore {\n        String get(String key);\n\n        void put(String key, String value);\n    }\n\n    public ImagePullCache load() {\n\n        String pullCacheJson = cacheStore.get(CONTEXT_KEY_PREVIOUSLY_PULLED);\n\n        ImagePullCache cache = new ImagePullCache(pullCacheJson);\n\n        if (pullCacheJson == null) {\n            save(cache);\n            cacheStore.put(CONTEXT_KEY_PREVIOUSLY_PULLED, cache.toString());\n        }\n        return cache;\n    }\n\n    public void save(ImagePullCache cache) {\n        cacheStore.put(CONTEXT_KEY_PREVIOUSLY_PULLED, cache.toString());\n    }\n\n    /**\n     * Simple serializable cache for holding image names\n     *\n     * @author roland\n     * @since 20/07/16\n     */\n    class ImagePullCache {\n\n        private JsonObject cache;\n\n        public ImagePullCache() {\n            this(null);\n        }\n\n        public ImagePullCache(String json) {\n            cache = json != null ? JsonFactory.newJsonObject(json) : new JsonObject();\n        }\n\n        public boolean has(String imageName) {\n            return cache.has(imageName);\n        }\n\n        public ImagePullCache add(String image) {\n            cache.addProperty(image, Boolean.TRUE);\n            return this;\n        }\n\n        @Override\n        public String toString() {\n            return cache.toString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/JibBuildService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport com.google.cloud.tools.jib.api.Credential;\nimport com.google.cloud.tools.jib.api.JibContainerBuilder;\nimport com.google.cloud.tools.jib.api.TarImage;\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.assembly.AssemblyFiles;\nimport io.fabric8.maven.docker.assembly.BuildDirs;\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.util.AuthConfigFactory;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.JibServiceUtil;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.plugin.MojoExecutionException;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport static io.fabric8.maven.docker.util.JibServiceUtil.containerFromImageConfiguration;\nimport static io.fabric8.maven.docker.util.JibServiceUtil.getBaseImage;\n\npublic class JibBuildService {\n    private static final String DOCKER_LOGIN_DEFAULT_REGISTRY = \"https://index.docker.io/v1/\";\n    private static final List<String> DEFAULT_DOCKER_REGISTRIES = Arrays.asList(\n            \"docker.io\", \"index.docker.io\", \"registry.hub.docker.com\"\n    );\n    private static final String PUSH_REGISTRY = \".docker.push.registry\";\n    private static final String ARCHIVE_FILE_NAME = \"docker-build\";\n    private final Logger log;\n    private final ServiceHub serviceHub;\n    private final MojoParameters mojoParameters;\n\n    public JibBuildService(ServiceHub hub, MojoParameters mojoParameters, Logger log) {\n        this.serviceHub = hub;\n        this.mojoParameters = mojoParameters;\n        this.log = log;\n    }\n\n    public void build(ImageConfiguration imageConfig, RegistryService.RegistryConfig registryConfig) throws MojoExecutionException {\n        try {\n            log.info(\"[[B]]JIB[[B]] image build started\");\n            if (imageConfig.getBuildConfiguration().isDockerFileMode()) {\n                throw new MojoExecutionException(\"Dockerfile mode is not supported with JIB build strategy\");\n            }\n            prependRegistry(imageConfig, mojoParameters.getProject().getProperties().getProperty(PUSH_REGISTRY));\n            BuildDirs buildDirs = new BuildDirs(imageConfig.getName(), mojoParameters);\n            final Credential pullRegistryCredential = getRegistryCredentials(\n                    registryConfig, false, imageConfig, log);\n            final JibContainerBuilder containerBuilder = containerFromImageConfiguration(imageConfig, pullRegistryCredential);\n\n            File dockerTarArchive = getAssemblyTarArchive(imageConfig, serviceHub, mojoParameters, log);\n\n            if (imageConfig.getBuildConfiguration().getAssemblyConfiguration() != null) {\n                // TODO: Improve Assembly Manager so that the effective assemblyFileEntries computed can be properly shared\n                // the call to DockerAssemblyManager.getInstance().createDockerTarArchive should not be necessary,\n                // files should be added using the AssemblyFileEntry list. DockerAssemblyManager, should provide\n                // a common way to achieve this so that both the tar builder and any other builder could get a hold of\n                // archive customizers, file entries, etc.\n                AssemblyFiles assemblyFiles = serviceHub.getDockerAssemblyManager()\n                        .getAssemblyFiles(imageConfig.getName(), imageConfig.getBuildConfiguration(), mojoParameters, log);\n                final Map<File, AssemblyFiles.Entry> files = assemblyFiles\n                        .getUpdatedEntriesAndRefresh().stream()\n                        .collect(Collectors.toMap(AssemblyFiles.Entry::getDestFile, Function.identity(), (oldV, newV) -> newV));\n                JibServiceUtil.copyToContainer(\n                        containerBuilder, buildDirs.getOutputDirectory(), buildDirs.getOutputDirectory().getAbsolutePath(), files);\n            }\n\n            JibServiceUtil.buildContainer(containerBuilder,\n                    TarImage.at(dockerTarArchive.toPath()).named(imageConfig.getName()), log);\n            log.info(\" %s successfully built\", dockerTarArchive.getAbsolutePath());\n        } catch (Exception ex) {\n            throw new MojoExecutionException(\"Error when building JIB image\", ex);\n        }\n    }\n\n    public void push(Collection<ImageConfiguration> imageConfigs, int retries, RegistryService.RegistryConfig registryConfig, boolean skipTag) throws MojoExecutionException {\n        try {\n            for (ImageConfiguration imageConfiguration : imageConfigs) {\n                prependRegistry(imageConfiguration, registryConfig.getRegistry());\n                log.info(\"This push refers to: %s\", imageConfiguration.getName());\n                JibServiceUtil.jibPush(\n                        imageConfiguration,\n                        getRegistryCredentials(registryConfig, true, imageConfiguration, log),\n                        getBuildTarArchive(imageConfiguration, mojoParameters),\n                        log\n                );\n            }\n        } catch (Exception ex) {\n            throw new MojoExecutionException(\"Error when push JIB image\", ex);\n        }\n    }\n\n    static ImageConfiguration prependRegistry(ImageConfiguration imageConfiguration, String registry) {\n        ImageName imageName = new ImageName(imageConfiguration.getName());\n        if (!imageName.hasRegistry() && registry != null) {\n            imageConfiguration.setName(registry + \"/\" + imageConfiguration.getName());\n            imageConfiguration.setRegistry(registry);\n        }\n        return imageConfiguration;\n    }\n\n    static File getAssemblyTarArchive(ImageConfiguration imageConfig, ServiceHub serviceHub, MojoParameters configuration, Logger log) throws MojoExecutionException {\n        log.info(\"Preparing assembly files\");\n        final String targetImage = imageConfig.getName();\n        return serviceHub\n                .getDockerAssemblyManager()\n                .createDockerTarArchive(targetImage, configuration, imageConfig.getBuildConfiguration(), log, null);\n    }\n\n    static Credential getRegistryCredentials(\n            RegistryService.RegistryConfig registryConfig, boolean isPush, ImageConfiguration imageConfiguration, Logger log)\n            throws MojoExecutionException {\n\n        String registry;\n        if (isPush) {\n            registry = EnvUtil.firstRegistryOf(\n                    new ImageName(imageConfiguration.getName()).getRegistry(),\n                    imageConfiguration.getRegistry(),\n                    registryConfig.getRegistry()\n            );\n        } else {\n            registry = EnvUtil.firstRegistryOf(\n                    new ImageName(getBaseImage(imageConfiguration)).getRegistry(),\n                    registryConfig.getRegistry()\n            );\n        }\n        if (registry == null || DEFAULT_DOCKER_REGISTRIES.contains(registry)) {\n            registry = DOCKER_LOGIN_DEFAULT_REGISTRY; // Let's assume docker is default registry.\n        }\n\n        AuthConfigFactory authConfigFactory = registryConfig.getAuthConfigFactory();\n        AuthConfig standardAuthConfig = authConfigFactory.createAuthConfig(isPush, registryConfig.isSkipExtendedAuth(), registryConfig.getAuthConfig(), registryConfig.getSettings(), null, registry);\n        Credential credentials = null;\n        if (standardAuthConfig != null) {\n            credentials = Credential.from(standardAuthConfig.getUsername(), standardAuthConfig.getPassword());\n        }\n        return credentials;\n    }\n\n    static File getBuildTarArchive(ImageConfiguration imageConfiguration, MojoParameters mojoParameters) {\n        BuildDirs buildDirs = new BuildDirs(imageConfiguration.getName(), mojoParameters);\n        return new File(buildDirs.getTemporaryRootDirectory(), ARCHIVE_FILE_NAME + \".\" + ArchiveCompression.none.getFileSuffix());\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/MojoExecutionService.java",
    "content": "package io.fabric8.maven.docker.service;\n\n/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.model.Plugin;\nimport org.apache.maven.plugin.*;\nimport org.apache.maven.plugin.descriptor.MojoDescriptor;\nimport org.apache.maven.plugin.descriptor.PluginDescriptor;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.configuration.PlexusConfiguration;\nimport org.codehaus.plexus.util.StringUtils;\nimport org.codehaus.plexus.util.xml.Xpp3Dom;\nimport org.eclipse.aether.RepositorySystemSession;\n\n/**\n * A service for executing goals on configured plugins.\n *\n * Inspired by and partly reused from\n * https://github.com/TimMoore/mojo-executor but adapted to newer Maven versions.\n *\n * @author roland\n * @since 01/07/15\n */\npublic class MojoExecutionService {\n\n    private final MavenProject project;\n\n    private final MavenSession session;\n\n    private final BuildPluginManager pluginManager;\n\n    MojoExecutionService(MavenProject project, MavenSession session, BuildPluginManager pluginManager) {\n        this.project = project;\n        this.session = session;\n        this.pluginManager = pluginManager;\n    }\n\n    // Call another goal after restart has finished\n    public void callPluginGoal(String fullGoal) throws MojoFailureException, MojoExecutionException {\n        String[] parts = splitGoalSpec(fullGoal);\n        Plugin plugin = project.getPlugin(parts[0]);\n        String goal = parts[1];\n\n        if (plugin == null) {\n            throw new MojoFailureException(\"No goal \" + fullGoal + \" found in pom.xml\");\n        }\n\n        try {\n            String executionId = null;\n            if (goal != null && goal.length() > 0 && goal.indexOf('#') > -1) {\n                int pos = goal.indexOf('#');\n                executionId = goal.substring(pos + 1);\n                goal = goal.substring(0, pos);\n            }\n\n            PluginDescriptor pluginDescriptor = getPluginDescriptor(project, plugin);\n            MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo(goal);\n            if (mojoDescriptor == null) {\n                throw new MojoExecutionException(\"Could not find goal '\" + goal + \"' in plugin \"\n                                                 + plugin.getGroupId() + \":\"\n                                                 + plugin.getArtifactId() + \":\"\n                                                 + plugin.getVersion());\n            }\n            MojoExecution exec = getMojoExecution(executionId, mojoDescriptor);\n            pluginManager.executeMojo(session, exec);\n        } catch (Exception e) {\n            throw new MojoExecutionException(\"Unable to execute mojo\", e);\n        }\n    }\n\n    private MojoExecution getMojoExecution(String executionId, MojoDescriptor mojoDescriptor) {\n        if (executionId != null) {\n            return new MojoExecution(mojoDescriptor, executionId);\n        } else {\n            return new MojoExecution(mojoDescriptor, toXpp3Dom(mojoDescriptor.getMojoConfiguration()));\n        }\n    }\n\n    PluginDescriptor getPluginDescriptor(MavenProject project, Plugin plugin)\n        throws InvocationTargetException, IllegalAccessException, MojoFailureException {\n\n        try {\n            Method loadPlugin = pluginManager.getClass().getMethod(\"loadPluginDescriptor\",\n                                                            plugin.getClass(),\n                                                            project.getClass(),\n                                                            session.getClass());\n            return (PluginDescriptor) loadPlugin.invoke(pluginManager, plugin, project, session);\n        } catch (NoSuchMethodException exp) {\n            try {\n                // Fallback for older Maven versions\n                RepositorySystemSession repositorySession = session.getRepositorySession();\n                Method loadPlugin = pluginManager.getClass().getMethod(\"loadPlugin\",\n                                                                       plugin.getClass(),\n                                                                       project.getRemotePluginRepositories().getClass(),\n                                                                       repositorySession.getClass());\n                return (PluginDescriptor) loadPlugin.invoke(pluginManager, plugin, project.getRemotePluginRepositories(), repositorySession);\n            } catch (NoSuchMethodException exp2) {\n                throw new MojoFailureException(\"Cannot load plugin descriptor for plugin \" + plugin.getGroupId() + \":\" + plugin.getArtifactId(),exp2);\n            }\n        }\n    }\n\n    private String[] splitGoalSpec(String fullGoal) throws MojoFailureException {\n        String parts[] = StringUtils.split(fullGoal, \":\");\n        if (parts.length != 3) {\n            throw new MojoFailureException(\"Cannot parse \" + fullGoal + \" as a maven plugin goal. \" +\n                                           \"It must be fully qualified as in <groupId>:<artifactId>:<goal>\");\n        }\n        return new String[]{parts[0] + \":\" + parts[1], parts[2]};\n    }\n\n    private Xpp3Dom toXpp3Dom(PlexusConfiguration config) {\n        Xpp3Dom result = new Xpp3Dom(config.getName());\n        result.setValue(config.getValue(null));\n        for (String name : config.getAttributeNames()) {\n            result.setAttribute(name, config.getAttribute(name));\n        }\n        for (PlexusConfiguration child : config.getChildren()) {\n            result.addChild(toXpp3Dom(child));\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/QueryService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.Image;\nimport io.fabric8.maven.docker.model.Network;\n\n/**\n * Query service for getting image and container information from the docker dameon\n *\n */\npublic class QueryService {\n\n    // Access to docker daemon & logger\n    private DockerAccess docker;\n\n    /**\n     * Constructor which gets its dependencies as args)\n     *  @param docker remote access to docker daemon\n     * */\n    public QueryService(DockerAccess docker) {\n        this.docker = docker;\n    }\n\n    /**\n     * Get container by id\n     *\n     * @param containerIdOrName container id or name\n     * @return container found\n     * @throws DockerAccessException if an error occurs or no container with this id or name exists\n     */\n    public Container getMandatoryContainer(String containerIdOrName) throws DockerAccessException {\n        Container container = getContainer(containerIdOrName);\n        if (container == null) {\n            throw new DockerAccessException(\"Cannot find container %s\", containerIdOrName);\n        }\n        return container;\n    }\n\n    /**\n     * Get a container running for a given container name.\n     * @param containerIdOrName name of container to lookup\n     * @return the container found or <code>null</code> if no container is available.\n     * @throws DockerAccessException in case of an remote error\n     */\n    public Container getContainer(final String containerIdOrName) throws DockerAccessException {\n        return docker.getContainer(containerIdOrName);\n    }\n\n    /**\n     * Get a network for a given network name.\n     * @param networkName name of network to lookup\n     * @return the network found or <code>null</code> if no network is available.\n     * @throws DockerAccessException in case of an remote error\n     */\n    public Network getNetworkByName(final String networkName) throws DockerAccessException {\n        for (Network el : docker.listNetworks()) {\n            if (networkName.equals(el.getName())) {\n                return el;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Get all networks.\n     * @return the network found or <code>null</code> if no network is available.\n     * @throws DockerAccessException in case of an remote error\n     */\n    public Set<Network> getNetworks() throws DockerAccessException {\n        return new HashSet<>(docker.listNetworks());\n    }\n\n    /**\n     * Get name for single container when the id is given\n     *\n     * @param containerId id of container to lookup\n     * @return the name of the container\n     * @throws DockerAccessException if access to the docker daemon fails\n     */\n    public String getContainerName(String containerId) throws DockerAccessException {\n        return getMandatoryContainer(containerId).getName();\n    }\n\n    /**\n     * List all containers on the Docker server.\n     *\n     * @param all if true, list stopped containers as well as running containers.\n     *\n     * @return list of <code>Container</code> objects\n     * @throws DockerAccessException if the request fails\n     */\n    public List<Container> listContainers(final boolean all) throws DockerAccessException {\n        return docker.listContainers(all);\n    }\n\n    /**\n     * Get all containers which are build from an image. By default only the last containers are considered but this\n     * can be tuned with a global parameters.\n     *\n     * @param image for which its container are looked up\n     * @param all if true, fetch stopped containers as well as running containers.\n     * @return list of <code>Container</code> objects\n     * @throws DockerAccessException if the request fails\n     */\n    public List<Container> getContainersForImage(final String image, final boolean all) throws DockerAccessException {\n        return docker.getContainersForImage(image, all);\n    }\n\n    /**\n     * Get all images on the Docker server.\n     *\n     * @param all if true, fetch untagged images as well as tagged.\n     *\n     * @return list of <code>Image</code> objects\n     * @throws DockerAccessException if the request fails\n     */\n    public List<Image> listImages(final boolean all) throws DockerAccessException {\n        return docker.listImages(all);\n    }\n\n    /**\n     * Finds the id of an image.\n     *\n     * @param imageName name of the image.\n     * @return the id of the image\n     * @throws DockerAccessException if the request fails\n     */\n    public String getImageId(String imageName) throws DockerAccessException {\n        return docker.getImageId(imageName);\n    }\n\n    /**\n     * Get the id of the latest container started for an image\n     *\n     * @param image for which its container are looked up\n     * @return container or <code>null</code> if no container has been started for this image.\n     * @throws DockerAccessException if the request fails\n     */\n    public Container getLatestContainerForImage(String image) throws DockerAccessException {\n        long newest = 0;\n        Container result = null;\n\n        for (Container container : getContainersForImage(image, false)) {\n            long timestamp = container.getCreated();\n\n            if (timestamp < newest) {\n                continue;\n            }\n\n            newest = timestamp;\n            result = container;\n        }\n\n        return result;\n    }\n\n    /**\n     * Check whether a container with the given name exists\n     *\n     * @param containerName container name\n     * @return true if a container with this name exists, false otherwise.\n     * @throws DockerAccessException\n     */\n    public boolean hasContainer(String containerName) throws DockerAccessException {\n        return getContainer(containerName) != null;\n    }\n\n    /**\n     * Check whether a network with the given name exists\n     *\n     * @param networkName network name\n     * @return true if a network with this name exists, false otherwise.\n     * @throws DockerAccessException\n     */\n\n    public boolean hasNetwork(String networkName) throws DockerAccessException {\n        return getNetworkByName(networkName) != null;\n    }\n\n    /**\n     * Check whether the given Image is locally available.\n     *\n     * @param name name of image to check, the image can contain a tag, otherwise 'latest' will be appended\n     * @return true if the image is locally available or false if it must be pulled.\n     * @throws DockerAccessException if the request fails\n     */\n    public boolean hasImage(String name) throws DockerAccessException {\n        return docker.hasImage(name);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/RegistryService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.ImagePullPolicy;\nimport io.fabric8.maven.docker.util.AuthConfigFactory;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.settings.Settings;\n\n/**\n * Allows to interact with registries, eg. to push/pull images.\n */\npublic class RegistryService {\n\n    private final DockerAccess docker;\n    private final Logger log;\n\n    RegistryService(DockerAccess docker, Logger log) {\n        this.docker = docker;\n        this.log = log;\n    }\n\n    /**\n     * Push a set of images to a registry\n     *\n     * @param imageConfigs images to push (but only if they have a build configuration)\n     * @param retries how often to retry\n     * @param registryConfig a global registry configuration\n     * @param skipTag flag to skip pushing tagged images\n     * @throws DockerAccessException\n     * @throws MojoExecutionException\n     */\n    public void pushImages(Collection<ImageConfiguration> imageConfigs,\n                           int retries, RegistryConfig registryConfig, boolean skipTag) throws DockerAccessException, MojoExecutionException {\n        for (ImageConfiguration imageConfig : imageConfigs) {\n            BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();\n            if (buildConfig.skipPush()) {\n                log.info(\"%s : Skipped pushing\", imageConfig.getDescription());\n                continue;\n            }\n\n            String name = imageConfig.getName();\n            if (buildConfig != null) {\n                String configuredRegistry = EnvUtil.firstRegistryOf(\n                    new ImageName(imageConfig.getName()).getRegistry(),\n                    imageConfig.getRegistry(),\n                    registryConfig.getRegistry());\n\n\n                AuthConfig authConfig = createAuthConfig(true, new ImageName(name).getUser(), configuredRegistry, registryConfig);\n\n                long start = System.currentTimeMillis();\n                docker.pushImage(name, authConfig, configuredRegistry, retries);\n                log.info(\"Pushed %s in %s\", name, EnvUtil.formatDurationTill(start));\n\n                if (!skipTag) {\n                    for (String tag : imageConfig.getBuildConfiguration().getTags()) {\n                        if (tag != null) {\n                            docker.pushImage(new ImageName(name, tag).getFullName(), authConfig, configuredRegistry, retries);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n\n    /**\n     * Check an image, and, if <code>autoPull</code> is set to true, fetch it. Otherwise if the image\n     * is not existent, throw an error\n     * @param registryConfig registry configuration\n     *\n     * @throws DockerAccessException\n     * @throws MojoExecutionException\n     */\n    public void pullImageWithPolicy(String image, ImagePullManager pullManager, RegistryConfig registryConfig, boolean hasImage)\n        throws DockerAccessException, MojoExecutionException {\n\n        // Already pulled, so we don't need to take care\n        if (pullManager.hasAlreadyPulled(image)) {\n            return;\n        }\n\n        // Check if a pull is required\n        if (!imageRequiresPull(hasImage, pullManager.getImagePullPolicy(), image)) {\n            return;\n        }\n\n        ImageName imageName = new ImageName(image);\n        long time = System.currentTimeMillis();\n        String actualRegistry = EnvUtil.firstRegistryOf(\n            imageName.getRegistry(),\n            registryConfig.getRegistry());\n        docker.pullImage(imageName.getFullName(),\n                         createAuthConfig(false, imageName.getUser(), actualRegistry, registryConfig), actualRegistry);\n        log.info(\"Pulled %s in %s\", imageName.getFullName(), EnvUtil.formatDurationTill(time));\n        pullManager.pulled(image);\n\n        if (actualRegistry != null && !imageName.hasRegistry()) {\n            // If coming from a registry which was not contained in the original name, add a tag from the\n            // full name with the registry to the short name with no-registry.\n            docker.tag(imageName.getFullName(actualRegistry), image, false);\n        }\n    }\n\n\n    // ============================================================================================================\n\n\n    private boolean imageRequiresPull(boolean hasImage, ImagePullPolicy pullPolicy, String imageName)\n        throws MojoExecutionException {\n\n        // The logic here is like this (see also #96):\n        // otherwise: don't pull\n\n        if (pullPolicy == ImagePullPolicy.Never) {\n            if (!hasImage) {\n                throw new MojoExecutionException(\n                    String.format(\"No image '%s' found and pull policy 'Never' is set. Please chose another pull policy or pull the image yourself)\", imageName));\n            }\n            return false;\n        }\n\n        // If the image is not available and mode is not ImagePullPolicy.Never --> pull\n        if (!hasImage) {\n            return true;\n        }\n\n        // If pullPolicy == Always --> pull, otherwise not (we have it already)\n        return pullPolicy == ImagePullPolicy.Always;\n    }\n\n    private AuthConfig createAuthConfig(boolean isPush, String user, String registry, RegistryConfig config)\n            throws MojoExecutionException {\n\n        return config.getAuthConfigFactory().createAuthConfig(\n            isPush, config.isSkipExtendedAuth(), config.getAuthConfig(),\n            config.getSettings(), user, registry);\n    }\n\n    // ===========================================\n\n\n    public static class RegistryConfig implements Serializable {\n\n        private String registry;\n\n        private Settings settings;\n\n        private AuthConfigFactory authConfigFactory;\n\n        private boolean skipExtendedAuth;\n\n        private Map authConfig;\n\n        public RegistryConfig() {\n        }\n\n        public String getRegistry() {\n            return registry;\n        }\n\n        public Settings getSettings() {\n            return settings;\n        }\n\n        public AuthConfigFactory getAuthConfigFactory() {\n            return authConfigFactory;\n        }\n\n        public boolean isSkipExtendedAuth() {\n            return skipExtendedAuth;\n        }\n\n        public Map getAuthConfig() {\n            return authConfig;\n        }\n\n        public static class Builder {\n\n            private RegistryConfig context = new RegistryConfig();\n\n            public Builder() {\n                this.context = new RegistryConfig();\n            }\n\n            public Builder(RegistryConfig context) {\n                this.context = context;\n            }\n\n            public Builder registry(String registry) {\n                context.registry = registry;\n                return this;\n            }\n\n            public Builder settings(Settings settings) {\n                context.settings = settings;\n                return this;\n            }\n\n            public Builder authConfigFactory(AuthConfigFactory authConfigFactory) {\n                context.authConfigFactory = authConfigFactory;\n                return this;\n            }\n\n            public Builder skipExtendedAuth(boolean skipExtendedAuth) {\n                context.skipExtendedAuth = skipExtendedAuth;\n                return this;\n            }\n\n            public Builder authConfig(Map authConfig) {\n                context.authConfig = authConfig;\n                return this;\n            }\n\n            public RegistryConfig build() {\n                return context;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/RunService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport static io.fabric8.maven.docker.util.VolumeBindingUtil.resolveRelativeVolumeBindings;\n\n/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\n\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.ContainerHostConfig;\nimport io.fabric8.maven.docker.access.ContainerNetworkingConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.access.NetworkCreateConfig;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.StopMode;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.model.ExecDetails;\nimport io.fabric8.maven.docker.model.Network;\nimport io.fabric8.maven.docker.util.ContainerNamingUtil;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.StartOrderResolver;\nimport io.fabric8.maven.docker.wait.WaitTimeoutException;\nimport io.fabric8.maven.docker.wait.WaitUtil;\n\n\n/**\n * Service class for helping in running containers.\n *\n * @author roland\n * @since 16/06/15\n */\npublic class RunService {\n\n    // logger delegated from top\n    private Logger log;\n\n    // Action to be used when doing a shutdown\n    final private ContainerTracker tracker;\n\n    // DAO for accessing the docker daemon\n    private DockerAccess docker;\n\n    private QueryService queryService;\n\n    private final LogOutputSpecFactory logConfig;\n\n    public RunService(DockerAccess docker,\n                      QueryService queryService,\n                      ContainerTracker tracker,\n                      LogOutputSpecFactory logConfig,\n                      Logger log) {\n        this.docker = docker;\n        this.queryService = queryService;\n        this.tracker = tracker;\n        this.log = log;\n        this.logConfig = logConfig;\n    }\n\n    /**\n     * Create and start a Exec container with the given image configuration.\n     * @param containerId container id to run exec command against\n     * @param command command to execute\n     * @param imageConfiguration configuration of the container's image\n     * @return the exec container id\n     *\n     * @throws DockerAccessException if access to the docker backend fails\n     */\n    public String execInContainer(String containerId, String command, ImageConfiguration imageConfiguration)\n        throws DockerAccessException, ExecException {\n        Arguments arguments = new Arguments();\n        arguments.setExec(Arrays.asList(EnvUtil.splitOnSpaceWithEscape(command)));\n        String execContainerId = docker.createExecContainer(containerId, arguments);\n        docker.startExecContainer(execContainerId, logConfig.createSpec(containerId, imageConfiguration));\n\n        ExecDetails execContainer = docker.getExecContainer(execContainerId);\n        Integer exitCode = execContainer.getExitCode();\n        if (exitCode != null && exitCode != 0) {\n            ContainerDetails container = docker.getContainer(containerId);\n            throw new ExecException(execContainer, container);\n        }\n        return execContainerId;\n    }\n\n    /**\n     * Create and start a container with the given image configuration.\n     * @param imageConfig image configuration holding the run information and the image name\n     * @param portMapping container port mapping\n     * @param gavLabel label to tag the started container with\n     *\n     * @param properties properties to fill in with dynamically assigned ports\n     * @param defaultContainerNamePattern pattern to use for naming containers. Can be null in which case a default pattern is used\n     * @param buildTimestamp date which should be used as the timestamp when calculating container names\n     * @return the container id\n     *\n     * @throws DockerAccessException if access to the docker backend fails\n     */\n    public String createAndStartContainer(ImageConfiguration imageConfig,\n                                          PortMapping portMapping,\n                                          GavLabel gavLabel,\n                                          Properties properties,\n                                          File baseDir,\n                                          String defaultContainerNamePattern,\n                                          Date buildTimestamp) throws DockerAccessException {\n        RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n        String imageName = imageConfig.getName();\n\n        Collection<Container> existingContainers = queryService.getContainersForImage(imageName, true);\n        String containerName = ContainerNamingUtil.formatContainerName(imageConfig, defaultContainerNamePattern, buildTimestamp, existingContainers);\n\n        ContainerCreateConfig config = createContainerConfig(imageName, runConfig, portMapping, gavLabel, properties, baseDir);\n\n        String id = docker.createContainer(config, containerName);\n        startContainer(imageConfig, id, gavLabel);\n\n        if (portMapping.needsPropertiesUpdate()) {\n            updateMappedPortsAndAddresses(id, portMapping);\n        }\n\n        return id;\n    }\n\n    /**\n     * Stop a container immediately by id.\n     * @param containerId the container to stop\n     * @param imageConfig image configuration for this container\n     * @param keepContainer whether to keep container or to remove them after stoppings\n     * @param removeVolumes whether to remove volumes after stopping\n     */\n    public void stopContainer(String containerId,\n                              ImageConfiguration imageConfig,\n                              boolean keepContainer,\n                              boolean removeVolumes)\n        throws DockerAccessException, ExecException {\n        ContainerTracker.ContainerShutdownDescriptor descriptor =\n                new ContainerTracker.ContainerShutdownDescriptor(imageConfig, containerId);\n        shutdown(descriptor, keepContainer, removeVolumes);\n    }\n\n    /**\n     * Lookup up whether a certain has been already started and registered. If so, stop it\n     *\n     * @param containerId the container to stop\n     * @param keepContainer whether to keep container or to remove them after stoppings\n     * @param removeVolumes whether to remove volumes after stopping\n     *\n     * @throws DockerAccessException\n     */\n    public void stopPreviouslyStartedContainer(String containerId,\n                                               boolean keepContainer,\n                                               boolean removeVolumes)\n        throws DockerAccessException, ExecException {\n        ContainerTracker.ContainerShutdownDescriptor descriptor = tracker.removeContainer(containerId);\n        if (descriptor != null) {\n            shutdown(descriptor, keepContainer, removeVolumes);\n        }\n    }\n\n    /**\n     * Stop all registered container\n     * @param keepContainer whether to keep container or to remove them after stopping\n     * @param removeVolumes whether to remove volumes after stopping\n     * @throws DockerAccessException if during stopping of a container sth fails\n     */\n    public void stopStartedContainers(boolean keepContainer,\n                                      boolean removeVolumes,\n                                      boolean removeCustomNetworks,\n                                      GavLabel gavLabel)\n        throws DockerAccessException, ExecException {\n\t\tList<DockerAccessException> thrownExceptions = new ArrayList<>();\n        Set<Network> networksToRemove = new HashSet<>();\n        for (ContainerTracker.ContainerShutdownDescriptor descriptor : tracker.removeShutdownDescriptors(gavLabel)) {\n\t\t\ttry {\n\t\t\t\tcollectCustomNetworks(networksToRemove, descriptor, removeCustomNetworks);\n\t\t\t\tshutdown(descriptor, keepContainer, removeVolumes);\n\t\t\t} catch (DockerAccessException exc) {\n\t\t\t\tthrownExceptions.add(exc);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tremoveCustomNetworks(networksToRemove);\n\t\t} catch (DockerAccessException exc) {\n\t\t\tthrownExceptions.add(exc);\n        }\n\t\tif (!thrownExceptions.isEmpty()) {\n\t\t\tDockerAccessException exception = new DockerAccessException(\"At least one exception thrown during container removal.\");\n\t\t\tfor (DockerAccessException dae : thrownExceptions) {\n\t\t\t\texception.addSuppressed(dae);\n\t\t\t}\n\t\t\tthrow exception;\n\t\t}\n    }\n\n    private void collectCustomNetworks(Set<Network> networksToRemove, ContainerTracker.ContainerShutdownDescriptor descriptor, boolean removeCustomNetworks) throws DockerAccessException {\n        final NetworkConfig config = descriptor.getImageConfiguration().getRunConfiguration().getNetworkingConfig();\n        if (removeCustomNetworks && config.isCustomNetwork()) {\n           networksToRemove.add(queryService.getNetworkByName(config.getCustomNetwork()));\n        }\n    }\n\n    /**\n     * Lookup a container that has been started\n     *\n     * @param lookup a container by id or alias\n     * @return the container id if the container exists, <code>null</code> otherwise.\n     */\n    public String lookupContainer(String lookup) {\n        return tracker.lookupContainer(lookup);\n    }\n\n    /**\n     * Get the proper order for images to start\n     * @param images list of images for which the order should be created\n     * @return list of images in the right startup order\n     */\n    public List<StartOrderResolver.Resolvable> getImagesConfigsInOrder(QueryService queryService, List<ImageConfiguration> images) {\n        return StartOrderResolver.resolve(queryService, convertToResolvables(images));\n    }\n\n    /**\n     * Create port mapping for a specific configuration as it can be used when creating containers\n     *\n     * @param runConfig the cun configuration\n     * @param properties properties to lookup variables\n     * @return the portmapping\n     */\n    public PortMapping createPortMapping(RunImageConfiguration runConfig, Properties properties) {\n        try {\n            return new PortMapping(runConfig.getPorts(), properties);\n        } catch (IllegalArgumentException exp) {\n            throw new IllegalArgumentException(\"Cannot parse port mapping\", exp);\n        }\n    }\n\n    /**\n     * Add a shutdown hook in order to stop all registered containers\n     */\n    public void addShutdownHookForStoppingContainers(final boolean keepContainer, final boolean removeVolumes, final boolean removeCustomNetworks) {\n        Runtime.getRuntime().addShutdownHook(new Thread() {\n            @Override\n            public void run() {\n                try {\n                    stopStartedContainers(keepContainer, removeVolumes, removeCustomNetworks, null);\n                } catch (DockerAccessException | ExecException e) {\n                    log.error(\"Error while stopping containers: %s\", e.getMessage());\n                }\n            }\n        });\n    }\n\n    private List<StartOrderResolver.Resolvable> convertToResolvables(List<ImageConfiguration> images) {\n        List<StartOrderResolver.Resolvable> ret = new ArrayList<>();\n        for (ImageConfiguration config : images) {\n            if (config.getRunConfiguration().skip()) {\n                log.info(\"%s: Skipped running\", config.getDescription());\n            } else {\n                ret.add(config);\n            }\n        }\n        return ret;\n    }\n\n    // visible for testing\n    ContainerCreateConfig createContainerConfig(String imageName, RunImageConfiguration runConfig, PortMapping mappedPorts,\n                                                GavLabel gavLabel, Properties mavenProps, File baseDir)\n            throws DockerAccessException {\n        try {\n            ContainerCreateConfig config = new ContainerCreateConfig(imageName)\n                    .hostname(runConfig.getHostname())\n                    .domainname(runConfig.getDomainname())\n                    .user(runConfig.getUser())\n                    .workingDir(runConfig.getWorkingDir())\n                    .entrypoint(runConfig.getEntrypoint())\n                    .exposedPorts(mappedPorts.getContainerPorts())\n                    .environment(runConfig.getEnvPropertyFile(), runConfig.getEnv(), mavenProps)\n                    .labels(mergeLabels(runConfig.getLabels(), gavLabel))\n                    .command(runConfig.getCmd())\n                    .hostConfig(createContainerHostConfig(runConfig, mappedPorts, baseDir));\n            RunVolumeConfiguration volumeConfig = runConfig.getVolumeConfiguration();\n            if (volumeConfig != null) {\n                resolveRelativeVolumeBindings(baseDir, volumeConfig);\n                config.binds(volumeConfig.getBind());\n            }\n\n            NetworkConfig networkConfig = runConfig.getNetworkingConfig();\n            if(networkConfig.isCustomNetwork() && networkConfig.hasAliases()) {\n                ContainerNetworkingConfig networkingConfig =\n                    new ContainerNetworkingConfig()\n                        .aliases(networkConfig);\n                config.networkingConfig(networkingConfig);\n            }\n\n            return config;\n        } catch (IllegalArgumentException e) {\n            throw new IllegalArgumentException(String.format(\"Failed to create contained configuration for [%s]\", imageName), e);\n        }\n    }\n\n    private Map<String, String> mergeLabels(Map<String, String> labels, GavLabel runIdLabel) {\n        Map<String,String> ret = new HashMap<>();\n        if (labels != null) {\n            ret.putAll(labels);\n        }\n        if (runIdLabel != null) {\n            ret.put(runIdLabel.getKey(), runIdLabel.getValue());\n        }\n        return ret;\n    }\n\n    ContainerHostConfig createContainerHostConfig(RunImageConfiguration runConfig, PortMapping mappedPorts, File baseDir)\n            throws DockerAccessException {\n        RestartPolicy restartPolicy = runConfig.getRestartPolicy();\n\n\n        List<String> links = findContainerIdsForLinks(runConfig.getLinks(),\n                                                      runConfig.getNetworkingConfig().isCustomNetwork());\n\n        ContainerHostConfig config = new ContainerHostConfig()\n                .extraHosts(runConfig.getExtraHosts())\n                .links(links)\n                .portBindings(mappedPorts)\n                .privileged(runConfig.getPrivileged())\n                .shmSize(runConfig.getShmSize())\n                .dns(runConfig.getDns())\n                .dnsSearch(runConfig.getDnsSearch())\n                .capAdd(runConfig.getCapAdd())\n                .capDrop(runConfig.getCapDrop())\n                .securityOpts(runConfig.getSecurityOpts())\n                .memory(runConfig.getMemory())\n                .memorySwap(runConfig.getMemorySwap())\n                .restartPolicy(restartPolicy.getName(), restartPolicy.getRetry())\n                .logConfig(runConfig.getLogConfiguration())\n                .tmpfs(runConfig.getTmpfs())\n                .ulimits(runConfig.getUlimits())\n                .cpuShares(runConfig.getCpuShares())\n                .cpus(runConfig.getCpus())\n                .cpuSet(runConfig.getCpuSet())\n                .readonlyRootfs(runConfig.getReadOnly())\n                .autoRemove(runConfig.getAutoRemove());\n\n        addVolumeConfig(config, runConfig, baseDir);\n        addNetworkingConfig(config, runConfig);\n\n        return config;\n    }\n\n    private void addNetworkingConfig(ContainerHostConfig config, RunImageConfiguration runConfig) throws DockerAccessException {\n        NetworkConfig networkConfig = runConfig.getNetworkingConfig();\n        if (networkConfig.isStandardNetwork()) {\n            String alias = networkConfig.getContainerAlias();\n            String containerId = alias != null ? findContainerId(alias, false) : null;\n            config.networkMode(networkConfig.getStandardMode(containerId));\n        } else if (networkConfig.isCustomNetwork()) {\n            config.networkMode(networkConfig.getCustomNetwork());\n        }\n    }\n\n    private void addVolumeConfig(ContainerHostConfig config, RunImageConfiguration runConfig, File baseDir) throws DockerAccessException {\n        RunVolumeConfiguration volConfig = runConfig.getVolumeConfiguration();\n        if (volConfig != null) {\n            resolveRelativeVolumeBindings(baseDir, volConfig);\n            config.binds(volConfig.getBind())\n                  .volumesFrom(findVolumesFromContainers(volConfig.getFrom()));\n        }\n    }\n\n    private List<String> findContainerIdsForLinks(List<String> links, boolean leaveUnresolvedIfNotFound) throws DockerAccessException {\n        List<String> ret = new ArrayList<>();\n        for (String[] link : EnvUtil.splitOnLastColon(links)) {\n            String id = findContainerId(link[0], false);\n            if (id != null) {\n                ret.add(queryService.getContainerName(id) + \":\" + link[1]);\n            } else if (leaveUnresolvedIfNotFound) {\n                ret.add(link[0] + \":\" + link[1]);\n            } else {\n                throw new DockerAccessException(\"No container found for image/alias '%s', unable to link\", link[0]);\n            }\n        }\n        return ret.size() != 0 ? ret : null;\n    }\n\n    // visible for testing\n    private List<String> findVolumesFromContainers(List<String> images) throws DockerAccessException {\n        List<String> list = new ArrayList<>();\n        if (images != null) {\n            for (String image : images) {\n                String id = findContainerId(image, true);\n                if (id == null) {\n                    throw new DockerAccessException(\"No container found for image/alias '%s', unable to mount volumes\", image);\n                }\n\n                list.add(queryService.getContainerName(id));\n            }\n        }\n        return list;\n    }\n\n\n    // checkAllContainers: false = only running containers are considered\n    private String findContainerId(String imageNameOrAlias, boolean checkAllContainers) throws DockerAccessException {\n        String id = lookupContainer(imageNameOrAlias);\n\n        // check for external container. The image name is interpreted as a *container name* for that case ...\n        if (id == null) {\n            Container container = queryService.getContainer(imageNameOrAlias);\n            if (container != null && (checkAllContainers || container.isRunning())) {\n                id = container.getId();\n            }\n        }\n        return id;\n    }\n\n    private void startContainer(ImageConfiguration imageConfig, String id, GavLabel gavLabel) throws DockerAccessException {\n        log.info(\"%s: Start container %s\",imageConfig.getDescription(), id);\n        docker.startContainer(id);\n        tracker.registerContainer(id, imageConfig, gavLabel);\n    }\n\n    private void updateMappedPortsAndAddresses(String containerId, PortMapping mappedPorts) throws DockerAccessException {\n        Container container = queryService.getMandatoryContainer(containerId);\n        if (container.isRunning()) {\n            mappedPorts.updateProperties(container.getPortBindings());\n        } else {\n            log.warn(\"Container %s is not running anymore, can not extract dynamic ports\",containerId);\n        }\n    }\n\n    private void shutdown(ContainerTracker.ContainerShutdownDescriptor descriptor, boolean keepContainer, boolean removeVolumes)\n        throws DockerAccessException, ExecException {\n\n        String containerId = descriptor.getContainerId();\n        StopMode stopMode = descriptor.getStopMode();\n        if (descriptor.getPreStop() != null) {\n            try {\n                execInContainer(containerId, descriptor.getPreStop(), descriptor.getImageConfiguration());\n            } catch (DockerAccessException e) {\n                log.error(\"%s\", e.getMessage());\n            } catch (ExecException e) {\n                if (descriptor.isBreakOnError()) {\n                    throw e;\n                } else {\n                    log.warn(\"Cannot run preStop: %s\", e.getMessage());\n                }\n            }\n        }\n\n        if (stopMode.equals(StopMode.graceful)) {\n            int killGracePeriod = adjustGracePeriod(descriptor.getKillGracePeriod());\n            log.debug(\"shutdown will wait max of %d seconds before removing container\", killGracePeriod);\n\n            long waited;\n            if (killGracePeriod == 0) {\n                docker.stopContainer(containerId, 0);\n                waited = 0;\n            } else {\n                waited = shutdownAndWait(containerId, killGracePeriod);\n            }\n            log.info(\"%s: Stop%s container %s after %s ms\",\n                    descriptor.getDescription(),\n                    (keepContainer ? \"\" : \" and removed\"),\n                    containerId.substring(0, 12), waited);\n        } else if (stopMode.equals(StopMode.kill)) {\n            docker.killContainer(containerId);\n            log.info(\"%s: Killed%s container %s.\",\n                    descriptor.getDescription(),\n                    (keepContainer ? \"\" : \" and removed\"),\n                    containerId.subSequence(0, 12));\n        }\n        if (!keepContainer) {\n            removeContainer(descriptor, removeVolumes, containerId);\n        }\n\n\n    }\n\n    public void createCustomNetworkIfNotExistant(String customNetwork) throws DockerAccessException {\n        if (!queryService.hasNetwork(customNetwork)) {\n            docker.createNetwork(new NetworkCreateConfig(customNetwork));\n        } else {\n            log.debug(\"Custom Network \" + customNetwork + \" found\");\n        }\n    }\n\n    public void removeCustomNetworks(Collection<Network> networks) throws DockerAccessException {\n\t\tList<DockerAccessException> thrownExceptions = new ArrayList<>();\n        for (Network network : networks) {\n\t\t\ttry {\n\t\t\t\tdocker.removeNetwork(network.getId());\n\t\t\t} catch (DockerAccessException exc) {\n\t\t\t\tthrownExceptions.add(exc);\n\t\t\t}\n\t\t}\n\t\tif (!thrownExceptions.isEmpty()) {\n\t\t\tDockerAccessException exception = new DockerAccessException(\"At least one exception was thrown while trying to remove custom networks.\");\n\t\t\tfor (DockerAccessException dae : thrownExceptions) {\n\t\t\t\texception.addSuppressed(dae);\n\t\t\t}\n\t\t\tthrow exception;\n        }\n    }\n\n    private int adjustGracePeriod(int gracePeriod) {\n        int killGracePeriodInSeconds = (gracePeriod + 500) / 1000;\n        if (gracePeriod != 0 && killGracePeriodInSeconds == 0) {\n            log.warn(\"A kill grace period of %d ms leads to no wait at all since its rounded to seconds. \" +\n                     \"Please use at least 500 as value for wait.kill\", gracePeriod);\n        }\n\n        return killGracePeriodInSeconds;\n    }\n\n    private void removeContainer(ContainerTracker.ContainerShutdownDescriptor descriptor, boolean removeVolumes, String containerId)\n        throws DockerAccessException {\n        int shutdownGracePeriod = descriptor.getShutdownGracePeriod();\n        if (shutdownGracePeriod != 0) {\n            log.debug(\"Shutdown: Wait %d ms before removing container\", shutdownGracePeriod);\n            WaitUtil.sleep(shutdownGracePeriod);\n        }\n        // Remove the container\n        docker.removeContainer(containerId, removeVolumes);\n    }\n\n    private long shutdownAndWait(final String containerId, final int killGracePeriodInSeconds) throws DockerAccessException {\n        long waited;\n        try {\n            waited = WaitUtil.wait(killGracePeriodInSeconds, new Callable<Void>() {\n                @Override\n                public Void call() throws Exception {\n                    docker.stopContainer(containerId, killGracePeriodInSeconds);\n                    return null;\n                }\n            });\n        } catch (ExecutionException e) {\n            if (e.getCause() instanceof DockerAccessException) {\n                throw (DockerAccessException) e.getCause();\n            } else {\n                throw new DockerAccessException(e, \"failed to stop container id [%s]\", containerId);\n            }\n        } catch (WaitTimeoutException e) {\n            waited = e.getWaited();\n            log.warn(\"Stop container id [%s] timed out after %s ms\", containerId, waited);\n        }\n\n        return waited;\n    }\n\n    /**\n     * Creates a Volume if a volume is referred to during startup in bind mount mapping and\n     * a VolumeConfiguration exists\n     *\n     * @param hub Service hub\n     * @param binds volume binds present in ImageConfiguration\n     * @param volumes VolumeConfigs present\n     * @return List of volumes created\n     * @throws DockerAccessException\n     */\n    public List<String> createVolumesAsPerVolumeBinds(ServiceHub hub, List<String> binds, List<VolumeConfiguration> volumes)\n            throws DockerAccessException {\n\n        Map<String, Integer> indexMap = new HashMap<>();\n        List<String> volumesCreated = new ArrayList<>();\n\n        for (int index = 0; index < volumes.size(); index++) {\n            indexMap.put(volumes.get(index).getName(), index);\n        }\n\n        for (String bind : binds) {\n            if (bind.contains(\":\")) {\n                String name = bind.substring(0, bind.indexOf(':'));\n                Integer volumeConfigIndex = indexMap.get(name);\n                if (volumeConfigIndex != null) {\n                    VolumeConfiguration volumeConfig = volumes.get(volumeConfigIndex);\n                    hub.getVolumeService().createVolume(volumeConfig);\n                    volumesCreated.add(volumeConfig.getName());\n                }\n            }\n        }\n\n        return volumesCreated;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/ServiceHub.java",
    "content": "/*\n *\n * Copyright 2015 Roland Huss\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 */\npackage io.fabric8.maven.docker.service;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.BuildPluginManager;\nimport org.apache.maven.project.MavenProject;\n\n/**\n * A service hub responsible for creating and managing services which are used by\n * Mojos for calling to the docker backend. The docker backend (DAO) is injected from the outside.\n *\n * @author roland\n * @since 01/12/15\n */\npublic class ServiceHub {\n\n    private final DockerAccess dockerAccess;\n\n    private final QueryService queryService;\n    private final RunService runService;\n    private final RegistryService registryService;\n    private final BuildService buildService;\n    private final MojoExecutionService mojoExecutionService;\n    private final ArchiveService archiveService;\n    private final VolumeService volumeService;\n    private final WatchService watchService;\n    private final WaitService waitService;\n    private final DockerAssemblyManager dockerAssemblyManager;\n\n    ServiceHub(DockerAccess dockerAccess, ContainerTracker containerTracker, BuildPluginManager pluginManager,\n               DockerAssemblyManager dockerAssemblyManager, MavenProject project, MavenSession session,\n               Logger logger, LogOutputSpecFactory logSpecFactory) {\n\n        this.dockerAccess = dockerAccess;\n        this.dockerAssemblyManager = dockerAssemblyManager;\n\n        mojoExecutionService = new MojoExecutionService(project, session, pluginManager);\n        archiveService = new ArchiveService(dockerAssemblyManager, logger);\n\n        if (dockerAccess != null) {\n            queryService = new QueryService(dockerAccess);\n            registryService = new RegistryService(dockerAccess, logger);\n            runService = new RunService(dockerAccess, queryService, containerTracker, logSpecFactory, logger);\n            buildService = new BuildService(dockerAccess, queryService, registryService, archiveService, logger);\n            volumeService = new VolumeService(dockerAccess);\n            watchService = new WatchService(archiveService, buildService, dockerAccess, mojoExecutionService, queryService, runService, logger);\n            waitService = new WaitService(dockerAccess, queryService, logger);\n        } else {\n            queryService = null;\n            registryService = null;\n            runService = null;\n            buildService = null;\n            volumeService = null;\n            watchService = null;\n            waitService = null;\n        }\n    }\n\n    /**\n     * Get access object for contacting the docker daemon\n     *\n     * @return docker access object\n     */\n    public DockerAccess getDockerAccess() {\n        checkDockerAccessInitialization();\n        return dockerAccess;\n    }\n\n    /**\n     * Service for doing the build against a Docker daemon\n     *\n     * @return get the build service\n     */\n    public BuildService getBuildService() {\n        checkDockerAccessInitialization();\n        return buildService;\n    }\n\n    /**\n     * Get the query service for obtaining information about containers and images\n     *\n     * @return query service\n     */\n    public QueryService getQueryService() {\n        checkDockerAccessInitialization();\n        return queryService;\n    }\n\n    /**\n     * Get the registry service to push/pull images\n     *\n     * @return query service\n     */\n    public RegistryService getRegistryService() {\n        checkDockerAccessInitialization();\n        return registryService;\n    }\n\n\n    /**\n     * The run service is responsible for creating and starting up containers\n     *\n     * @return the run service\n     */\n    public RunService getRunService() {\n        checkDockerAccessInitialization();\n        return runService;\n    }\n\n    /**\n     * The volume service is responsible for creating volumes\n     *\n     * @return the run service\n     */\n    public VolumeService getVolumeService() {\n        checkDockerAccessInitialization();\n        return volumeService;\n    }\n\n    /**\n     * The watch service is responsible for watching container status and rebuilding\n     *\n     * @return the watch service\n     */\n    public WatchService getWatchService() {\n        checkDockerAccessInitialization();\n        return watchService;\n    }\n\n    /**\n     * The wait service is responsible on waiting on container based on several\n     * conditions\n     *\n     * @return the wait service\n     */\n    public WaitService getWaitService() {\n        checkDockerAccessInitialization();\n        return waitService;\n    }\n\n    /**\n     * Serivce for creating archives\n     *\n     * @return the archive service\n     */\n    public ArchiveService getArchiveService() {\n        return archiveService;\n    }\n\n    /**\n     * Get a service for executing goals on other Maven mojos\n     *\n     * @return service for calling other mojos\n     */\n    public MojoExecutionService getMojoExecutionService() {\n        return mojoExecutionService;\n    }\n\n    public DockerAssemblyManager getDockerAssemblyManager() {\n        return dockerAssemblyManager;\n    }\n\n    private synchronized void checkDockerAccessInitialization() {\n        if (dockerAccess == null) {\n            throw new IllegalStateException(\"Service hub created without a docker access to a docker daemon\");\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/ServiceHubFactory.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.util.Logger;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.BuildPluginManager;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.component.annotations.Component;\nimport org.codehaus.plexus.component.annotations.Requirement;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\n\n/**\n * Factory for creating the ServiceHub (i.e. the overall context for performing all services)\n */\n@Component(role = ServiceHubFactory.class, instantiationStrategy = \"singleton\")\npublic class ServiceHubFactory {\n\n    // Track started containers\n    private final ContainerTracker containerTracker = new ContainerTracker();\n\n    @Requirement\n    protected BuildPluginManager pluginManager;\n\n    @Requirement\n    protected DockerAssemblyManager dockerAssemblyManager;\n\n    private LogOutputSpecFactory logOutputSpecFactory;\n\n    public ServiceHub createServiceHub(MavenProject project, MavenSession session, DockerAccess access, Logger log, LogOutputSpecFactory logSpecFactory) {\n        this.logOutputSpecFactory = logSpecFactory;\n        return new ServiceHub(access, containerTracker, pluginManager, dockerAssemblyManager, project, session,\n                              log, logSpecFactory);\n    }\n\n    public LogOutputSpecFactory getLogOutputSpecFactory() {\n        return logOutputSpecFactory;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/VolumeService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.VolumeCreateConfig;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\n\nimport java.lang.String;\n\n/**\n *  Service Class for helping control Volumes\n *\n *  @author Tom Burton\n *  @version Dec 15, 2016\n */\npublic class VolumeService {\n   // DAO for accessing the docker daemon\n   private DockerAccess docker;\n\n   VolumeService(DockerAccess dockerAccess) {\n      this.docker = dockerAccess;\n   }\n\n   public String createVolume(VolumeConfiguration vc) throws DockerAccessException {\n      VolumeCreateConfig config =\n          new VolumeCreateConfig(vc.getName())\n              .driver(vc.getDriver())\n              .opts(vc.getOpts())\n              .labels(vc.getLabels());\n\n      return docker.createVolume(config);\n   }\n\n   public void removeVolume(String volumeName) throws DockerAccessException {\n      docker.removeVolume(volumeName);\n   }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/WaitService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.apache.commons.text.StrSubstitutor;\nimport org.apache.maven.shared.utils.StringUtils;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.log.DefaultLogCallback;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.log.LogOutputSpec;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.wait.ExitCodeChecker;\nimport io.fabric8.maven.docker.wait.HealthCheckChecker;\nimport io.fabric8.maven.docker.wait.HttpPingChecker;\nimport io.fabric8.maven.docker.wait.LogWaitChecker;\nimport io.fabric8.maven.docker.wait.PreconditionFailedException;\nimport io.fabric8.maven.docker.wait.TcpPortChecker;\nimport io.fabric8.maven.docker.wait.WaitChecker;\nimport io.fabric8.maven.docker.wait.WaitTimeoutException;\nimport io.fabric8.maven.docker.wait.WaitUtil;\n\n/**\n * @author roland\n * @since 03.05.17\n */\npublic class WaitService {\n\n    private final QueryService queryService;\n    private DockerAccess dockerAccess;\n    private Logger log;\n\n    public WaitService(DockerAccess dockerAccess, QueryService queryService, Logger log) {\n        this.dockerAccess = dockerAccess;\n        this.log = log;\n        this.queryService = queryService;\n    }\n\n    // ========================================================================================================\n\n    public void wait(ImageConfiguration imageConfig, Properties projectProperties, String containerId) throws IOException {\n        List<WaitChecker> checkers = prepareWaitCheckers(imageConfig, projectProperties, containerId);\n        int timeout = getTimeOut(imageConfig);\n\n        if (checkers.isEmpty()) {\n            if (timeout > 0) {\n                log.info(\"%s: Pausing for %d ms\", imageConfig.getDescription(), timeout);\n                WaitUtil.sleep(timeout);\n            }\n            return;\n        }\n\n        String logLine = extractCheckerLog(checkers);\n        ContainerRunningPrecondition precondition = new ContainerRunningPrecondition(dockerAccess, containerId);\n        try {\n            long waited = WaitUtil.wait(precondition, timeout, checkers);\n            log.info(\"%s: Waited %s %d ms\", imageConfig.getDescription(), logLine, waited);\n        } catch (WaitTimeoutException exp) {\n            String desc = String.format(\"%s: Timeout after %d ms while waiting %s\",\n                                        imageConfig.getDescription(), exp.getWaited(),\n                                        logLine);\n            log.error(desc);\n            throw new IOException(desc);\n        } catch (PreconditionFailedException exp) {\n            String desc = String.format(\"%s: Container stopped with exit code %d unexpectedly after %d ms while waiting %s\",\n                                        imageConfig.getDescription(), precondition.getExitCode(), exp.getWaited(),\n                                        logLine);\n            log.error(desc);\n            throw new IOException(desc);\n        }\n    }\n\n    private int getTimeOut(ImageConfiguration imageConfig) {\n        WaitConfiguration wait = getWaitConfiguration(imageConfig);\n        return wait != null && wait.getTime() != null ? wait.getTime() : 0;\n    }\n\n    private String extractCheckerLog(List<WaitChecker> checkers) {\n        List<String> logOut = new ArrayList<>();\n        for (WaitChecker checker : checkers) {\n            logOut.add(checker.getLogLabel());\n        }\n        return StringUtils.join(logOut.toArray(), \" and \");\n    }\n\n    private List<WaitChecker> prepareWaitCheckers(ImageConfiguration imageConfig, Properties projectProperties, String containerId) throws IOException {\n        WaitConfiguration wait = getWaitConfiguration(imageConfig);\n\n        if (wait == null) {\n            return Collections.emptyList();\n        }\n\n        List<WaitChecker> checkers = new ArrayList<>();\n\n        if (wait.getUrl() != null) {\n            checkers.add(getUrlWaitChecker(imageConfig.getDescription(), projectProperties, wait));\n        }\n\n        if (wait.getLog() != null) {\n            log.debug(\"LogWaitChecker: Waiting on %s\", wait.getLog());\n            checkers.add(new LogWaitChecker(wait.getLog(), dockerAccess, containerId, log));\n        }\n\n        if (wait.getTcp() != null) {\n            try {\n                Container container = queryService.getMandatoryContainer(containerId);\n                checkers.add(getTcpWaitChecker(container, imageConfig.getDescription(), projectProperties, wait.getTcp()));\n            } catch (DockerAccessException e) {\n                throw new IOException(\"Unable to access container \" + containerId, e);\n            }\n        }\n\n        if (Boolean.TRUE.equals(wait.getHealthy())) {\n            checkers.add(new HealthCheckChecker(dockerAccess, containerId, imageConfig.getDescription(), log));\n        }\n\n        if (wait.getExit() != null) {\n            checkers.add(new ExitCodeChecker(wait.getExit(), queryService, containerId));\n        }\n        return checkers;\n    }\n\n    private WaitConfiguration getWaitConfiguration(ImageConfiguration imageConfig) {\n        RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n        return runConfig.getWaitConfiguration();\n    }\n\n    // =================================================================================================================\n\n    private WaitChecker getUrlWaitChecker(String imageConfigDesc,\n                                          Properties projectProperties,\n                                          WaitConfiguration wait) {\n        String waitUrl = StrSubstitutor.replace(wait.getUrl(), projectProperties);\n        WaitConfiguration.HttpConfiguration httpConfig = wait.getHttp();\n        HttpPingChecker checker;\n        if (httpConfig != null) {\n            checker = new HttpPingChecker(waitUrl, httpConfig.getMethod(), httpConfig.getStatus(), httpConfig.isAllowAllHosts());\n            log.info(\"%s: Waiting on url %s with method %s for status %s.\",\n                     imageConfigDesc, waitUrl, httpConfig.getMethod(), httpConfig.getStatus());\n        } else {\n            checker = new HttpPingChecker(waitUrl);\n            log.info(\"%s: Waiting on url %s.\", imageConfigDesc, waitUrl);\n        }\n        return checker;\n    }\n\n    private WaitChecker getTcpWaitChecker(Container container,\n                                          String imageConfigDesc,\n                                          Properties projectProperties,\n                                          WaitConfiguration.TcpConfiguration tcpConfig) {\n        List<Integer> ports = new ArrayList<>();\n\n        List<Integer> portsConfigured = getTcpPorts(tcpConfig);\n        String host = getTcpHost(tcpConfig, projectProperties);\n        WaitConfiguration.TcpConfigMode mode = getTcpMode(tcpConfig, host);\n\n        if (mode == WaitConfiguration.TcpConfigMode.mapped) {\n            for (int port : portsConfigured) {\n                Container.PortBinding binding = container.getPortBindings().get(port + \"/tcp\");\n                if (binding == null) {\n                    throw new IllegalArgumentException(\n                        String.format(\"Cannot watch on port %d, since there is no network binding\", port));\n                }\n                ports.add(binding.getHostPort());\n            }\n            log.info(\"%s: Waiting for mapped ports %s on host %s\", imageConfigDesc, ports, host);\n        } else {\n            final String networkMode = container.getNetworkMode();\n            log.info(\"%s: Network mode: %s\", imageConfigDesc, networkMode);\n            if (networkMode == null || networkMode.isEmpty() || \"default\".equals(networkMode) || \"bridge\".equals(networkMode)) {\n                // Safe mode when network mode is not present\n                host = container.getIPAddress();\n            } else if (!\"host\".equals(networkMode)) {\n                // Custom network\n                host = container.getCustomNetworkIpAddresses().get(networkMode);\n            }\n            ports = portsConfigured;\n            log.info(\"%s: Waiting for ports %s directly on container with IP (%s).\",\n                     imageConfigDesc, ports, host);\n        }\n        return new TcpPortChecker(host, ports);\n    }\n\n    private List<Integer> getTcpPorts(WaitConfiguration.TcpConfiguration tcpConfig) {\n        List<Integer> portsConfigured = tcpConfig.getPorts();\n        if (portsConfigured == null || portsConfigured.isEmpty()) {\n            throw new IllegalArgumentException(\"TCP wait config given but no ports to wait on\");\n        }\n        return portsConfigured;\n    }\n\n    private WaitConfiguration.TcpConfigMode getTcpMode(WaitConfiguration.TcpConfiguration tcpConfig, String host) {\n        WaitConfiguration.TcpConfigMode mode = tcpConfig.getMode();\n        if (mode == null) {\n            return \"localhost\".equals(host) ? WaitConfiguration.TcpConfigMode.direct : WaitConfiguration.TcpConfigMode.mapped;\n        } else {\n            return mode;\n        }\n    }\n\n    private String getTcpHost(WaitConfiguration.TcpConfiguration tcpConfig, Properties projectProperties) {\n        String host = tcpConfig.getHost();\n        if (host == null) {\n            // Host defaults to ${docker.host.address}.\n            host = projectProperties.getProperty(\"docker.host.address\");\n        }\n        return host;\n    }\n\n    private class ContainerRunningPrecondition implements WaitUtil.Precondition {\n        private final String containerId;\n        private final DockerAccess dockerAccess;\n        private Integer exitCode;\n\n        ContainerRunningPrecondition(DockerAccess dockerAccess, String containerId) {\n            this.dockerAccess = dockerAccess;\n            this.containerId = containerId;\n        }\n\n        @Override\n        public boolean isOk() {\n            try {\n                exitCode = dockerAccess.getContainer(containerId).getExitCode();\n                return exitCode == null;\n            } catch (DockerAccessException e) {\n                return false;\n            }\n        }\n\n        @Override\n        public void cleanup() {\n            if (exitCode != null && log.isVerboseEnabled()) {\n                // if not running, probably something went wrong during startup: spit out logs\n                new LogDispatcher(dockerAccess).fetchContainerLog(containerId, LogOutputSpec.DEFAULT);\n                dockerAccess.getLogSync(containerId, new DefaultLogCallback(\n                    new LogOutputSpec.Builder()\n                        .color(\"black\", true)\n                        .prefix(containerId.substring(0, 6))\n                        .useColor(true)\n                        .logStdout(true)\n                        .build()\n                ));\n\n            }\n        }\n\n        Integer getExitCode() {\n            return exitCode;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/WatchService.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.assembly.AssemblyFiles;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.WatchImageConfiguration;\nimport io.fabric8.maven.docker.config.WatchMode;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.service.helper.StartContainerExecutor;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport io.fabric8.maven.docker.util.StartOrderResolver;\nimport io.fabric8.maven.docker.util.Task;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * Watch service for monitoring changes and restarting containers\n */\npublic class WatchService {\n\n    private final ArchiveService archiveService;\n    private final BuildService buildService;\n    private final DockerAccess dockerAccess;\n    private final MojoExecutionService mojoExecutionService;\n    private final QueryService queryService;\n    private final RunService runService;\n    private final Logger log;\n\n    public WatchService(ArchiveService archiveService, BuildService buildService, DockerAccess dockerAccess, MojoExecutionService mojoExecutionService, QueryService queryService, RunService\n            runService, Logger log) {\n        this.archiveService = archiveService;\n        this.buildService = buildService;\n        this.dockerAccess = dockerAccess;\n        this.mojoExecutionService = mojoExecutionService;\n        this.queryService = queryService;\n        this.runService = runService;\n        this.log = log;\n    }\n\n    public synchronized void watch(WatchContext context, BuildService.BuildContext buildContext, List<ImageConfiguration> images) throws DockerAccessException,\n            MojoExecutionException {\n\n        // Important to be be a single threaded scheduler since watch jobs must run serialized\n        ScheduledExecutorService executor = null;\n        try {\n            executor = Executors.newSingleThreadScheduledExecutor();\n\n            for (StartOrderResolver.Resolvable resolvable : runService.getImagesConfigsInOrder(queryService, images)) {\n                final ImageConfiguration imageConfig = (ImageConfiguration) resolvable;\n\n                String imageId = queryService.getImageId(imageConfig.getName());\n                String containerId = runService.lookupContainer(imageConfig.getName());\n\n                ImageWatcher watcher = new ImageWatcher(imageConfig, context, imageId, containerId);\n                long interval = watcher.getInterval();\n\n                WatchMode watchMode = watcher.getWatchMode(imageConfig);\n                log.info(\"Watching \" + imageConfig.getName() + (watchMode != null ? \" using \" + watchMode.getDescription() : \"\"));\n\n                ArrayList<String> tasks = new ArrayList<>();\n\n                if (imageConfig.getBuildConfiguration() != null &&\n                        imageConfig.getBuildConfiguration().getAssemblyConfiguration() != null) {\n                    if (watcher.isCopy()) {\n                        String containerBaseDir = imageConfig.getBuildConfiguration().getAssemblyConfiguration().getTargetDir();\n                        schedule(executor, createCopyWatchTask(watcher, context.getMojoParameters(), containerBaseDir), interval);\n                        tasks.add(\"copying artifacts\");\n                    }\n\n                    if (watcher.isBuild()) {\n                        schedule(executor, createBuildWatchTask(watcher, context.getMojoParameters(), watchMode == WatchMode.both, buildContext), interval);\n                        tasks.add(\"rebuilding\");\n                    }\n                }\n\n                if (watcher.isRun() && watcher.getContainerId() != null) {\n                    schedule(executor, createRestartWatchTask(watcher), interval);\n                    tasks.add(\"restarting\");\n                }\n\n                if (tasks.size() > 0) {\n                    log.info(\"%s: Watch for %s\", imageConfig.getDescription(), StringUtils.join(tasks.toArray(), \" and \"));\n                }\n            }\n            log.info(\"Waiting ...\");\n            if (!context.isKeepRunning()) {\n                runService.addShutdownHookForStoppingContainers(context.isKeepContainer(), context.isRemoveVolumes(), context.isAutoCreateCustomNetworks());\n            }\n            wait();\n        } catch (InterruptedException e) {\n            log.warn(\"Interrupted\");\n        } finally {\n            if (executor != null) {\n                executor.shutdownNow();\n            }\n        }\n    }\n\n    private void schedule(ScheduledExecutorService executor, Runnable runnable, long interval) {\n        executor.scheduleAtFixedRate(runnable, 0, interval, TimeUnit.MILLISECONDS);\n    }\n\n    private Runnable createCopyWatchTask(final ImageWatcher watcher,\n                                         final MojoParameters mojoParameters, final String containerBaseDir) throws MojoExecutionException {\n        final ImageConfiguration imageConfig = watcher.getImageConfiguration();\n\n        final AssemblyFiles files = archiveService.getAssemblyFiles(imageConfig, mojoParameters);\n        return new Runnable() {\n            @Override\n            public void run() {\n                List<AssemblyFiles.Entry> entries = files.getUpdatedEntriesAndRefresh();\n                if (entries != null && entries.size() > 0) {\n                    try {\n                        log.info(\"%s: Assembly changed. Copying changed files to container ...\", imageConfig.getDescription());\n\n                        File changedFilesArchive = archiveService.createChangedFilesArchive(entries, files.getAssemblyDirectory(),\n                                imageConfig.getName(), mojoParameters);\n                        dockerAccess.copyArchive(watcher.getContainerId(), changedFilesArchive, containerBaseDir);\n                        callPostExec(watcher);\n                    } catch (MojoExecutionException | IOException | ExecException e) {\n                        log.error(\"%s: Error when copying files to container %s: %s\",\n                                  imageConfig.getDescription(), watcher.getContainerId(), e.getMessage());\n                    }\n                }\n            }\n        };\n    }\n\n    private void callPostExec(ImageWatcher watcher) throws DockerAccessException, ExecException {\n        if (watcher.getPostExec() != null) {\n            String containerId = watcher.getContainerId();\n            runService.execInContainer(containerId, watcher.getPostExec(), watcher.getImageConfiguration());\n        }\n    }\n\n    private Runnable createBuildWatchTask(final ImageWatcher watcher,\n                                          final MojoParameters mojoParameters, final boolean doRestart, final BuildService.BuildContext buildContext)\n            throws MojoExecutionException {\n        final ImageConfiguration imageConfig = watcher.getImageConfiguration();\n        final AssemblyFiles files = archiveService.getAssemblyFiles(imageConfig, mojoParameters);\n        if (files.isEmpty()) {\n            log.error(\"No assembly files for %s. Are you sure you invoked together with the `package` goal?\", imageConfig.getDescription());\n            throw new MojoExecutionException(\"No files to watch found for \" + imageConfig);\n        }\n\n        return new Runnable() {\n            @Override\n            public void run() {\n                List<AssemblyFiles.Entry> entries = files.getUpdatedEntriesAndRefresh();\n                if (entries != null && entries.size() > 0) {\n                    try {\n                        log.info(\"%s: Assembly changed. Rebuild ...\", imageConfig.getDescription());\n\n                        if (watcher.getWatchContext().getImageCustomizer() != null) {\n                            log.info(\"%s: Customizing the image ...\", imageConfig.getDescription());\n                            watcher.getWatchContext().getImageCustomizer().execute(imageConfig);\n                        }\n\n                        buildService.buildImage(imageConfig, null, buildContext, buildService.buildArchive(imageConfig, buildContext, \"false\"));\n\n                        String name = imageConfig.getName();\n                        watcher.setImageId(queryService.getImageId(name));\n                        if (doRestart) {\n                            restartContainer(watcher);\n                        }\n                        callPostGoal(watcher);\n                    } catch (Exception e) {\n                        log.error(\"%s: Error when rebuilding - %s\", imageConfig.getDescription(), e);\n                    }\n                }\n            }\n        };\n    }\n\n    private Runnable createRestartWatchTask(final ImageWatcher watcher)\n            throws DockerAccessException {\n\n        final String imageName = watcher.getImageName();\n\n        return new Runnable() {\n            @Override\n            public void run() {\n\n                try {\n                    String currentImageId = queryService.getImageId(imageName);\n                    String oldValue = watcher.getAndSetImageId(currentImageId);\n                    if (!currentImageId.equals(oldValue)) {\n                        restartContainer(watcher);\n                        callPostGoal(watcher);\n                    }\n                } catch (Exception e) {\n                    log.warn(\"%s: Error when restarting image - %s\", watcher.getImageConfiguration().getDescription(), e);\n                }\n            }\n        };\n    }\n\n    private void restartContainer(ImageWatcher watcher) throws Exception {\n        Task<ImageWatcher> restarter = watcher.getWatchContext().getContainerRestarter();\n        if (restarter == null) {\n            restarter = defaultContainerRestartTask();\n        }\n\n        // Restart\n        restarter.execute(watcher);\n    }\n\n    private Task<ImageWatcher> defaultContainerRestartTask() {\n        return watcher -> {\n            // Stop old one\n            ImageConfiguration imageConfig = watcher.getImageConfiguration();\n            PortMapping mappedPorts = runService.createPortMapping(imageConfig.getRunConfiguration(), watcher.getWatchContext().getMojoParameters().getProject().getProperties());\n            String id = watcher.getContainerId();\n\n            String optionalPreStop = getPreStopCommand(imageConfig);\n            if (optionalPreStop != null) {\n                runService.execInContainer(id, optionalPreStop, watcher.getImageConfiguration());\n            }\n            runService.stopPreviouslyStartedContainer(id, false, false);\n\n            // Start new one\n            StartContainerExecutor helper = new StartContainerExecutor.Builder()\n                    .dispatcher(watcher.watchContext.dispatcher)\n                    .follow(watcher.watchContext.follow)\n                    .log(log)\n                    .portMapping(mappedPorts)\n                    .gavLabel(watcher.watchContext.getGavLabel())\n                    .projectProperties(watcher.watchContext.mojoParameters.getProject().getProperties())\n                    .basedir(watcher.watchContext.mojoParameters.getProject().getBasedir())\n                    .imageConfig(imageConfig)\n                    .serviceHub(watcher.watchContext.hub)\n                    .logOutputSpecFactory(watcher.watchContext.serviceHubFactory.getLogOutputSpecFactory())\n                    .showLogs(watcher.watchContext.showLogs)\n                    .containerNamePattern(watcher.watchContext.containerNamePattern)\n                    .buildTimestamp(watcher.watchContext.buildTimestamp)\n                    .build();\n\n            String containerId = helper.startContainer();\n            watcher.setContainerId(containerId);\n        };\n    }\n\n    private String getPreStopCommand(ImageConfiguration imageConfig) {\n        if (imageConfig.getRunConfiguration() != null &&\n                imageConfig.getRunConfiguration().getWaitConfiguration() != null &&\n                imageConfig.getRunConfiguration().getWaitConfiguration().getExec() != null) {\n            return imageConfig.getRunConfiguration().getWaitConfiguration().getExec().getPreStop();\n        }\n        return null;\n    }\n\n    private void callPostGoal(ImageWatcher watcher) throws MojoFailureException,\n            MojoExecutionException {\n        String postGoal = watcher.getPostGoal();\n        if (postGoal != null) {\n            mojoExecutionService.callPluginGoal(postGoal);\n        }\n    }\n\n\n    // ===============================================================================================================\n\n    // Helper class for holding state and parameter when watching images\n    public class ImageWatcher {\n\n        private final ImageConfiguration imageConfig;\n        private final WatchContext watchContext;\n        private final WatchMode mode;\n        private final AtomicReference<String> imageIdRef, containerIdRef;\n        private final long interval;\n        private final String postGoal;\n        private String postExec;\n\n        public ImageWatcher(ImageConfiguration imageConfig, WatchContext watchContext, String imageId, String containerIdRef) {\n            this.imageConfig = imageConfig;\n            this.watchContext = watchContext;\n            this.imageIdRef = new AtomicReference<>(imageId);\n            this.containerIdRef = new AtomicReference<>(containerIdRef);\n\n            this.interval = getWatchInterval(imageConfig);\n            this.mode = getWatchMode(imageConfig);\n            this.postGoal = getPostGoal(imageConfig);\n            this.postExec = getPostExec(imageConfig);\n        }\n\n        public String getContainerId() {\n            return containerIdRef.get();\n        }\n\n        public long getInterval() {\n            return interval;\n        }\n\n        public String getPostGoal() {\n            return postGoal;\n        }\n\n        public boolean isCopy() {\n            return mode.isCopy();\n        }\n\n        public boolean isBuild() {\n            return mode.isBuild();\n        }\n\n        public boolean isRun() {\n            return mode.isRun();\n        }\n\n        public ImageConfiguration getImageConfiguration() {\n            return imageConfig;\n        }\n\n        public void setImageId(String imageId) {\n            imageIdRef.set(imageId);\n        }\n\n        public void setContainerId(String containerId) {\n            containerIdRef.set(containerId);\n        }\n\n        public String getImageName() {\n            return imageConfig.getName();\n        }\n\n        public String getAndSetImageId(String currentImageId) {\n            return imageIdRef.getAndSet(currentImageId);\n        }\n\n        public String getPostExec() {\n            return postExec;\n        }\n\n        public WatchContext getWatchContext() {\n            return watchContext;\n        }\n\n        // =========================================================\n\n        private int getWatchInterval(ImageConfiguration imageConfig) {\n            WatchImageConfiguration watchConfig = imageConfig.getWatchConfiguration();\n            int interval = watchConfig != null ? watchConfig.getInterval() : watchContext.getWatchInterval();\n            return interval < 100 ? 100 : interval;\n        }\n\n        private String getPostExec(ImageConfiguration imageConfig) {\n            WatchImageConfiguration watchConfig = imageConfig.getWatchConfiguration();\n            return watchConfig != null && watchConfig.getPostExec() != null ?\n                    watchConfig.getPostExec() : watchContext.getWatchPostExec();\n        }\n\n        private String getPostGoal(ImageConfiguration imageConfig) {\n            WatchImageConfiguration watchConfig = imageConfig.getWatchConfiguration();\n            return watchConfig != null && watchConfig.getPostGoal() != null ?\n                    watchConfig.getPostGoal() : watchContext.getWatchPostGoal();\n\n        }\n\n        private WatchMode getWatchMode(ImageConfiguration imageConfig) {\n            WatchImageConfiguration watchConfig = imageConfig.getWatchConfiguration();\n            WatchMode mode = watchConfig != null ? watchConfig.getMode() : null;\n            return mode != null ? mode : watchContext.getWatchMode();\n        }\n    }\n\n    // ===========================================================\n\n    /**\n     * Context class to hold the watch configuration\n     */\n    public static class WatchContext implements Serializable {\n\n        private MojoParameters mojoParameters;\n\n        private WatchMode watchMode;\n\n        private int watchInterval;\n\n        private boolean keepRunning;\n\n        private String watchPostGoal;\n\n        private String watchPostExec;\n\n        private GavLabel gavLabel;\n\n        private boolean keepContainer;\n\n        private boolean removeVolumes;\n\n        private boolean autoCreateCustomNetworks;\n\n        private Task<ImageConfiguration> imageCustomizer;\n\n        private Task<ImageWatcher> containerRestarter;\n\n        private transient ServiceHub hub;\n        private transient ServiceHubFactory serviceHubFactory;\n        private transient LogDispatcher dispatcher;\n        private boolean follow;\n        private String showLogs;\n\n        private Date buildTimestamp;\n\n        private String containerNamePattern;\n\n        public WatchContext() {\n        }\n\n        public MojoParameters getMojoParameters() {\n            return mojoParameters;\n        }\n\n        public WatchMode getWatchMode() {\n            return watchMode;\n        }\n\n        public int getWatchInterval() {\n            return watchInterval;\n        }\n\n        public boolean isKeepRunning() {\n            return keepRunning;\n        }\n\n        public String getWatchPostGoal() {\n            return watchPostGoal;\n        }\n\n        public String getWatchPostExec() {\n            return watchPostExec;\n        }\n\n        public GavLabel getGavLabel() {\n            return gavLabel;\n        }\n\n        public boolean isKeepContainer() {\n            return keepContainer;\n        }\n\n        public boolean isRemoveVolumes() {\n            return removeVolumes;\n        }\n\n        public boolean isAutoCreateCustomNetworks() {\n            return autoCreateCustomNetworks;\n        }\n\n        public Task<ImageConfiguration> getImageCustomizer() {\n            return imageCustomizer;\n        }\n\n        public Task<ImageWatcher> getContainerRestarter() {\n            return containerRestarter;\n        }\n\n        public Date getBuildTimestamp() {\n            return buildTimestamp;\n        }\n\n        public String getContainerNamePattern() {\n            return containerNamePattern;\n        }\n\n        public static class Builder {\n\n            private WatchContext context;\n\n            public Builder() {\n                this.context = new WatchContext();\n            }\n\n            public Builder(WatchContext context) {\n                this.context = context;\n            }\n\n            public Builder mojoParameters(MojoParameters mojoParameters) {\n                context.mojoParameters = mojoParameters;\n                return this;\n            }\n\n            public Builder watchMode(WatchMode watchMode) {\n                context.watchMode = watchMode;\n                return this;\n            }\n\n            public Builder watchInterval(int watchInterval) {\n                context.watchInterval = watchInterval;\n                return this;\n            }\n\n            public Builder keepRunning(boolean keepRunning) {\n                context.keepRunning = keepRunning;\n                return this;\n            }\n\n            public Builder watchPostGoal(String watchPostGoal) {\n                context.watchPostGoal = watchPostGoal;\n                return this;\n            }\n\n            public Builder watchPostExec(String watchPostExec) {\n                context.watchPostExec = watchPostExec;\n                return this;\n            }\n\n            public Builder pomLabel(GavLabel gavLabel) {\n                context.gavLabel = gavLabel;\n                return this;\n            }\n\n            public Builder keepContainer(boolean keepContainer) {\n                context.keepContainer = keepContainer;\n                return this;\n            }\n\n            public Builder removeVolumes(boolean removeVolumes) {\n                context.removeVolumes = removeVolumes;\n                return this;\n            }\n\n            public Builder imageCustomizer(Task<ImageConfiguration> imageCustomizer) {\n                context.imageCustomizer = imageCustomizer;\n                return this;\n            }\n\n            public Builder containerRestarter(Task<ImageWatcher> containerRestarter) {\n                context.containerRestarter = containerRestarter;\n                return this;\n            }\n\n            public Builder autoCreateCustomNetworks(boolean autoCreateCustomNetworks) {\n                context.autoCreateCustomNetworks = autoCreateCustomNetworks;\n                return this;\n            }\n\n            public Builder follow(boolean follow) {\n                context.follow = follow;\n                return this;\n            }\n\n            public Builder showLogs(String showLogs) {\n                context.showLogs = showLogs;\n                return this;\n            }\n\n            public Builder hub(ServiceHub hub){\n                context.hub = hub;\n                return this;\n            }\n\n            public Builder serviceHubFactory(ServiceHubFactory serviceHubFactory){\n                context.serviceHubFactory = serviceHubFactory;\n                return this;\n            }\n\n            public Builder dispatcher(LogDispatcher dispatcher){\n                context.dispatcher = dispatcher;\n                return this;\n            }\n\n            public Builder buildTimestamp(Date buildTimestamp) {\n                context.buildTimestamp = buildTimestamp;\n                return this;\n            }\n\n            public Builder containerNamePattern(String containerNamePattern) {\n                context.containerNamePattern = containerNamePattern;\n                return this;\n            }\n\n\n            public WatchContext build() {\n                return context;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/service/helper/StartContainerExecutor.java",
    "content": "package io.fabric8.maven.docker.service.helper;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.codehaus.plexus.util.StringUtils;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.Logger;\n\npublic class StartContainerExecutor {\n    private String exposeContainerProps;\n    private Logger log;\n    private LogOutputSpecFactory logOutputSpecFactory;\n    private ServiceHub hub;\n    private boolean follow;\n    private String showLogs;\n    private String containerNamePattern;\n    private Date buildDate;\n    private Properties projectProperties;\n    private File basedir;\n    private ImageConfiguration imageConfig;\n    private GavLabel gavLabel;\n    private PortMapping portMapping;\n    private LogDispatcher dispatcher;\n\n    private StartContainerExecutor(){}\n\n    public String startContainer() throws IOException, ExecException {\n        final Properties projProperties = projectProperties;\n\n        final String containerId = hub.getRunService().createAndStartContainer(imageConfig, portMapping, gavLabel, projProperties, basedir, containerNamePattern, buildDate);\n\n        showLogsIfRequested(containerId);\n        Properties exposedProps = queryContainerProperties(containerId);\n        projProperties.putAll(exposedProps);\n        waitAndPostExec(containerId, projProperties);\n\n        return containerId;\n    }\n\n    public Properties queryContainerProperties(String containerId)\n        throws DockerAccessException {\n        String propKey = getExposedPropertyKeyPart();\n        Properties exposedProperties = new Properties();\n\n        if (StringUtils.isNotEmpty(exposeContainerProps) && StringUtils.isNotEmpty(propKey)) {\n            Container container = hub.getQueryService().getMandatoryContainer(containerId);\n\n            String prefix = addDot(exposeContainerProps) + addDot(propKey);\n            exposedProperties.put(prefix + \"id\", containerId);\n            String ip = container.getIPAddress();\n            if (StringUtils.isNotEmpty(ip)) {\n                exposedProperties.put(prefix + \"ip\", ip);\n            }\n\n            Map<String, String> nets = container.getCustomNetworkIpAddresses();\n            if (nets != null) {\n                for (Map.Entry<String, String> entry : nets.entrySet()) {\n                    exposedProperties.put(prefix + addDot(\"net\") + addDot(entry.getKey()) + \"ip\", entry.getValue());\n                }\n            }\n        }\n        return exposedProperties;\n    }\n\n    String getExposedPropertyKeyPart() {\n        String propKey = imageConfig.getRunConfiguration() != null ? imageConfig.getRunConfiguration().getExposedPropertyKey() : \"\";\n        if (StringUtils.isEmpty(propKey)) {\n            propKey = imageConfig.getAlias();\n        }\n        return propKey;\n    }\n\n    private String addDot(String part) {\n        return part.endsWith(\".\") ? part : part + \".\";\n    }\n\n    private void showLogsIfRequested(String containerId) {\n        if (showLogs()) {\n            dispatcher.trackContainerLog(containerId,\n                                         logOutputSpecFactory.createSpec(containerId, imageConfig));\n        }\n    }\n\n    private void waitAndPostExec(String containerId, Properties projProperties) throws IOException, ExecException {\n        // Wait if requested\n        hub.getWaitService().wait(imageConfig, projProperties, containerId);\n        WaitConfiguration waitConfig = imageConfig.getRunConfiguration().getWaitConfiguration();\n        if (waitConfig != null && waitConfig.getExec() != null && waitConfig.getExec().getPostStart() != null) {\n            try {\n                hub.getRunService().execInContainer(containerId, waitConfig.getExec().getPostStart(), imageConfig);\n            } catch (ExecException exp) {\n                if (waitConfig.getExec().isBreakOnError()) {\n                    throw exp;\n                } else {\n                    log.warn(\"Cannot run postStart: %s\", exp.getMessage());\n                }\n            }\n        }\n    }\n\n    boolean showLogs() {\n        if (showLogs != null) {\n            if (showLogs.equalsIgnoreCase(\"true\")) {\n                return true;\n            } else if (showLogs.equalsIgnoreCase(\"false\")) {\n                return false;\n            } else {\n                return ConfigHelper.matchesConfiguredImages(showLogs, imageConfig);\n            }\n        }\n\n        RunImageConfiguration runConfig = imageConfig.getRunConfiguration();\n        if (runConfig != null) {\n            LogConfiguration logConfig = runConfig.getLogConfiguration();\n            if (logConfig != null) {\n                return logConfig.isActivated();\n            } else {\n                // Default is to show logs if \"follow\" is true\n                return follow;\n            }\n        }\n        return false;\n    }\n\n    public static class Builder {\n        private final StartContainerExecutor helper;\n\n        public Builder(){\n            helper = new StartContainerExecutor();\n        }\n\n        public Builder log(Logger log) {\n            helper.log = log;\n            return this;\n        }\n\n        public Builder logOutputSpecFactory(LogOutputSpecFactory factory) {\n            helper.logOutputSpecFactory = factory;\n            return this;\n        }\n\n        public Builder exposeContainerProps(String exposeContainerProps) {\n            helper.exposeContainerProps = exposeContainerProps;\n            return this;\n        }\n\n        public Builder serviceHub(ServiceHub hub) {\n            helper.hub = hub;\n            return this;\n        }\n\n        public Builder projectProperties(Properties props) {\n            helper.projectProperties = props;\n            return this;\n        }\n\n        public Builder basedir(File dir) {\n            helper.basedir = dir;\n            return this;\n        }\n\n        public Builder follow(boolean follow) {\n            helper.follow = follow;\n            return this;\n        }\n\n        public Builder showLogs(String showLogs) {\n            helper.showLogs = showLogs;\n            return this;\n        }\n\n        public Builder containerNamePattern(String pattern) {\n            helper.containerNamePattern = pattern;\n            return this;\n        }\n\n        public Builder buildTimestamp(Date date) {\n            helper.buildDate = date;\n            return this;\n        }\n\n\n\n        public Builder dispatcher(LogDispatcher dispatcher) {\n            helper.dispatcher = dispatcher;\n            return this;\n        }\n\n        public Builder portMapping(PortMapping portMapping) {\n            helper.portMapping = portMapping;\n            return this;\n        }\n\n        public Builder gavLabel(GavLabel gavLabel) {\n            helper.gavLabel = gavLabel;\n            return this;\n        }\n\n        public Builder imageConfig(ImageConfiguration imageConfig) {\n            helper.imageConfig = imageConfig;\n            return this;\n        }\n\n        public StartContainerExecutor build() {\n            return helper;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/AnsiLogger.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.maven.plugin.logging.Log;\nimport org.codehaus.plexus.util.StringUtils;\nimport org.fusesource.jansi.Ansi;\nimport org.fusesource.jansi.AnsiConsole;\n\nimport static org.fusesource.jansi.Ansi.Color.*;\nimport static org.fusesource.jansi.Ansi.ansi;\n\n/**\n * Simple log handler for printing used during the maven build\n *\n * @author roland\n * @since 31.03.14\n */\npublic class AnsiLogger implements Logger {\n\n    // prefix used for console output\n    public static final String DEFAULT_LOG_PREFIX = \"DOCKER> \";\n    private static final int NON_ANSI_UPDATE_PERIOD = 80;\n\n    private final Log log;\n    private final String prefix;\n    private final boolean batchMode;\n\n    private boolean isVerbose = false;\n    private List<LogVerboseCategory> verboseModes = null;\n\n    // ANSI escapes for various colors (or empty strings if no coloring is used)\n    static Ansi.Color\n            COLOR_ERROR = RED,\n            COLOR_INFO = GREEN,\n            COLOR_WARNING = YELLOW,\n            COLOR_PROGRESS_ID = YELLOW,\n            COLOR_PROGRESS_STATUS = GREEN,\n            COLOR_PROGRESS_BAR = CYAN,\n            COLOR_EMPHASIS = BLUE;\n\n\n    // Map remembering lines\n    private ThreadLocal<Map<String, Integer>> imageLines = new ThreadLocal<>();\n    private ThreadLocal<AtomicInteger> updateCount = new ThreadLocal<>();\n\n    // Whether to use ANSI codes\n    private boolean useAnsi;\n\n\n    public AnsiLogger(Log log, boolean useColor, String verbose) {\n        this(log, useColor, verbose, false);\n    }\n\n    public AnsiLogger(Log log, boolean useColor, String verbose, boolean batchMode) {\n        this(log, useColor, verbose, batchMode, DEFAULT_LOG_PREFIX);\n    }\n\n    public AnsiLogger(Log log, boolean useColor, String verbose, boolean batchMode, String prefix) {\n        this.log = log;\n        this.prefix = prefix;\n        this.batchMode = batchMode;\n        checkVerboseLoggingEnabled(verbose);\n        initializeColor(useColor);\n    }\n\n    /** {@inheritDoc} */\n    public void debug(String message, Object ... params) {\n        if (isDebugEnabled()) {\n            log.debug(prefix + format(message, params));\n        }\n    }\n\n    /** {@inheritDoc} */\n    public void info(String message, Object ... params) {\n        log.info(colored(message, COLOR_INFO, true, params));\n    }\n\n    /** {@inheritDoc} */\n    public void verbose(LogVerboseCategory logVerboseCategory, String message, Object ... params) {\n        if (isVerbose && verboseModes != null && verboseModes.contains(logVerboseCategory)) {\n            log.info(ansi().fgBright(BLACK).a(prefix).a(format(message, params)).reset().toString());\n        }\n    }\n\n    /** {@inheritDoc} */\n    public void warn(String format, Object ... params) {\n        log.warn(colored(format, COLOR_WARNING, true, params));\n    }\n\n    /** {@inheritDoc} */\n    public void error(String message, Object ... params) {\n        log.error(colored(message, COLOR_ERROR, true, params));\n    }\n\n    @Override\n    public String errorMessage(String message) {\n        return colored(message, COLOR_ERROR, false);\n    }\n\n    /**\n     * Whether debugging is enabled.\n     */\n    public boolean isDebugEnabled() {\n        return log.isDebugEnabled();\n    }\n\n    public boolean isVerboseEnabled() {\n        return isVerbose;\n    }\n\n    /**\n     * Start a progress bar\n     */\n    public void progressStart() {\n        // A progress indicator is always written out to standard out if a tty is enabled.\n        if (!batchMode && log.isInfoEnabled()) {\n            imageLines.remove();\n            updateCount.remove();\n            imageLines.set(new HashMap<String, Integer>());\n            updateCount.set(new AtomicInteger());\n        }\n    }\n\n    /**\n     * Update the progress\n     */\n    public void progressUpdate(String layerId, String status, String progressMessage) {\n        if (!batchMode && log.isInfoEnabled() && StringUtils.isNotEmpty(layerId)) {\n            if (useAnsi) {\n                updateAnsiProgress(layerId, status, progressMessage);\n            } else {\n                updateNonAnsiProgress(layerId);\n            }\n            flush();\n        }\n    }\n\n    private void updateAnsiProgress(String imageId, String status, String progressMessage) {\n        Map<String,Integer> imgLineMap = imageLines.get();\n        Integer line = imgLineMap.get(imageId);\n\n        int diff = 0;\n        if (line == null) {\n            line = imgLineMap.size();\n            imgLineMap.put(imageId, line);\n        } else {\n            diff = imgLineMap.size() - line;\n        }\n\n        if (diff > 0) {\n            print(ansi().cursorUp(diff).eraseLine(Ansi.Erase.ALL).toString());\n        }\n\n        // Status with progress bars: (max length = 11, hence pad to 11)\n        // Extracting\n        // Downloading\n        String progress = progressMessage != null ? progressMessage : \"\";\n        String msg =\n            ansi()\n                .fg(COLOR_PROGRESS_ID).a(imageId).reset().a(\": \")\n                .fg(COLOR_PROGRESS_STATUS).a(StringUtils.rightPad(status,11) + \" \")\n                .fg(COLOR_PROGRESS_BAR).a(progress).toString();\n        println(msg);\n\n        if (diff > 0) {\n            // move cursor back down to bottom\n            print(ansi().cursorDown(diff - 1).toString());\n        }\n    }\n\n    private void updateNonAnsiProgress(String imageId) {\n        AtomicInteger count = updateCount.get();\n        int nr = count.getAndIncrement();\n        if (nr % NON_ANSI_UPDATE_PERIOD == 0) {\n            print(\"#\");\n        }\n        if (nr > 0 && nr % (80 * NON_ANSI_UPDATE_PERIOD) == 0) {\n            print(\"\\n\");\n        }\n    }\n\n    /**\n     * Finis progress meter. Must be always called if {@link #progressStart()} has been used.\n     */\n    public void progressFinished() {\n        if (!batchMode && log.isInfoEnabled()) {\n            imageLines.remove();\n            print(ansi().reset().toString());\n            if (!useAnsi) {\n                println(\"\");\n            }\n        }\n    }\n\n    private void flush() {\n        System.out.flush();\n    }\n\n    private void initializeColor(boolean useColor) {\n        this.useAnsi = useColor && !log.isDebugEnabled();\n        if (useAnsi) {\n            AnsiConsole.systemInstall();\n            Ansi.setEnabled(true);\n        }\n        else {\n            Ansi.setEnabled(false);\n        }\n    }\n\n    private void println(String txt) {\n        System.out.println(txt);\n    }\n\n    private void print(String txt) {\n        System.out.print(txt);\n    }\n\n    private String colored(String message, Ansi.Color color, boolean addPrefix, Object ... params) {\n        Ansi ansi = ansi().fg(color);\n        String msgToPrint = addPrefix ? prefix + message : message;\n        return ansi.a(format(evaluateEmphasis(msgToPrint, color), params)).reset().toString();\n    }\n\n    // Use parameters when given, otherwise we use the string directly\n    private String format(String message, Object[] params) {\n        if (params.length == 0) {\n            return message;\n        } else if (params.length == 1 && params[0] instanceof Throwable) {\n            // We print only the message here since breaking exception will bubble up\n            // anyway\n            return message + \": \" + params[0].toString();\n        } else {\n            return String.format(message, params);\n        }\n    }\n\n    // Emphasize parts encloses in \"[[*]]\" tags\n    private String evaluateEmphasis(String message, Ansi.Color msgColor) {\n        // Split but keep the content by splitting on [[ and ]] separately when they\n        // are followed or preceded by their counterpart. This lets the split retain\n        // the character in the center.\n        String[] parts = message.split(\"(\\\\[\\\\[(?=.]])|(?<=\\\\[\\\\[.)]])\");\n        if (parts.length == 1) {\n            return message;\n        }\n        // The split up string is comprised of a leading plain part, followed\n        // by groups of colorization that are <SET> color-part <RESET> plain-part.\n        // To avoid emitting needless color changes, we skip the set or reset\n        // if the subsequent part is empty.\n        String msgColorS = ansi().fg(msgColor).toString();\n        StringBuilder ret = new StringBuilder(parts[0]);\n\n        for (int i = 1; i < parts.length; i += 4) {\n            boolean colorPart = i + 1 < parts.length && parts[i + 1].length() > 0;\n            boolean plainPart = i + 3 < parts.length && parts[i + 3].length() > 0;\n\n            if (colorPart) {\n                ret.append(getEmphasisColor(parts[i]));\n                ret.append(parts[i + 1]);\n                if(plainPart) {\n                    ret.append(msgColorS);\n                }\n            }\n            if (plainPart) {\n                ret.append(parts[i + 3]);\n            }\n        }\n        return ret.toString();\n    }\n\n    private static final Map<String, Ansi.Color> COLOR_MAP = new HashMap<>();\n\n    static {\n        COLOR_MAP.put(\"*\", COLOR_EMPHASIS);\n        COLOR_MAP.put(\"B\", BLUE);\n        COLOR_MAP.put(\"C\", CYAN);\n        COLOR_MAP.put(\"Y\", YELLOW);\n        COLOR_MAP.put(\"G\", GREEN);\n        COLOR_MAP.put(\"M\", MAGENTA);\n        COLOR_MAP.put(\"R\", RED);\n        COLOR_MAP.put(\"W\", WHITE);\n        COLOR_MAP.put(\"S\", BLACK);\n        COLOR_MAP.put(\"D\", DEFAULT);\n    }\n\n    private String getEmphasisColor(String id) {\n        Ansi.Color color = COLOR_MAP.get(id.toUpperCase());\n        if (color != null) {\n            return id.toLowerCase().equals(id) ?\n                // lower case letter means bright color ...\n                ansi().fgBright(color).toString() :\n                ansi().fg(color).toString();\n        } else {\n            return \"\";\n        }\n    }\n\n    private void checkVerboseLoggingEnabled(String verbose) {\n        if (verbose == null || verbose.equalsIgnoreCase(\"false\")) {\n            this.isVerbose = false;\n            return;\n        }\n        if (verbose.equalsIgnoreCase(\"all\")) {\n            this.isVerbose = true;\n            this.verboseModes = Arrays.asList(LogVerboseCategory.values());\n            return;\n        }\n        if (verbose.equals(\"\") || verbose.equalsIgnoreCase(\"true\")) {\n            this.isVerbose = true;\n            this.verboseModes = Collections.singletonList(LogVerboseCategory.BUILD);\n            return;\n        }\n\n        this.verboseModes = getVerboseModesFromString(verbose);\n        this.isVerbose = true;\n    }\n\n    private Boolean checkBackwardVersionValues(String verbose) {\n        if (verbose.isEmpty()) {\n            return Boolean.TRUE;\n        }\n        if (verbose.equalsIgnoreCase(\"true\") || verbose.equalsIgnoreCase(\"false\")) {\n            return Boolean.parseBoolean(verbose.toLowerCase());\n        }\n        return null;\n    }\n\n    private List<LogVerboseCategory> getVerboseModesFromString(String groups) {\n        List<LogVerboseCategory> ret = new ArrayList<>();\n        for (String group : groups.split(\",\")) {\n            try {\n                ret.add(LogVerboseCategory.valueOf(group.toUpperCase()));\n            } catch (Exception exp) {\n                log.info(\"log: Unknown verbosity group \" + groups + \". Ignoring...\");\n            }\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport com.google.gson.JsonObject;\n\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.client.config.RequestConfig;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.conn.ConnectTimeoutException;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.settings.Server;\nimport org.apache.maven.settings.Settings;\nimport org.codehaus.plexus.PlexusContainer;\nimport org.codehaus.plexus.component.repository.exception.ComponentLookupException;\nimport org.codehaus.plexus.util.xml.Xpp3Dom;\nimport org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;\n\nimport com.google.common.net.UrlEscapers;\nimport com.google.gson.Gson;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.ecr.EcrExtendedAuth;\nimport io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory;\n\n/**\n * Factory for creating docker specific authentication configuration\n *\n * @author roland\n * @since 29.07.14\n */\npublic class AuthConfigFactory {\n\n    // Whether to check for OpenShift authentication\n    private static final String AUTH_USE_OPENSHIFT_AUTH = \"useOpenShiftAuth\";\n\n    static final String DOCKER_LOGIN_DEFAULT_REGISTRY = \"https://index.docker.io/v1/\";\n\n    private final PlexusContainer container;\n\n    private Logger log;\n    private static final String[] DEFAULT_REGISTRIES = new String[]{\n            \"docker.io\", \"index.docker.io\", \"registry.hub.docker.com\"\n    };\n\n    /**\n     * Constructor which should be used during startup phase of a plugin\n     *\n     * @param container the container used for do decryption of passwords\n     */\n    public AuthConfigFactory(PlexusContainer container) {\n        this.container = container;\n    }\n\n    public void setLog(Logger log) {\n        this.log = log;\n    }\n\n    /**\n     * Create an authentication config object which can be used for communication with a Docker registry\n     *\n     * The authentication information is looked up at various places (in this order):\n     *\n     * <ul>\n     *    <li>From system properties</li>\n     *    <li>From the provided map which can contain key-value pairs</li>\n     *    <li>From the openshift settings in ~/.config/kube</li>\n     *    <li>From the Maven settings stored typically in ~/.m2/settings.xml</li>\n     *    <li>From the Docker settings stored in ~/.docker/config.json</li>\n     * </ul>\n     *\n     * The following properties (prefix with 'docker.') and config key are evaluated:\n     *\n     * <ul>\n     *     <li>username: User to authenticate</li>\n     *     <li>password: Password to authenticate. Can be encrypted</li>\n     *     <li>email: Optional EMail address which is send to the registry, too</li>\n     * </ul>\n     *\n     *  If the repository is in an aws ecr registry and skipExtendedAuth is not true, if found\n     *  credentials are not from docker settings, they will be interpreted as iam credentials\n     *  and exchanged for ecr credentials.\n     *\n     * @param isPush if true this AuthConfig is created for a push, if false it's for a pull\n     * @param skipExtendedAuth if true, do not execute extended authentication methods\n     * @param authConfig String-String Map holding configuration info from the plugin's configuration. Can be <code>null</code> in\n     *                   which case the settings are consulted.\n     * @param settings the global Maven settings object\n     * @param user user to check for\n     * @param registry registry to use, might be null in which case a default registry is checked,\n     * @return the authentication configuration or <code>null</code> if none could be found\n     *\n     * @throws MojoFailureException\n     */\n    public AuthConfig createAuthConfig(boolean isPush, boolean skipExtendedAuth, Map authConfig, Settings settings, String user, String registry)\n            throws MojoExecutionException {\n\n        AuthConfig ret = createStandardAuthConfig(isPush, authConfig, settings, user, registry);\n        if (ret != null) {\n            if (registry == null || skipExtendedAuth) {\n                return ret;\n            }\n            try {\n                return extendedAuthentication(ret, registry);\n            } catch (IOException e) {\n                throw new MojoExecutionException(e.getMessage(), e);\n            }\n        }\n\n        // Finally check ~/.docker/config.json\n        ret = getAuthConfigFromDockerConfig(registry);\n        if (ret != null) {\n            log.debug(\"AuthConfig: credentials from ~/.docker/config.json\");\n            return ret;\n        }\n\n        log.debug(\"AuthConfig: no credentials found\");\n        return null;\n    }\n\n\n    /**\n     * Try various extended authentication method.  Currently only supports amazon ECR\n     *\n     * @param standardAuthConfig The locally stored credentials.\n     * @param registry The registry to authenticated against.\n     * @return The given credentials, if registry does not need extended authentication;\n     * else, the credentials after authentication.\n     * @throws IOException\n     * @throws MojoExecutionException\n     */\n    private AuthConfig extendedAuthentication(AuthConfig standardAuthConfig, String registry) throws IOException, MojoExecutionException {\n        EcrExtendedAuth ecr = new EcrExtendedAuth(log, registry);\n        if (ecr.isAwsRegistry()) {\n            return ecr.extendedAuth(standardAuthConfig);\n        }\n        return standardAuthConfig;\n    }\n\n    /**\n     * Create an authentication config object which can be used for communication with a Docker registry\n     *\n     * The authentication information is looked up at various places (in this order):\n     *\n     * <ul>\n     *    <li>From system properties</li>\n     *    <li>From the provided map which can contain key-value pairs</li>\n     *    <li>From the openshift settings in ~/.config/kube</li>\n     *    <li>From the Maven settings stored typically in ~/.m2/settings.xml</li>\n     * </ul>\n     *\n     * The following properties (prefix with 'docker.' or 'registry.') and config key are evaluated:\n     *\n     * <ul>\n     *     <li>username: User to authenticate</li>\n     *     <li>password: Password to authenticate. Can be encrypted</li>\n     *     <li>email: Optional EMail address which is send to the registry, too</li>\n     * </ul>\n     *\n     *\n     * @param isPush if true this AuthConfig is created for a push, if false it's for a pull\n     * @param authConfigMap String-String Map holding configuration info from the plugin's configuration. Can be <code>null</code> in\n     *                   which case the settings are consulted.\n     * @param settings the global Maven settings object\n     * @param user user to check for\n     * @param registry registry to use, might be null in which case a default registry is checked,\n     * @return the authentication configuration or <code>null</code> if none could be found\n     *\n     * @throws MojoFailureException\n     */\n    private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, Settings settings, String user, String registry)\n            throws MojoExecutionException {\n        AuthConfig ret;\n\n        // Check first for specific configuration based on direction (pull or push), then for a default value\n        for (LookupMode lookupMode : new LookupMode[] { getLookupMode(isPush), LookupMode.DEFAULT, LookupMode.REGISTRY}) {\n            // System properties docker.username and docker.password always take precedence\n            ret = getAuthConfigFromSystemProperties(lookupMode);\n            if (ret != null) {\n                log.debug(\"AuthConfig: credentials from system properties\");\n                return ret;\n            }\n\n            // Check for openshift authentication either from the plugin config or from system props\n            if (lookupMode != LookupMode.REGISTRY) {\n                ret = getAuthConfigFromOpenShiftConfig(lookupMode, authConfigMap);\n                if (ret != null) {\n                    log.debug(\"AuthConfig: OpenShift credentials\");\n                    return ret;\n                }\n            }\n\n            // Get configuration from global plugin config\n            ret = getAuthConfigFromPluginConfiguration(lookupMode, authConfigMap);\n            if (ret != null) {\n                log.debug(\"AuthConfig: credentials from plugin config\");\n                return ret;\n            }\n        }\n\n        // ===================================================================\n        // These are lookups based on registry only, so the direction (push or pull) doesn't matter:\n\n        // Now lets lookup the registry & user from ~/.m2/setting.xml\n        ret = getAuthConfigFromSettings(settings, user, registry);\n        if (ret != null) {\n            log.debug(\"AuthConfig: credentials from ~/.m2/setting.xml\");\n            return ret;\n        }\n\n        // check EC2 instance role if registry is ECR\n        if (EcrExtendedAuth.isAwsRegistry(registry)) {\n            ret = getAuthConfigViaAwsSdk();\n            if (ret != null) {\n                log.debug(\"AuthConfig: AWS credentials from AWS SDK\");\n                return ret;\n            }\n\n            ret = getAuthConfigFromAwsEnvironmentVariables();\n            if (ret != null) {\n                log.debug(\"AuthConfig: AWS credentials from ENV variables\");\n                return ret;\n            }\n\n            try {\n                ret = getAuthConfigFromEC2InstanceRole();\n            } catch (ConnectTimeoutException ex) {\n                log.debug(\"Connection timeout while retrieving instance meta-data, likely not an EC2 instance (%s)\",\n                        ex.getMessage());\n            } catch (IOException ex) {\n                // don't make that an error since it may fail if not run on an EC2 instance\n                log.warn(\"Error while retrieving EC2 instance credentials: %s\", ex.getMessage());\n            }\n            if (ret != null) {\n                log.debug(\"AuthConfig: credentials from EC2 instance role\");\n                return ret;\n            }\n\n            try {\n                ret = getAuthConfigFromTaskRole();\n            } catch (ConnectTimeoutException ex) {\n                log.debug(\"Connection timeout while retrieving ECS meta-data, likely not an ECS instance (%s)\",\n                        ex.getMessage());\n            } catch (IOException ex) {\n                log.warn(\"Error while retrieving ECS Task role credentials: %s\", ex.getMessage());\n            }\n            if (ret != null) {\n                log.debug(\"AuthConfig: credentials from ECS Task role\");\n                return ret;\n            }\n        }\n\n        // No authentication found\n        return null;\n    }\n\n    private AuthConfig getAuthConfigViaAwsSdk() {\n        try {\n            Class.forName(\"com.amazonaws.auth.DefaultAWSCredentialsProviderChain\");\n        } catch (ClassNotFoundException e) {\n            log.info(\"It appears that you're using AWS ECR.\" +\n                    \" Consider integrating the AWS SDK in order to make use of common AWS authentication mechanisms,\" +\n                    \" see https://dmp.fabric8.io/#extended-authentication\");\n            return null;\n        }\n        return new AwsSdkAuthConfigFactory(log).createAuthConfig();\n    }\n\n    /**\n     * Try using the AWS credentials provided via ENV variables.\n     * See https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html\n     */\n    private AuthConfig getAuthConfigFromAwsEnvironmentVariables() {\n        String accessKeyId = System.getenv(\"AWS_ACCESS_KEY_ID\");\n        if (accessKeyId == null) {\n            log.debug(\"System environment not set for variable AWS_ACCESS_KEY_ID, no AWS credentials found\");\n            return null;\n        }\n        String secretAccessKey = System.getenv(\"AWS_SECRET_ACCESS_KEY\");\n        if (secretAccessKey == null) {\n            log.warn(\"System environment set for variable AWS_ACCESS_KEY_ID, but NOT for variable AWS_SECRET_ACCESS_KEY!\");\n            return null;\n        }\n        return new AuthConfig(accessKeyId, secretAccessKey, \"none\", System.getenv(\"AWS_SESSION_TOKEN\"));\n    }\n\n    // ===================================================================================================\n\n\n    // if the local credentials don't contain user and password, use EC2 instance\n    // role credentials\n    private AuthConfig getAuthConfigFromEC2InstanceRole() throws IOException {\n        log.debug(\"No user and password set for ECR, checking EC2 instance role\");\n        try (CloseableHttpClient client = HttpClients.custom().useSystemProperties().build()) {\n            // we can set very low timeouts because the request returns almost instantly on\n            // an EC2 instance\n            // on a non-EC2 instance we can fail early\n            RequestConfig conf = RequestConfig.custom().setConnectionRequestTimeout(1000).setConnectTimeout(1000)\n                    .setSocketTimeout(1000).build();\n\n            // get instance role - if available\n            HttpGet request = new HttpGet(\"http://169.254.169.254/latest/meta-data/iam/security-credentials\");\n            request.setConfig(conf);\n            String instanceRole;\n            try (CloseableHttpResponse response = client.execute(request)) {\n                if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {\n                    // no instance role found\n                    log.debug(\"No instance role found, return code was %d\", response.getStatusLine().getStatusCode());\n                    return null;\n                }\n\n                // read instance role\n                try (InputStream is = response.getEntity().getContent()) {\n                    instanceRole = IOUtils.toString(is, StandardCharsets.UTF_8);\n                }\n            }\n            log.debug(\"Found instance role %s, getting temporary security credentials\", instanceRole);\n\n            // get temporary credentials\n            request = new HttpGet(\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"\n                    + UrlEscapers.urlPathSegmentEscaper().escape(instanceRole));\n            request.setConfig(conf);\n            return readAwsCredentials(client, request);\n        }\n    }\n\n    // if the local credentials don't contain user and password & is not a EC2 instance,\n    // use ECS|Fargate Task instance role credentials\n    private AuthConfig getAuthConfigFromTaskRole() throws IOException {\n        log.debug(\"No user and password set for ECR, checking ECS Task role\");\n        URI uri = getMetadataEndpointForCredentials();\n        if (uri == null) {\n            return null;\n        }\n        // get temporary credentials\n        log.debug(\"Getting temporary security credentials from: %s\", uri);\n        try (CloseableHttpClient client = HttpClients.custom().useSystemProperties().build()) {\n            RequestConfig conf =\n                RequestConfig.custom().setConnectionRequestTimeout(1000).setConnectTimeout(1000)\n                    .setSocketTimeout(1000).build();\n            HttpGet request = new HttpGet(uri);\n            request.setConfig(conf);\n            return readAwsCredentials(client, request);\n        }\n    }\n\n    private AuthConfig readAwsCredentials(CloseableHttpClient client, HttpGet request) throws IOException {\n        try (CloseableHttpResponse response = client.execute(request)) {\n            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {\n                log.debug(\"No security credential found, return code was %d\",\n                        response.getStatusLine().getStatusCode());\n                // no instance role found\n                return null;\n            }\n\n            // read instance role\n            try (Reader r = new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8)) {\n                JsonObject securityCredentials = new Gson().fromJson(r, JsonObject.class);\n\n                String user = securityCredentials.getAsJsonPrimitive(\"AccessKeyId\").getAsString();\n                String password = securityCredentials.getAsJsonPrimitive(\"SecretAccessKey\").getAsString();\n                String token = securityCredentials.getAsJsonPrimitive(\"Token\").getAsString();\n\n                log.debug(\"Received temporary access key %s...\", user.substring(0, 8));\n                return new AuthConfig(user, password, \"none\", token);\n            }\n        }\n    }\n\n    private URI getMetadataEndpointForCredentials() {\n        // get ECS task role - if available\n        String awsContainerCredentialsUri = System.getenv(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\");\n        if (awsContainerCredentialsUri == null) {\n            log.debug(\"System environment not set for variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, no task role found\");\n            return null;\n        }\n        if (awsContainerCredentialsUri.charAt(0) != '/') {\n            awsContainerCredentialsUri = \"/\" + awsContainerCredentialsUri;\n        }\n\n        String ecsMetadataEndpoint = System.getenv(\"ECS_METADATA_ENDPOINT\");\n        if (ecsMetadataEndpoint == null) {\n            ecsMetadataEndpoint = \"http://169.254.170.2\";\n        }\n\n        try {\n            return new URI(ecsMetadataEndpoint + awsContainerCredentialsUri);\n        } catch (URISyntaxException e) {\n            log.warn(\"Failed to construct path to ECS metadata endpoint for credentials\", e);\n            return null;\n        }\n    }\n\n    private AuthConfig getAuthConfigFromSystemProperties(LookupMode lookupMode) throws MojoExecutionException {\n        Properties props = System.getProperties();\n        String userKey = lookupMode.asSysProperty(AuthConfig.AUTH_USERNAME);\n        String passwordKey = lookupMode.asSysProperty(AuthConfig.AUTH_PASSWORD);\n        if (props.containsKey(userKey)) {\n            if (!props.containsKey(passwordKey)) {\n                throw new MojoExecutionException(\"No \" + passwordKey + \" provided for username \" + props.getProperty(userKey));\n            }\n            return new AuthConfig(props.getProperty(userKey),\n                                  decrypt(props.getProperty(passwordKey)),\n                                  props.getProperty(lookupMode.asSysProperty(AuthConfig.AUTH_EMAIL)),\n                                  getAuthProperty(props, lookupMode));\n        } else {\n            return null;\n        }\n    }\n\n    private String getAuthProperty(Properties props, LookupMode lookupMode) {\n        String authProp = props.getProperty(lookupMode.asSysProperty(AuthConfig.AUTH_AUTH));\n        if (authProp != null) {\n            return authProp;\n        }\n        // Fallback is deprecated AUTH_AUTHTOKEN property\n        return props.getProperty(lookupMode.asSysProperty(\"authToken\"));\n    }\n\n    private AuthConfig getAuthConfigFromOpenShiftConfig(LookupMode lookupMode, Map authConfigMap) throws MojoExecutionException {\n        Properties props = System.getProperties();\n        String useOpenAuthModeProp = lookupMode.asSysProperty(AUTH_USE_OPENSHIFT_AUTH);\n        // Check for system property\n        if (props.containsKey(useOpenAuthModeProp)) {\n            boolean useOpenShift = Boolean.valueOf(props.getProperty(useOpenAuthModeProp));\n            if (useOpenShift) {\n                return validateMandatoryOpenShiftLogin(parseOpenShiftConfig(), useOpenAuthModeProp);\n            } else {\n                return null;\n            }\n        }\n\n        // Check plugin config\n        Map mapToCheck = getAuthConfigMapToCheck(lookupMode,authConfigMap);\n        if (mapToCheck != null && mapToCheck.containsKey(AUTH_USE_OPENSHIFT_AUTH) &&\n            Boolean.valueOf((String) mapToCheck.get(AUTH_USE_OPENSHIFT_AUTH))) {\n                return validateMandatoryOpenShiftLogin(parseOpenShiftConfig(), useOpenAuthModeProp);\n        } else {\n            return null;\n        }\n    }\n\n    private AuthConfig getAuthConfigFromPluginConfiguration(LookupMode lookupMode, Map authConfig) throws MojoExecutionException {\n        Map mapToCheck = getAuthConfigMapToCheck(lookupMode,authConfig);\n\n        if (mapToCheck != null && mapToCheck.containsKey(AuthConfig.AUTH_USERNAME)) {\n            if (!mapToCheck.containsKey(AuthConfig.AUTH_PASSWORD)) {\n                throw new MojoExecutionException(\"No 'password' given while using <authConfig> in configuration for mode \" + lookupMode);\n            }\n            Map<String, String> cloneConfig = new HashMap<>(mapToCheck);\n            cloneConfig.put(AuthConfig.AUTH_PASSWORD, decrypt(cloneConfig.get(AuthConfig.AUTH_PASSWORD)));\n            return new AuthConfig(cloneConfig);\n        } else {\n            return null;\n        }\n    }\n\n    private AuthConfig getAuthConfigFromSettings(Settings settings, String user, String registry) throws MojoExecutionException {\n        Server defaultServer = null;\n        Server found;\n        for (Server server : settings.getServers()) {\n            String id = server.getId();\n\n            // Remember a default server without user as fallback for later\n            if (defaultServer == null) {\n                defaultServer = checkForServer(server, id, registry, null);\n            }\n            // Check for specific server with user part\n            found = checkForServer(server, id, registry, user);\n            if (found != null) {\n                return createAuthConfigFromServer(found);\n            }\n        }\n        return defaultServer != null ? createAuthConfigFromServer(defaultServer) : null;\n    }\n\n    private AuthConfig getAuthConfigFromDockerConfig(String registry) throws MojoExecutionException {\n        JsonObject dockerConfig = DockerFileUtil.readDockerConfig();\n        if (dockerConfig == null) {\n            return null;\n        }\n        String registryToLookup = registry != null ? registry : DOCKER_LOGIN_DEFAULT_REGISTRY;\n\n        if (dockerConfig.has(\"credHelpers\") || dockerConfig.has(\"credsStore\")) {\n            if (dockerConfig.has(\"credHelpers\")) {\n                final JsonObject credHelpers = dockerConfig.getAsJsonObject(\"credHelpers\");\n                if (credHelpers.has(registryToLookup)) {\n                    return extractAuthConfigFromCredentialsHelper(registryToLookup, credHelpers.get(registryToLookup).getAsString());\n                }\n            }\n            if (dockerConfig.has(\"credsStore\")) {\n                return extractAuthConfigFromCredentialsHelper(registryToLookup, dockerConfig.get(\"credsStore\").getAsString());\n            }\n        }\n\n        if (dockerConfig.has(\"auths\")) {\n            return extractAuthConfigFromDockerConfigAuths(registryToLookup, dockerConfig.getAsJsonObject(\"auths\"));\n        }\n\n        return null;\n    }\n\n    private AuthConfig extractAuthConfigFromDockerConfigAuths(String registryToLookup, JsonObject auths) {\n        JsonObject credentials = getCredentialsNode(auths,registryToLookup);\n        if (credentials == null || !credentials.has(\"auth\")) {\n            return null;\n        }\n        String auth = credentials.get(\"auth\").getAsString();\n        String identityToken = credentials.has(\"identitytoken\") ? credentials.get(\"identitytoken\").getAsString() : null;\n        String email = credentials.has(AuthConfig.AUTH_EMAIL) && !credentials.get(AuthConfig.AUTH_EMAIL).isJsonNull() ? credentials.get(AuthConfig.AUTH_EMAIL).getAsString() : null;\n        return new AuthConfig(auth, email, identityToken);\n    }\n\n    private AuthConfig extractAuthConfigFromCredentialsHelper(String registryToLookup, String credConfig) throws MojoExecutionException {\n        CredentialHelperClient credentialHelper = new CredentialHelperClient(log, credConfig);\n        String version = credentialHelper.getVersion();\n        log.debug(\"AuthConfig: credentials from credential helper/store %s%s\",\n                  credentialHelper.getName(),\n                  version != null ? \" version \" + version : \"\");\n        return credentialHelper.getAuthConfig(registryToLookup);\n    }\n\n    private JsonObject getCredentialsNode(JsonObject auths,String registryToLookup) {\n        if (auths.has(registryToLookup)) {\n            return auths.getAsJsonObject(registryToLookup);\n        }\n        String registryWithScheme = EnvUtil.ensureRegistryHttpUrl(registryToLookup);\n        if (auths.has(registryWithScheme)) {\n            return auths.getAsJsonObject(registryWithScheme);\n        }\n        return null;\n    }\n\n    // =======================================================================================================\n\n    private Map getAuthConfigMapToCheck(LookupMode lookupMode, Map authConfigMap) {\n        String configMapKey = lookupMode.getConfigMapKey();\n        if (configMapKey == null) {\n            return authConfigMap;\n        }\n        if (authConfigMap != null) {\n            return (Map) authConfigMap.get(configMapKey);\n        }\n        return null;\n    }\n\n    // Parse OpenShift config to get credentials, but return null if not found\n    private AuthConfig parseOpenShiftConfig() {\n        Map kubeConfig = DockerFileUtil.readKubeConfig();\n        if (kubeConfig == null) {\n            return null;\n        }\n\n        String currentContextName = (String) kubeConfig.get(\"current-context\");\n        if (currentContextName == null) {\n            return null;\n        }\n\n        for (Map contextMap : (List<Map>) kubeConfig.get(\"contexts\")) {\n            if (currentContextName.equals(contextMap.get(\"name\"))) {\n                return parseContext(kubeConfig, (Map) contextMap.get(\"context\"));\n            }\n        }\n\n        return null;\n    }\n\n    private AuthConfig parseContext(Map kubeConfig, Map context) {\n        if (context == null) {\n            return null;\n        }\n        String userName = (String) context.get(\"user\");\n        if (userName == null) {\n            return null;\n        }\n\n        List<Map> users = (List<Map>) kubeConfig.get(\"users\");\n        if (users == null) {\n            return null;\n        }\n\n        for (Map userMap : users) {\n            if (userName.equals(userMap.get(\"name\"))) {\n                return parseUser(userName, (Map) userMap.get(\"user\"));\n            }\n        }\n        return null;\n    }\n\n    private AuthConfig parseUser(String userName, Map user) {\n        if (user == null) {\n            return null;\n        }\n        String token = (String) user.get(\"token\");\n        if (token == null) {\n            return null;\n        }\n\n        // Strip off stuff after username\n        Matcher matcher = Pattern.compile(\"^([^/]+).*$\").matcher(userName);\n        return new AuthConfig(matcher.matches() ? matcher.group(1) : userName,\n                              token, null, null);\n    }\n\n    private AuthConfig validateMandatoryOpenShiftLogin(AuthConfig openShiftAuthConfig, String useOpenAuthModeProp) throws MojoExecutionException {\n        if (openShiftAuthConfig != null) {\n            return openShiftAuthConfig;\n        }\n        // No login found\n        String kubeConfigEnv = System.getenv(\"KUBECONFIG\");\n        throw new MojoExecutionException(\n            String.format(\"System property %s set, but not active user and/or token found in %s. \" +\n                          \"Please use 'oc login' for connecting to OpenShift.\",\n                          useOpenAuthModeProp, kubeConfigEnv != null ? kubeConfigEnv : \"~/.kube/config\"));\n\n    }\n\n\n    private Server checkForServer(Server server, String id, String registry, String user) {\n\n        String[] registries = registry != null ? new String[] { registry } : DEFAULT_REGISTRIES;\n        for (String reg : registries) {\n            if (id.equals(user == null ? reg : reg + \"/\" + user)) {\n                return server;\n            }\n        }\n        return null;\n    }\n\n    private String decrypt(String password) throws MojoExecutionException {\n        try {\n            // Done by reflection since I have classloader issues otherwise\n            Object secDispatcher = container.lookup(SecDispatcher.ROLE, \"maven\");\n            Method method = secDispatcher.getClass().getMethod(\"decrypt\",String.class);\n            return (String) method.invoke(secDispatcher,password);\n        } catch (ComponentLookupException e) {\n            throw new MojoExecutionException(\"Error looking security dispatcher\",e);\n        } catch (ReflectiveOperationException e) {\n            throw new MojoExecutionException(\"Cannot decrypt password: \" + e.getCause(),e);\n        }\n    }\n\n    private AuthConfig createAuthConfigFromServer(Server server) throws MojoExecutionException {\n        return new AuthConfig(\n                server.getUsername(),\n                decrypt(server.getPassword()),\n                extractFromServerConfiguration(server.getConfiguration(), AuthConfig.AUTH_EMAIL),\n                extractFromServerConfiguration(server.getConfiguration(), AuthConfig.AUTH_AUTH)\n        );\n    }\n\n    private String extractFromServerConfiguration(Object configuration, String prop) {\n        if (configuration != null) {\n            Xpp3Dom dom = (Xpp3Dom) configuration;\n            Xpp3Dom element = dom.getChild(prop);\n            if (element != null) {\n                return element.getValue();\n            }\n        }\n        return null;\n    }\n\n    // ========================================================================================\n    // Mode which direction to lookup (pull, push or default value for both, pull and push)\n\n    private LookupMode getLookupMode(boolean isPush) {\n        return isPush ? LookupMode.PUSH : LookupMode.PULL;\n    }\n\n    private enum LookupMode {\n        PUSH(\"docker.push.\",\"push\"),\n        PULL(\"docker.pull.\",\"pull\"),\n        REGISTRY(\"registry.\",null),\n        DEFAULT(\"docker.\",null);\n\n        private final String sysPropPrefix;\n        private String configMapKey;\n\n        LookupMode(String sysPropPrefix,String configMapKey) {\n            this.sysPropPrefix = sysPropPrefix;\n            this.configMapKey = configMapKey;\n        }\n\n        public String asSysProperty(String prop) {\n            return sysPropPrefix + prop;\n        }\n\n        public String getConfigMapKey() {\n            return configMapKey;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/AutoPullMode.java",
    "content": "package io.fabric8.maven.docker.util;\n/*-\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.*;\n\n/**\n * Enum holding the possible values avalaible for auto-pulling.\n *\n * @author roland\n * @since 01/03/15\n */\npublic enum AutoPullMode {\n\n    ON(true, \"on\", \"true\"),\n    ONCE(true, \"once\"),\n    OFF(false, \"off\", \"false\"),\n    ALWAYS(true, \"always\");\n\n    private Set<String> values = new HashSet<>();\n    private boolean doPullIfNotPresent;\n\n    AutoPullMode(boolean doPullIfNotPresent, String... vals) {\n        this.doPullIfNotPresent = doPullIfNotPresent;\n        Collections.addAll(values, vals);\n    }\n\n    public boolean doPullIfNotPresent() {\n        return doPullIfNotPresent;\n    }\n\n    public boolean alwaysPull() {\n        return (this == ONCE || this == ALWAYS);\n    }\n\n    static public AutoPullMode fromString(String val) {\n        String valNorm = val.toLowerCase();\n        for (AutoPullMode mode : values()) {\n            if (mode.values.contains(valNorm)) {\n                return mode;\n            }\n        }\n        throw new IllegalArgumentException(\"Invalid auto-pull mode \" + val + \". Please use 'on', 'off', 'once' or 'always'.\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport com.google.common.collect.ImmutableSet;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.model.Container;\n\n/**\n * @author marcus\n * @since 1.0.0\n */\npublic class ContainerNamingUtil {\n\n    private static final String INDEX_PLACEHOLDER = \"%i\";\n\n    public static final String DEFAULT_CONTAINER_NAME_PATTERN = \"%n-%i\";\n\n    // class with only static methods\n    private ContainerNamingUtil() { }\n\n    public static String formatContainerName(final ImageConfiguration image,\n                                             final String defaultContainerNamePattern,\n                                             final Date buildTimestamp,\n                                             final Collection<Container> existingContainers) {\n\n        String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern);\n        Set<String> existingContainersNames = extractContainerNames(existingContainers);\n\n        final String partiallyApplied =\n            replacePlaceholders(\n                containerNamePattern,\n                image.getName(),\n                image.getAlias(),\n                buildTimestamp);\n\n\n        if (partiallyApplied.contains(INDEX_PLACEHOLDER)) {\n            for (long i = 1; i < Long.MAX_VALUE; i++) {\n                final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i));\n                if (!existingContainersNames.contains(withIndexApplied)) {\n                    return withIndexApplied;\n                }\n            }\n            throw new IllegalStateException(\"Could not find any free container name for pattern \" + partiallyApplied);\n        } else {\n            return partiallyApplied;\n        }\n    }\n\n    /**\n     * Keep only the entry with the higest index if an indexed naming scheme for container has been chosen.\n     * @param image the image from which to the the container pattern\n     * @param buildTimestamp the timestamp for the build\n     * @param containers the list of existing containers\n     * @return a list with potentially lower indexed entries removed\n     */\n    public static Collection<Container> getContainersToStop(final ImageConfiguration image,\n                                                            final String defaultContainerNamePattern,\n                                                            final Date buildTimestamp,\n                                                            final Collection<Container> containers) {\n\n        String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern);\n\n        // Only special treatment for indexed container names\n        if (!containerNamePattern.contains(INDEX_PLACEHOLDER)) {\n            return containers;\n        }\n\n        final String partiallyApplied =\n            replacePlaceholders(\n                containerNamePattern,\n                image.getName(),\n                image.getAlias(),\n                buildTimestamp);\n\n        return keepOnlyLastIndexedContainer(containers, partiallyApplied);\n\n    }\n\n    // ========================================================================================================\n\n    private static String replacePlaceholders(String containerNamePattern, String imageName, String nameAlias, Date buildTimestamp) {\n\n        Map<String, FormatParameterReplacer.Lookup> lookups = new HashMap<>();\n\n        lookups.put(\"a\", () -> nameAlias);\n        lookups.put(\"n\", () -> cleanImageName(imageName));\n        lookups.put(\"t\", () -> String.valueOf(buildTimestamp.getTime()));\n        lookups.put(\"i\", () -> INDEX_PLACEHOLDER);\n\n        return new FormatParameterReplacer(lookups).replace(containerNamePattern);\n    }\n\n\n    // Filter out any older indexed containernames, keeping only the last one (i.e. with the highest index)\n    private static Collection<Container> keepOnlyLastIndexedContainer(Collection<Container> existingContainers, final String partiallyApplied) {\n\n        Collection<Container> result = new ArrayList<>(existingContainers);\n\n        // No index place holder, so nothing to filters\n        if (!partiallyApplied.contains(INDEX_PLACEHOLDER)) {\n            return result;\n        }\n\n        Map<String,Container> containerMap = existingContainers.stream().collect(Collectors.toMap(Container::getName, Function.identity()));\n\n        Container last = null;\n        for (long i = 1; i < Long.MAX_VALUE; i++) {\n            final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i));\n            Container mapped = containerMap.get(withIndexApplied);\n            if (mapped != null) {\n                result.remove(mapped);\n                last = mapped;\n            } else {\n                // Readd last one removed (if any)\n                if (last != null) {\n                    result.add(last);\n                }\n                return result;\n            }\n        }\n        throw new IllegalStateException(\"Internal error: Cannot find a free container index slot in \" + existingContainers);\n    }\n\n    private static Set<String> extractContainerNames(final Collection<Container> existingContainers) {\n        final ImmutableSet.Builder<String> containerNamesBuilder = ImmutableSet.builder();\n        for (final Container container : existingContainers) {\n            containerNamesBuilder.add(container.getName());\n        }\n        return containerNamesBuilder.build();\n    }\n\n    private static String extractContainerNamePattern(ImageConfiguration image, String defaultContainerNamePattern) {\n        RunImageConfiguration runConfig = image.getRunConfiguration();\n        if (runConfig != null) {\n            if (runConfig.getContainerNamePattern() != null) {\n                return runConfig.getContainerNamePattern();\n            }\n            if (runConfig.getNamingStrategy() == RunImageConfiguration.NamingStrategy.alias) {\n                return \"%a\";\n            }\n        }\n        return defaultContainerNamePattern != null ? defaultContainerNamePattern : DEFAULT_CONTAINER_NAME_PATTERN;\n    }\n\n    private static String cleanImageName(final String imageName) {\n        return new ImageName(imageName).getSimpleName().replaceAll(\"[^a-zA-Z0-9_.-]+\", \"_\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/CredentialHelperClient.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.common.base.Joiner;\nimport com.google.common.collect.Lists;\nimport com.google.gson.JsonObject;\n\nimport org.apache.maven.plugin.MojoExecutionException;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.util.ExternalCommand;\n\npublic class CredentialHelperClient {\n\n    static final String SECRET_KEY = \"Secret\";\n    static final String USERNAME_KEY = \"Username\";\n    static final String TOKEN_USERNAME = \"<token>\";\n\n    private final String credentialHelperName;\n    private final Logger log;\n\n    public CredentialHelperClient(Logger log, String credentialsStore) {\n        this.log = log;\n        credentialHelperName = \"docker-credential-\" + credentialsStore;\n    }\n\n    public String getName() {\n        return credentialHelperName;\n    }\n\n    public String getVersion() {\n        try {\n            return new VersionCommand().getVersion();\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    public AuthConfig getAuthConfig(String registryToLookup) throws MojoExecutionException {\n        try {\n            JsonObject creds = new GetCommand().getCredentialNode(registryToLookup);\n            if (creds == null) {\n                creds = new GetCommand().getCredentialNode(EnvUtil.ensureRegistryHttpUrl(registryToLookup));\n            }\n            return toAuthConfig(creds);\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Error getting the credentials for \" + registryToLookup + \" from the configured credential helper\",e);\n        }\n    }\n\n    AuthConfig toAuthConfig(JsonObject credential){\n        if (credential == null) {\n            return null;\n        }\n        String password = credential.get(CredentialHelperClient.SECRET_KEY).getAsString();\n        String userKey = credential.get(CredentialHelperClient.USERNAME_KEY).getAsString();\n        if(TOKEN_USERNAME.equals(userKey)) {\n            // If userKey is <token>, the password is actually a token\n            return new AuthConfig(null, null, null, null, password);\n        }\n\n        return new AuthConfig(userKey,password, null,null);\n    }\n\n    // docker-credential-XXX version\n    private class VersionCommand extends ExternalCommand {\n\n        private String version;\n\n        VersionCommand() {\n            super(CredentialHelperClient.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            return new String[]{CredentialHelperClient.this.credentialHelperName,\"version\"};\n        }\n\n        @Override\n        protected void processLine(String line) {\n            log.verbose(Logger.LogVerboseCategory.BUILD,\"Credentials helper reply for \\\"%s\\\" is %s\",CredentialHelperClient.this.credentialHelperName,line);\n            version = line;\n        }\n\n        public String getVersion() throws IOException {\n            execute();\n            return version;\n        }\n    }\n\n    // echo <registryToLookup> | docker-credential-XXX get\n    private class GetCommand extends ExternalCommand {\n\n        private List<String> reply = Lists.newLinkedList();\n\n        GetCommand() {\n            super(CredentialHelperClient.this.log);\n        }\n\n        @Override\n        protected String[] getArgs() {\n            return new String[]{CredentialHelperClient.this.credentialHelperName,\"get\"};\n        }\n\n        @Override\n        protected void processLine(String line) {\n            reply.add(line);\n        }\n\n        public JsonObject getCredentialNode(String registryToLookup) throws IOException {\n            try {\n                execute(registryToLookup);\n            } catch (IOException ex) {\n                if (getStatusCode() == 1) {\n                    return null;\n                } else {\n                    throw ex;\n                }\n            }\n            JsonObject credentials = JsonFactory.newJsonObject(Joiner.on('\\n').join(reply));\n            if (!credentials.has(SECRET_KEY) || !credentials.has(USERNAME_KEY)) {\n                return null;\n            }\n            return credentials;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/DeepCopy.java",
    "content": "package io.fabric8.maven.docker.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.*;\n\npublic class DeepCopy {\n\n    /**\n     * Returns a copy of the object, or null if the object cannot\n     * be serialized.\n     */\n    public static <T> T copy(T orig) {\n        if (orig == null) {\n            return null;\n        }\n        try {\n            // Write the object out to a byte array\n            ByteArrayOutputStream fbos = new ByteArrayOutputStream();\n\n            try (ObjectOutputStream out = new ObjectOutputStream(fbos)) {\n                out.writeObject(orig);\n                out.flush();\n            }\n\n            // Retrieve an input stream from the byte array and read\n            // a copy of the object back in.\n            try (ByteArrayInputStream fbis = new ByteArrayInputStream(fbos.toByteArray());\n                 ObjectInputStream in = new ObjectInputStream(fbis))  {\n                return (T) in.readObject();\n            }\n        } catch (IOException | ClassNotFoundException e) {\n            throw new IllegalStateException(\"Cannot copy \" + orig, e);\n        }\n    };\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/DockerFileUtil.java",
    "content": "package io.fabric8.maven.docker.util;/*\n *\n * Copyright 2015 Roland Huss\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\nimport java.io.*;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport org.apache.maven.plugins.assembly.interpolation.AssemblyInterpolator;\nimport org.apache.maven.plugins.assembly.io.DefaultAssemblyReader;\nimport org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;\n\nimport io.fabric8.maven.docker.assembly.DockerAssemblyConfigurationSource;\nimport org.yaml.snakeyaml.Yaml;\n\n\n/**\n * Utility class for dealing with dockerfiles\n * @author roland\n * @since 21/01/16\n */\npublic class DockerFileUtil {\n\n    private DockerFileUtil() {}\n\n    /**\n     * Extract the base images from a dockerfile. All lines containing a <code>FROM</code> is\n     * taken.\n     *\n     * @param dockerFile file from where to extract the base image\n     * @param interpolator interpolator for replacing properties\n     * @return LinkedList of base images name or empty collection if none is found.\n     */\n    public static List<String> extractBaseImages(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException {\n        List<String[]> fromLines = extractLines(dockerFile, \"FROM\", interpolator);\n        Map<String, String> args = extractArgs(dockerFile, interpolator);\n        Set<String> result = new LinkedHashSet<>();\n        Set<String> fromAlias = new HashSet<>();\n        for (String[] fromLine :  fromLines) {\n            if (fromLine.length > 1) {\n                if (fromLine.length == 2) { // FROM image:tag use case\n                    result.add(resolveImageTagFromArgs(fromLine[1], args));\n                } else if (fromLine.length == 4) { // FROM image:tag AS alias use case\n                    if (!fromAlias.contains(fromLine[1])) {\n                        result.add(resolveImageTagFromArgs(fromLine[1], args));\n                    }\n                    fromAlias.add(resolveImageTagFromArgs(fromLine[3], args));\n                }\n            }\n        }\n        return new ArrayList<>(result);\n    }\n\n    /**\n     * Extract Args from dockerfile. All lines containing ARG is taken.\n     *\n     * @param dockerfile Docker File\n     * @param interpolator interpolator for replacement\n     * @return HashMap of arguments or empty collection if none is found\n     */\n    public static Map<String, String> extractArgs(File dockerfile, FixedStringSearchInterpolator interpolator) throws IOException {\n        return extractArgsFromLines(extractLines(dockerfile, \"ARG\", interpolator));\n    }\n\n    /**\n     * Extract all lines containing the given keyword\n     *\n     * @param dockerFile dockerfile to examine\n     * @param keyword keyword to extract the lines for\n     * @param interpolator interpolator for replacing properties\n     * @return list of matched lines or an empty list\n     */\n    public static List<String[]> extractLines(File dockerFile, String keyword, FixedStringSearchInterpolator interpolator) throws IOException {\n        List<String[]> ret = new ArrayList<>();\n        try (BufferedReader reader = new BufferedReader(new FileReader(dockerFile))) {\n            String line;\n            while ((line = reader.readLine()) != null) {\n                String lineInterpolated = interpolator.interpolate(line);\n                String[] lineParts = lineInterpolated.split(\"\\\\s+\");\n                if (lineParts.length > 0 && lineParts[0].equalsIgnoreCase(keyword)) {\n                    ret.add(lineParts);\n                }\n            }\n        }\n        return ret;\n    }\n\n    /**\n     * Interpolate a docker file with the given properties and filter\n     *\n     * @param dockerFile docker file to interpolate\n     * @param interpolator interpolator for replacing properties\n     * @return The interpolated contents of the file.\n     * @throws IOException\n     */\n    public static String interpolate(File dockerFile, FixedStringSearchInterpolator interpolator) throws IOException {\n        StringBuilder ret = new StringBuilder();\n        try (BufferedReader reader = new BufferedReader(new FileReader(dockerFile))) {\n            String line;\n            while ((line = reader.readLine()) != null) {\n                ret.append(interpolator.interpolate(line)).append(System.lineSeparator());\n            }\n        }\n        return ret.toString();\n    }\n\n    /**\n     * Create an interpolator for the given maven parameters and filter configuration.\n     *\n     * @param params The maven parameters.\n     * @param filter The filter configuration.\n     * @return An interpolator for replacing maven properties.\n     */\n    public static FixedStringSearchInterpolator createInterpolator(MojoParameters params, String filter) {\n        String[] delimiters = extractDelimiters(filter);\n        if (delimiters == null) {\n            // Don't interpolate anything\n            return FixedStringSearchInterpolator.create();\n        }\n\n        DockerAssemblyConfigurationSource configSource = new DockerAssemblyConfigurationSource(params, null, null);\n        // Patterned after org.apache.maven.plugins.assembly.interpolation.AssemblyExpressionEvaluator\n        return AssemblyInterpolator\n                .fullInterpolator(params.getProject(),\n                        DefaultAssemblyReader.createProjectInterpolator(params.getProject())\n                          .withExpressionMarkers(delimiters[0], delimiters[1]), configSource)\n                .withExpressionMarkers(delimiters[0], delimiters[1]);\n    }\n\n    /**\n     * Helper method for extractArgs(exposed for test)\n     *\n     * @param argLines list of string arrays containing lines with words\n     * @return map of parsed arguments\n     */\n    protected static Map<String, String> extractArgsFromLines(List<String[]> argLines) {\n        Map<String, String> result = new HashMap<>();\n        for (String[] argLine : argLines) {\n            if (argLine.length > 1) {\n                updateMapWithArgValue(result, argLine[1]);\n            }\n        }\n        return result;\n    }\n\n    private static String resolveImageTagFromArgs(String imageTagString, Map<String, String> args) {\n        if (imageTagString.startsWith(\"$\")) { // FROM $IMAGE\n            String resolvedVal = resolveArgValueFromStrContainingArgKey(imageTagString, args);\n            if (resolvedVal != null) {\n                return resolvedVal;\n            }\n        } else { // FROM image:$TAG_ARG\n            String[] imageTagArr = imageTagString.split(\":\");\n            if (imageTagArr.length > 1) {\n                String tag = resolveArgValueFromStrContainingArgKey(imageTagArr[1], args);\n                if (tag != null) {\n                    return imageTagArr[0] + \":\" + tag;\n                }\n            }\n        }\n        return imageTagString;\n    }\n\n    private static String resolveArgValueFromStrContainingArgKey(String argString, Map<String, String> args) {\n        if (argString.startsWith(\"$\") && args.containsKey(argString.substring(1))) {\n            return args.get(argString.substring(1));\n        }\n        return null;\n    }\n\n    private static Reader getFileReaderFromDir(File file) {\n        if (file.exists() && file.length() != 0) {\n            try {\n                return new FileReader(file);\n            } catch (FileNotFoundException e) {\n                // Shouldnt happen. Nevertheless ...\n                throw new IllegalStateException(\"Cannot find \" + file,e);\n            }\n        } else {\n            return null;\n        }\n    }\n\n    public static JsonObject readDockerConfig() {\n        String dockerConfig = System.getenv(\"DOCKER_CONFIG\");\n\n        Reader reader = dockerConfig == null\n                ? getFileReaderFromDir(new File(getHomeDir(),\".docker/config.json\"))\n                : getFileReaderFromDir(new File(dockerConfig,\"config.json\"));\n        return reader != null ? new Gson().fromJson(reader, JsonObject.class) : null;\n    }\n\n    public static String[] extractDelimiters(String filter) {\n        if (filter == null ||\n            filter.equalsIgnoreCase(\"false\") ||\n            filter.equalsIgnoreCase(\"none\")) {\n            return null;\n        }\n        if (filter.contains(\"*\")) {\n            Matcher matcher = Pattern.compile(\"^(?<start>[^*]+)\\\\*(?<end>.*)$\").matcher(filter);\n            if (matcher.matches()) {\n                return new String[] { matcher.group(\"start\"), matcher.group(\"end\") };\n            }\n        }\n        return new String[] { filter, filter };\n    }\n\n    public static Map<String,?> readKubeConfig() {\n        String kubeConfig = System.getenv(\"KUBECONFIG\");\n\n        Reader reader = kubeConfig == null\n                ? getFileReaderFromDir(new File(getHomeDir(),\".kube/config\"))\n                : getFileReaderFromDir(new File(kubeConfig));\n        if (reader != null) {\n            Yaml ret = new Yaml();\n            return (Map<String, ?>) ret.load(reader);\n        }\n        return null;\n    }\n\n    private static File getHomeDir() {\n        String homeDir = System.getProperty(\"user.home\");\n        if (homeDir == null) {\n            homeDir = System.getenv(\"HOME\");\n        }\n        return new File(homeDir);\n    }\n\n    private static void updateMapWithArgValue(Map<String, String> result, String argString) {\n        if (argString.contains(\"=\") || argString.contains(\":\")) {\n            String[] argStringParts = argString.split(\"[=:]\");\n            String argStringValue = argString.substring(argStringParts[0].length() + 1);\n            if (argStringValue.startsWith(\"\\\"\") || argStringValue.startsWith(\"'\")) {\n                // Replaces surrounding quotes\n                argStringValue = argStringValue.replaceAll(\"^\\\"|\\\"|'|'$\", \"\");\n            } else {\n                validateArgValue(argStringValue);\n            }\n            result.put(argStringParts[0], argStringValue);\n        } else {\n            validateArgValue(argString);\n            result.put(argString.split(\"\\\\s+\")[0], \"\");\n        }\n    }\n\n    private static void validateArgValue(String argStringParam) {\n        String[] argStringParts = argStringParam.split(\"\\\\s+\");\n        if (argStringParts.length > 1) {\n            throw new IllegalArgumentException(\"Dockerfile parse error: ARG requires exactly one argument. Provided : \" + argStringParam);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/DockerPathUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Docker path resolution and manipulation utility methods.\n * <p>\n * This class provides methods for manipulating paths <em>as they appear in docker-compose or Dockerfiles</em>.  This\n * class does not provide for generic path manipulation across platforms or file systems.  Paths that appear in Docker\n * configurations use forward slash as a separator character, so this class makes no provisions for handling Windows\n * platform path semantics (e.g. the presence of drive letters or backward slash).\n * </p>\n */\npublic class DockerPathUtil {\n\n    /**\n     * Resolves the supplied resource (a path or directory on the filesystem) relative the supplied {@code\n     * baseDir}.  The returned {@code File} is guaranteed to be {@link File#isAbsolute() absolute}.  The returned file\n     * is <em>not</em> guaranteed to exist.\n     * <p>\n     * If the supplied {@code pathToResolve} is already {@link File#isAbsolute() absolute}, then it is returned\n     * <em>unmodified</em>.  Otherwise, the {@code pathToResolve} is returned as an absolute {@code File} using the\n     * supplied {@code baseDir} as its parent.\n     * </p>\n     *\n     * @param pathToResolve represents a filesystem resource, which may be an absolute path\n     * @param baseDir the absolute path used to resolve non-absolute path resources; <em>must</em> be absolute\n     * @return an absolute {@code File} reference to {@code pathToResolve}; <em>not</em> guaranteed to exist\n     * @throws IllegalArgumentException if the supplied {@code baseDir} does not represent an absolute path\n     */\n    public static File resolveAbsolutely(String pathToResolve, String baseDir) {\n        // TODO: handle the case where pathToResolve specifies a non-existent path, for example, a base directory equal to \"/\" and a relative path of \"../../foo\".\n        File fileToResolve = new File(pathToResolve);\n\n        if (fileToResolve.isAbsolute()) {\n            return fileToResolve;\n        }\n\n        if (baseDir == null) {\n            throw new IllegalArgumentException(\"Cannot resolve relative path '\" + pathToResolve + \"' with a \" +\n                    \"null base directory.\");\n        }\n\n        File baseDirAsFile = new File(baseDir);\n        if (!baseDirAsFile.isAbsolute()) {\n            throw new IllegalArgumentException(\"Base directory '\" + baseDirAsFile + \"' must be absolute\");\n        }\n\n        final File toCanonicalize = new File(baseDirAsFile, pathToResolve);\n        try {\n            return toCanonicalize.getCanonicalFile();\n        } catch (IOException e) {\n            throw new RuntimeException(\"Unable to canonicalize the file path '\" + toCanonicalize + \"'\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/EnvUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.base.*;\nimport com.google.common.collect.Iterables;\nimport com.google.common.collect.Lists;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.utils.io.FileUtils;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\nimport static java.util.concurrent.TimeUnit.*;\n\n/**\n * Utility class for various (loosely related) environment related tasks.\n *\n * @author roland\n * @since 04.04.14\n */\npublic class EnvUtil {\n\n    public static final String MAVEN_PROPERTY_REGEXP = \"\\\\s*\\\\$\\\\{\\\\s*([^}]+)\\\\s*}\\\\s*$\";\n\n    // Standard HTTPS port (IANA registered) is 2376.\n    // The other port 2375 with plain HTTP is used only in older docker installations.\n    public static final String DOCKER_HTTP_PORT = \"2375\";\n\n    public static final String PROPERTY_COMBINE_POLICY_SUFFIX = \"_combine\";\n\n    private EnvUtil() {}\n\n    // Convert docker host URL to an HTTP(s) URL\n    public static String convertTcpToHttpUrl(String connect) {\n        String protocol = connect.contains(\":\" + DOCKER_HTTP_PORT) ? \"http:\" : \"https:\";\n        return connect.replaceFirst(\"^tcp:\", protocol);\n    }\n\n    /**\n     * Compare to version strings and return the larger version strings. This is used in calculating\n     * the minimal required API version for this plugin. Version strings must be comparable as floating numbers.\n     * The versions must be given in the format in a semantic version foramt (e.g. \"1.23\"\n     *\n     * If either version is <code>null</code>, the other version is returned (which can be null as well)\n     *\n     * @param versionA first version number\n     * @param versionB second version number\n     * @return the larger version number\n     */\n    public static String extractLargerVersion(String versionA, String versionB) {\n        if (versionB == null || versionA == null) {\n            return versionA == null ? versionB : versionA;\n        } else {\n            String partsA[] = versionA.split(\"\\\\.\");\n            String partsB[] = versionB.split(\"\\\\.\");\n            for (int i = 0; i < (partsA.length < partsB.length ? partsA.length : partsB.length); i++) {\n                int pA = Integer.parseInt(partsA[i]);\n                int pB = Integer.parseInt(partsB[i]);\n                if (pA > pB) {\n                    return versionA;\n                } else if (pB > pA) {\n                    return versionB;\n                }\n            }\n            return partsA.length > partsB.length ? versionA : versionB;\n        }\n    }\n\n    /**\n     * Check whether the first given API version is larger or equals the second given version\n     *\n     * @param versionA first version to check against\n     * @param versionB the second version\n     * @return true if versionA is greater or equals versionB, false otherwise\n     */\n    public static boolean greaterOrEqualsVersion(String versionA, String versionB) {\n        String largerVersion = extractLargerVersion(versionA, versionB);\n        return largerVersion != null && largerVersion.equals(versionA);\n    }\n\n    private static final Function<String, String[]> SPLIT_ON_LAST_COLON = new Function<String, String[]>() {\n        @Override\n        public String[] apply(String element) {\n          int colon = element.lastIndexOf(':');\n          if (colon < 0) {\n              return new String[] {element, element};\n          } else {\n              return new String[] {element.substring(0, colon), element.substring(colon + 1)};\n          }\n        }\n    };\n\n    /**\n     * Splits every element in the given list on the last colon in the name and returns a list with\n     * two elements: The left part before the colon and the right part after the colon. If the string\n     * doesn't contain a colon, the value is used for both elements in the returned arrays.\n     *\n     * @param listToSplit list of strings to split\n     * @return return list of 2-element arrays or an empty list if the given list is empty or null\n     */\n    public static List<String[]> splitOnLastColon(List<String> listToSplit) {\n        if (listToSplit != null) {\n          return Lists.transform(listToSplit, SPLIT_ON_LAST_COLON);\n        }\n        return Collections.emptyList();\n    }\n\n    private static final Function<String, Iterable<String>> COMMA_SPLITTER = new Function<String, Iterable<String>>() {\n        private Splitter COMMA_SPLIT = Splitter.on(\",\").trimResults().omitEmptyStrings();\n\n        @Override\n        public Iterable<String> apply(String input) {\n            return COMMA_SPLIT.split(input);\n        }\n    };\n\n    private static final Predicate<String> NOT_EMPTY = new Predicate<String>() {\n        @Override\n        public boolean apply(@Nullable String s) {\n            return s!=null && !s.isEmpty();\n        }\n    };\n\n    private static final Function<String,String> TRIM = new Function<String,String>() {\n        @Nullable\n        @Override\n        public String apply(@Nullable String s) {\n            return s!=null ?s.trim() :s;\n        }\n    };\n\n    /**\n     * Remove empty members of a list.\n     * @param input A list of String\n     * @return A list of Non-Empty (length>0) String\n     */\n    @Nonnull\n    public static List<String> removeEmptyEntries(@Nullable List<String> input) {\n        if(input==null) {\n            return Collections.emptyList();\n        }\n        Iterable<String> trimmedInputs = Iterables.transform(input, TRIM);\n        Iterable<String> nonEmptyInputs = Iterables.filter(trimmedInputs, NOT_EMPTY);\n        return Lists.newArrayList(nonEmptyInputs);\n    }\n\n    /**\n     * Split each element of an Iterable<String> at commas.\n     * @param input Iterable over strings.\n     * @return An Iterable over string which breaks down each input element at comma boundaries\n     */\n    @Nonnull\n    public static List<String> splitAtCommasAndTrim(Iterable<String> input) {\n        if(input==null) {\n            return Collections.emptyList();\n        }\n        Iterable<String> nonEmptyInputs = Iterables.filter(input, Predicates.notNull());\n        return Lists.newArrayList(Iterables.concat(Iterables.transform(nonEmptyInputs, COMMA_SPLITTER)));\n    }\n\n    public static String[] splitOnSpaceWithEscape(String toSplit) {\n        String[] split = toSplit.split(\"(?<!\" + Pattern.quote(\"\\\\\") + \")\\\\s+\");\n        String[] res = new String[split.length];\n        for (int i = 0; i < split.length; i++) {\n            res[i] = split[i].replaceAll(\"\\\\\\\\ \",\" \");\n        }\n        return res;\n    }\n\n\n    /**\n     * Join a list of objects to a string with a given separator by calling Object.toString() on the elements.\n     *\n     * @param list to join\n     * @param separator separator to use\n     * @return the joined string.\n     */\n    public static String stringJoin(List list, String separator) {\n        StringBuilder ret = new StringBuilder();\n        boolean first = true;\n        for (Object o : list) {\n            if (!first) {\n                ret.append(separator);\n            }\n            ret.append(o);\n            first = false;\n        }\n        return ret.toString();\n    }\n\n    /**\n     * Return all properties in Maven project, merged with all System properties (-D flags sent to Maven).\n     *\n     * System properties always takes precedence.\n     *\n     * @param project Project to extract Properties from\n     * @return\n     */\n    public static Properties getPropertiesWithSystemOverrides(MavenProject project) {\n        Properties properties = new Properties(project.getProperties());\n        properties.putAll(System.getProperties());\n        return properties;\n    }\n\n    /**\n     * Extract part of given properties as a map. The given prefix is used to find the properties,\n     * the rest of the property name is used as key for the map.\n     *\n     * NOTE: If key is \"._combine\" ({@link #PROPERTY_COMBINE_POLICY_SUFFIX)} it is ignored! This is reserved for combine policy tweaking.\n     *\n     * @param prefix prefix which specifies the part which should be extracted as map\n     * @param properties properties to extract from\n     * @return the extracted map or null if no such map exists\n     */\n    public static Map<String, String> extractFromPropertiesAsMap(String prefix, Properties properties) {\n        Map<String, String> ret = new HashMap<>();\n        Enumeration names = properties.propertyNames();\n        String prefixP = prefix + \".\";\n        while (names.hasMoreElements()) {\n            String propName = (String) names.nextElement();\n            if (propMatchesPrefix(prefixP, propName)) {\n                String mapKey = propName.substring(prefixP.length());\n                if(PROPERTY_COMBINE_POLICY_SUFFIX.equals(mapKey)) {\n                    continue;\n                }\n\n                ret.put(mapKey, properties.getProperty(propName));\n            }\n        }\n        return ret.size() > 0 ? ret : null;\n    }\n\n    /**\n     * Extract from given properties a list of string values. The prefix is used to determine the subset of the\n     * given properties from which the list should be extracted, the rest is used as a numeric index. If the rest\n     * is not numeric, the order is not determined (all those props are appended to the end of the list)\n     *\n     * NOTE: If suffix/index is \"._combine\" ({@link #PROPERTY_COMBINE_POLICY_SUFFIX)} it is ignored!\n     * This is reserved for combine policy tweaking.\n     *\n     * @param prefix for selecting the properties from which the list should be extracted\n     * @param properties properties from which to extract from\n     * @return parsed list or null if no element with prefixes exists\n     */\n    public static List<String> extractFromPropertiesAsList(String prefix, Properties properties) {\n        TreeMap<Integer,String> orderedMap = new TreeMap<>();\n        List<String> rest = new ArrayList<>();\n        Enumeration names = properties.propertyNames();\n        String prefixP = prefix + \".\";\n        while (names.hasMoreElements()) {\n            String key = (String) names.nextElement();\n            if (propMatchesPrefix(prefixP, key)) {\n                String index = key.substring(prefixP.length());\n\n                if(PROPERTY_COMBINE_POLICY_SUFFIX.equals(index)) {\n                    continue;\n                }\n\n                String value = properties.getProperty(key);\n                try {\n                    Integer nrIndex = Integer.parseInt(index);\n                    orderedMap.put(nrIndex,value);\n                } catch (NumberFormatException exp) {\n                    rest.add(value);\n                }\n            }\n        }\n        List<String> ret = new ArrayList<>(orderedMap.values());\n        ret.addAll(rest);\n        return ret.size() > 0 ? ret : null;\n    }\n\n    /**\n     * Extract from a Maven property which is in the form ${name} the name.\n     *\n     * @param propName property name to extrat\n     * @return the pure name or null if this is not a property name\n     */\n    public static String extractMavenPropertyName(String propName) {\n        Matcher matcher = Pattern.compile(MAVEN_PROPERTY_REGEXP).matcher(propName);\n        if (matcher.matches()) {\n            return matcher.group(1);\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Fix path on Windows machines, i.e. convert 'c:\\...\\' to '/c/..../'\n     *\n     * @param path path to fix\n     * @return the fixed path\n     */\n    public static String fixupPath(String path) {\n        // Hack-fix for mounting on Windows where the ${projectDir} variable and other\n        // contain backslashes and what not. Related to #188\n        Pattern pattern = Pattern.compile(\"^(?i)([A-Z]):(.*)$\");\n        Matcher matcher = pattern.matcher(path);\n        if (matcher.matches()) {\n            String result = \"/\" + matcher.group(1).toLowerCase() + matcher.group(2);\n            return result.replace(\"\\\\\",\"/\");\n        }\n        return path;\n    }\n\n    /**\n     * Calculate the duration between now and the given time\n     *\n     * Taken mostly from http://stackoverflow.com/a/5062810/207604 . Kudos to @dblevins\n     *\n     * @param start starting time (in milliseconds)\n     * @return time in seconds\n     *\n     */\n    public static String formatDurationTill(long start) {\n        long duration = System.currentTimeMillis() - start;\n        StringBuilder res = new StringBuilder();\n\n        TimeUnit current = HOURS;\n\n        while (duration > 0) {\n            long temp = current.convert(duration, MILLISECONDS);\n\n            if (temp > 0) {\n                duration -= current.toMillis(temp);\n                res.append(temp).append(\" \").append(current.name().toLowerCase());\n                if (temp < 2) res.deleteCharAt(res.length() - 1);\n                res.append(\", \");\n            }\n            if (current == SECONDS) {\n                break;\n            }\n            current = TimeUnit.values()[current.ordinal() - 1];\n        }\n        if (res.lastIndexOf(\", \") < 0) {\n            return duration + \" \" + MILLISECONDS.name().toLowerCase();\n        }\n        res.deleteCharAt(res.length() - 2);\n        int i = res.lastIndexOf(\", \");\n        if (i > 0) {\n            res.deleteCharAt(i);\n            res.insert(i, \" and\");\n        }\n\n        return res.toString();\n    }\n\n    // ======================================================================================================\n\n    private static boolean propMatchesPrefix(String prefix, String key) {\n        return key.startsWith(prefix) && key.length() >= prefix.length();\n    }\n\n    /**\n     * Return the first non null registry given. Use the env var DOCKER_REGISTRY as final fallback\n     * @param checkFirst list of registries to check\n     * @return registry found or null if none.\n     */\n    public static String firstRegistryOf(String ... checkFirst) {\n        for (String registry : checkFirst) {\n            if (registry != null) {\n                return registry;\n            }\n        }\n        // Check environment as last resort\n        return System.getenv(\"DOCKER_REGISTRY\");\n    }\n\n    // sometimes registries might be specified with https? schema, sometimes not\n    public static String ensureRegistryHttpUrl(String registry) {\n        if (registry.toLowerCase().startsWith(\"http\")) {\n            return registry;\n        }\n        // Default to https:// schema\n        return \"https://\" + registry;\n    }\n\n    public static File prepareAbsoluteOutputDirPath(MojoParameters params, String dir, String path) {\n        return prepareAbsolutePath(params, new File(params.getOutputDirectory(), dir).toString(), path);\n    }\n\n    public static File prepareAbsoluteSourceDirPath(MojoParameters params, String path) {\n        return prepareAbsolutePath(params, params.getSourceDirectory(), path);\n    }\n\n    private static File prepareAbsolutePath(MojoParameters params, String directory, String path) {\n        File file = new File(path);\n        if (file.isAbsolute()) {\n            return file;\n        }\n\n        File baseDir = new File(directory);\n        if (!baseDir.isAbsolute()) {\n            baseDir = new File(params.getProject().getBasedir(), directory);\n        }\n\n        return new File(baseDir, path);\n    }\n\n    // create a timestamp file holding time in epoch seconds\n    public static void storeTimestamp(File tsFile, Date buildDate) throws MojoExecutionException {\n        try {\n            if (tsFile.exists()) {\n                tsFile.delete();\n            }\n            File dir = tsFile.getParentFile();\n            if (!dir.exists()) {\n                if (!dir.mkdirs()) {\n                    throw new MojoExecutionException(\"Cannot create directory \" + dir);\n                }\n            }\n            FileUtils.fileWrite(tsFile, StandardCharsets.US_ASCII.name(), Long.toString(buildDate.getTime()));\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Cannot create \" + tsFile + \" for storing time \" + buildDate.getTime(),e);\n        }\n    }\n\n    public static Date loadTimestamp(File tsFile) throws IOException {\n        try {\n            if (tsFile.exists()) {\n                String ts = FileUtils.fileRead(tsFile);\n                return new Date(Long.parseLong(ts));\n            } else {\n                return null;\n            }\n        } catch (IOException e) {\n            throw new IOException(\"Cannot read timestamp \" + tsFile,e);\n        }\n    }\n\n    public static boolean isWindows() {\n        return System.getProperty(\"os.name\").toLowerCase().contains(\"windows\");\n    }\n\n    public static boolean isMaven350OrLater(MavenSession mavenSession) {\n        // Maven enforcer and help:evaluate goals both use mavenSession.getSystemProperties(),\n        // and it turns out that System.getProperty(\"maven.version\") does not return the value.\n        String mavenVersion = mavenSession.getSystemProperties().getProperty(\"maven.version\", \"3\");\n        return greaterOrEqualsVersion(mavenVersion, \"3.5.0\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/FormatParameterReplacer.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @author roland\n * @since 04.11.17\n */\npublic class FormatParameterReplacer {\n\n    // Detect format elements within the name\n    private final Pattern formatIdentifierPattern = Pattern.compile(\"^(.*?)%([^a-zA-Z]*)([a-zA-Z])(.*)$\");\n\n    private final Map<String, Lookup> lookupMap;\n\n    public FormatParameterReplacer(Map<String, Lookup> lookupMap) {\n        this.lookupMap = lookupMap;\n    }\n\n    public synchronized String replace(String input) {\n        StringBuilder ret = new StringBuilder();\n        while (true) {\n            Matcher matcher = formatIdentifierPattern.matcher(input);\n            if (!matcher.matches()) {\n                ret.append(input);\n                return ret.toString();\n            }\n            ret.append(matcher.group(1));\n            ret.append(formatElement(matcher.group(2),matcher.group(3)));\n            input = matcher.group(4);\n        }\n    }\n\n    private String formatElement(String options, String what) {\n        FormatParameterReplacer.Lookup lookup = lookupMap.get(what);\n        if (lookup == null) {\n            throw new IllegalArgumentException(String.format(\"No image name format element '%%%s' known\", what) );\n        }\n        String val = lookup.lookup();\n        return String.format(\"%\" + (options != null ? options : \"\") + \"s\",val);\n    }\n\n\n    // Lookup abstraction\n    public interface Lookup {\n        String lookup();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/GavLabel.java",
    "content": "/*\n *\n * Copyright 2014 Roland Huss\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\n\npackage io.fabric8.maven.docker.util;\n/**\n * Label used to mark a container belonging to a certain build.\n *\n * @author roland\n * @since 31/03/15\n */\npublic class GavLabel {\n\n    private String mavenCoordinates;\n\n    /**\n     * Construct from a given label\n     *\n     * @param label label as stored with the container\n     */\n    public GavLabel(String label) {\n        String[] parts = label.split(\":\");\n        if (parts.length != 3) {\n            throw new IllegalArgumentException(\"Label '\" + label +\n                                               \"' has not the format <group>:<artifact>:<version>\");\n        }\n        mavenCoordinates = parts[0] + \":\" + parts[1] + \":\" + parts[2];\n    }\n\n    /**\n     * Construct from maven coordinates and run ID. If the runId is <code>null</code> this label\n     * will.\n     *\n     * @param groupId Maven group\n     * @param artifactId Maven artifact\n     * @param version version\n     */\n    public GavLabel(String groupId, String artifactId, String version) {\n        mavenCoordinates = groupId + \":\" + artifactId + \":\" + version;\n    }\n\n    /**\n     * Get the label name\n     *\n     * @return the label name to use to mark a container belonging to this build\n     */\n    public String getKey() {\n        return \"dmp.coordinates\";\n    }\n\n    /**\n     * Get this label in string representation\n     * @return this label as string\n     */\n    public String getValue() {\n        return mavenCoordinates;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        return mavenCoordinates.equals(((GavLabel) o).mavenCoordinates);\n    }\n\n    @Override\n    public int hashCode() {\n        return mavenCoordinates.hashCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/ImageArchiveUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\nimport org.apache.commons.compress.archivers.tar.TarArchiveEntry;\nimport org.apache.commons.compress.archivers.tar.TarArchiveInputStream;\nimport org.apache.commons.compress.compressors.CompressorException;\nimport org.apache.commons.compress.compressors.CompressorStreamFactory;\nimport org.apache.commons.lang3.tuple.Pair;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonParseException;\n\nimport io.fabric8.maven.docker.model.ImageArchiveManifest;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestAdapter;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestEntry;\n\n/**\n * Helper functions for working with Docker image archives, as produced by\n * the docker:save mojo.\n */\npublic class ImageArchiveUtil {\n    public static final String MANIFEST_JSON = \"manifest.json\";\n\n    private static InputStream createUncompressedStream(InputStream possiblyCompressed) {\n        if(!possiblyCompressed.markSupported()) {\n            possiblyCompressed = new BufferedInputStream(possiblyCompressed, 512 * 1000);\n        }\n\n        try {\n            return new CompressorStreamFactory().createCompressorInputStream(possiblyCompressed);\n        } catch(CompressorException e) {\n            return possiblyCompressed;\n        }\n    }\n\n    /**\n     * Read the (possibly compressed) image archive provided and return the archive manifest.\n     *\n     * If there is no manifest found, then null is returned. Incomplete manifests are returned\n     * with as much information parsed as possible.\n     *\n     * @param file\n     * @return the parsed manifest, or null if none found.\n     * @throws IOException\n     * @throws JsonParseException\n     */\n    public static ImageArchiveManifest readManifest(File file) throws IOException, JsonParseException {\n        return readManifest(new FileInputStream(file));\n    }\n\n\n    /**\n     * Read the (possibly compressed) image archive stream provided and return the archive manifest.\n     *\n     * If there is no manifest found, then null is returned. Incomplete manifests are returned\n     * with as much information parsed as possible.\n     *\n     * @param inputStream\n     * @return the parsed manifest, or null if none found.\n     * @throws IOException\n     * @throws JsonParseException\n     */\n    public static ImageArchiveManifest readManifest(InputStream inputStream) throws IOException, JsonParseException {\n        Map<String, JsonParseException> parseExceptions = new LinkedHashMap<>();\n        Map<String, JsonElement> parsedEntries = new LinkedHashMap<>();\n\n        try (TarArchiveInputStream tarStream = new TarArchiveInputStream(createUncompressedStream(inputStream))) {\n            TarArchiveEntry tarEntry;\n            Gson gson = new Gson();\n\n            while((tarEntry = tarStream.getNextTarEntry()) != null) {\n                if(tarEntry.isFile() && tarEntry.getName().endsWith(\".json\")) {\n                    try {\n                        JsonElement element = gson.fromJson(new InputStreamReader(tarStream, StandardCharsets.UTF_8), JsonElement.class);\n                        parsedEntries.put(tarEntry.getName(), element);\n                    } catch(JsonParseException exception) {\n                        parseExceptions.put(tarEntry.getName(), exception);\n                    }\n                }\n            }\n        }\n\n        JsonElement manifestJson = parsedEntries.get(MANIFEST_JSON);\n        if(manifestJson == null) {\n            JsonParseException parseException = parseExceptions.get(MANIFEST_JSON);\n            if(parseException != null) {\n                throw parseException;\n            }\n\n            return null;\n        }\n\n        ImageArchiveManifestAdapter manifest = new ImageArchiveManifestAdapter(manifestJson);\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            JsonElement entryConfigJson = parsedEntries.get(entry.getConfig());\n            if(entryConfigJson != null && entryConfigJson.isJsonObject()) {\n                manifest.putConfig(entry.getConfig(), entryConfigJson.getAsJsonObject());\n            }\n        }\n\n        return manifest;\n    }\n\n    /**\n     * Search the manifest for an entry that has the repository and tag provided.\n     *\n     * @param repoTag the repository and tag to search (e.g. busybox:latest).\n     * @param manifest the manifest to be searched\n     * @return the entry found, or null if no match.\n     */\n    public static ImageArchiveManifestEntry findEntryByRepoTag(String repoTag, ImageArchiveManifest manifest) {\n        if(repoTag == null || manifest == null) {\n            return null;\n        }\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            for(String entryRepoTag : entry.getRepoTags()) {\n                if(repoTag.equals(entryRepoTag)) {\n                    return entry;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Search the manifest for an entry that has a repository and tag matching the provided pattern.\n     *\n     * @param repoTagPattern the repository and tag to search (e.g. busybox:latest).\n     * @param manifest the manifest to be searched\n     * @return a pair containing the matched tag and the entry found, or null if no match.\n     */\n    public static Pair<String, ImageArchiveManifestEntry> findEntryByRepoTagPattern(String repoTagPattern, ImageArchiveManifest manifest) throws PatternSyntaxException {\n        return findEntryByRepoTagPattern(repoTagPattern == null ? null : Pattern.compile(repoTagPattern), manifest);\n    }\n\n    /**\n     * Search the manifest for an entry that has a repository and tag matching the provided pattern.\n     *\n     * @param repoTagPattern the repository and tag to search (e.g. busybox:latest).\n     * @param manifest the manifest to be searched\n     * @return a pair containing the matched tag and the entry found, or null if no match.\n     */\n    public static Pair<String, ImageArchiveManifestEntry> findEntryByRepoTagPattern(Pattern repoTagPattern, ImageArchiveManifest manifest) throws PatternSyntaxException {\n        if(repoTagPattern == null || manifest == null) {\n            return null;\n        }\n\n        Matcher matcher = repoTagPattern.matcher(\"\");\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            for(String entryRepoTag : entry.getRepoTags()) {\n                if(matcher.reset(entryRepoTag).find()) {\n                    return Pair.of(entryRepoTag, entry);\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Search the manifest for an entry that has a repository and tag matching the provided pattern.\n     *\n     * @param repoTagPattern the repository and tag to search (e.g. busybox:latest).\n     * @param manifest the manifest to be searched\n     * @return a pair containing the matched tag and the entry found, or null if no match.\n     */\n    public static Map<String, ImageArchiveManifestEntry> findEntriesByRepoTagPattern(String repoTagPattern, ImageArchiveManifest manifest) throws PatternSyntaxException {\n        return findEntriesByRepoTagPattern(repoTagPattern == null ? null : Pattern.compile(repoTagPattern), manifest);\n    }\n\n    /**\n     * Search the manifest for an entry that has a repository and tag matching the provided pattern.\n     *\n     * @param repoTagPattern the repository and tag to search (e.g. busybox:latest).\n     * @param manifest the manifest to be searched\n     * @return a pair containing the matched tag and the entry found, or null if no match.\n     */\n    public static Map<String, ImageArchiveManifestEntry> findEntriesByRepoTagPattern(Pattern repoTagPattern, ImageArchiveManifest manifest) throws PatternSyntaxException {\n        Map<String, ImageArchiveManifestEntry> entries = new LinkedHashMap<>();\n\n        if(repoTagPattern == null || manifest == null) {\n            return entries;\n        }\n\n        Matcher matcher = repoTagPattern.matcher(\"\");\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            for(String entryRepoTag : entry.getRepoTags()) {\n                if(matcher.reset(entryRepoTag).find()) {\n                    entries.putIfAbsent(entryRepoTag, entry);\n                }\n            }\n        }\n\n        return entries;\n    }\n\n    /**\n     * Build a map of entries by id from an iterable of entries.\n     *\n     * @param entries\n     * @return a map of entries by id\n     */\n    public static Map<String, ImageArchiveManifestEntry> mapEntriesById(Iterable<ImageArchiveManifestEntry> entries) {\n        Map<String, ImageArchiveManifestEntry> mapped = new LinkedHashMap<>();\n\n        for(ImageArchiveManifestEntry entry : entries) {\n            mapped.put(entry.getId(), entry);\n        }\n\n        return mapped;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/ImageName.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Helper class for parsing docker repository/image names:\n *\n * <ul>\n *     <li>If the first part before the slash contains a \".\" or a \":\" it is considered to be a registry URL</li>\n *     <li>A last part starting with a \":\" is considered to be a tag</li>\n *     <li>The rest is considered the repository name (which might be separated via slashes)</li>\n * </ul>\n *\n * Example of valid names:\n *\n * <ul>\n *     <li>consol/tomcat-8.0</li>\n *     <li>consol/tomcat-8.0:8.0.9</li>\n *     <li>docker.consol.de:5000/tomcat-8.0</li>\n *     <li>docker.consol.de:5000/jolokia/tomcat-8.0:8.0.9</li>\n * </ul>\n *\n * @author roland\n * @since 22.07.14\n */\npublic class ImageName {\n\n    // The repository part of the full image\n    private String repository;\n\n    // Registry\n    private String registry;\n\n    // Tag name\n    private String tag;\n\n    // Digest\n    private String digest;\n\n    // User name\n    private String user;\n\n    /**\n     * Create an image name\n     *\n     * @param fullName The fullname of the image in Docker format.\n     */\n    public ImageName(String fullName) {\n        this(fullName,null);\n    }\n\n    /**\n     * Create an image name with a tag. If a tag is provided (i.e. is not null) then this tag is used.\n     * Otherwise the tag of the provided name is used (if any).\n     *\n     * @param fullName The fullname of the image in Docker format. I\n     * @param givenTag tag to use. Can be null in which case the tag specified in fullName is used.\n     */\n    public ImageName(String fullName, String givenTag) {\n        if (fullName == null) {\n            throw new NullPointerException(\"Image name must not be null\");\n        }\n\n        // set digest to null as default\n        digest = null;\n        // check if digest is part of fullName, if so -> extract it\n        if(fullName.contains(\"@sha256\")) { // Of it contains digest\n            String[] digestParts = fullName.split(\"@\");\n            digest = digestParts[1];\n            fullName = digestParts[0];\n        }\n\n        // check for tag\n        Pattern tagPattern = Pattern.compile(\"^(.+?)(?::([^:/]+))?$\");\n        Matcher matcher = tagPattern.matcher(fullName);\n        if (!matcher.matches()) {\n            throw new IllegalArgumentException(fullName + \" is not a proper image name ([registry/][repo][:port]\");\n        }\n        // extract tag if it exists\n        tag = givenTag != null ? givenTag : matcher.group(2);\n        String rest = matcher.group(1);\n\n        // extract registry, repository, user\n        parseComponentsBeforeTag(rest);\n\n        /*\n         * set tag to latest if tag AND digest are null\n         * if digest is not null but tag is -> leave it!\n         *  -> in case of \"image_name@sha256\" it is not required to get resolved to \"latest\"\n         */\n        if (tag == null && digest == null) {\n            tag = \"latest\";\n        }\n\n        doValidate();\n    }\n\n    public String getRepository() {\n        return repository;\n    }\n\n    public String getRegistry() {\n        return registry;\n    }\n\n    public String getTag() {\n        return tag;\n    }\n\n    public String getDigest() {\n        return digest;\n    }\n\n    public boolean hasRegistry() {\n        return registry != null && registry.length() > 0;\n    }\n\n    private String joinTail(String[] parts) {\n        StringBuilder builder = new StringBuilder();\n        for (int i = 1;i < parts.length; i++) {\n            builder.append(parts[i]);\n            if (i < parts.length - 1) {\n                builder.append(\"/\");\n            }\n        }\n        return builder.toString();\n    }\n\n    private boolean isRegistry(String part) {\n        return part.contains(\".\") || part.contains(\":\");\n    }\n\n    /**\n     * Get the full name of this image, including the registry but without\n     * any tag (e.g. <code>privateregistry:fabric8io/java</code>)\n     *\n     * @return full name with the original registry\n     */\n    public String getNameWithoutTag() {\n        return getNameWithoutTag(null);\n    }\n\n    /**\n     * Get the full name of this image like {@link #getNameWithoutTag()} does, but allow\n     * an optional registry. This registry is used when this image does not already\n     * contain a registry.\n     *\n     * @param optionalRegistry optional registry to use when this image does not provide\n     *                         a registry. Can be null in which case no optional registry is used*\n     * @return full name with original registry (if set) or optional registry (if not <code>null</code>)\n     */\n    public String getNameWithoutTag(String optionalRegistry) {\n        StringBuilder ret = new StringBuilder();\n        if (registry != null || optionalRegistry != null) {\n            ret.append(registry != null ? registry : optionalRegistry).append(\"/\");\n        }\n        ret.append(repository);\n        return ret.toString();\n    }\n\n\n    /**\n     * Get the full name of this image, including the registry and tag\n     * (e.g. <code>privateregistry:fabric8io/java:7u53</code>)\n     *\n     * @return full name with the original registry and the original tag given (if any).\n     */\n    public String getFullName() {\n        return getFullName(null);\n    }\n\n    /**\n     * Get the full name of this image like {@link #getFullName(String)} does, but allow\n     * an optional registry. This registry is used when this image does not already\n     * contain a registry. If no tag was provided in the initial name, <code>latest</code> is used.\n     *\n     * @param optionalRegistry optional registry to use when this image does not provide\n     *                         a registry. Can be null in which case no optional registry is used*\n     * @return full name with original registry (if set) or optional registry (if not <code>null</code>).\n     */\n    public String getFullName(String optionalRegistry) {\n        String fullName = getNameWithoutTag(optionalRegistry);\n        if (tag != null) {\n            fullName = fullName +  \":\" + tag;\n        }\n        if(digest != null) {\n            fullName = fullName + \"@\" + digest;\n        }\n        return fullName;\n    }\n\n    /**\n     * Get the user (or \"project\") part of the image name. This is the part after the registry and before\n     * the image name\n     *\n     * @return user part or <code>null</code> if no user is present in the name\n     */\n    public String getUser() {\n        return user;\n    }\n\n    /**\n     * Get the simple name of the image, which is the repository sans the user parts.\n     *\n     * @return simple name of the image\n     */\n    public String getSimpleName() {\n        String prefix = user + \"/\";\n        return repository.startsWith(prefix) ? repository.substring(prefix.length()) : repository;\n    }\n\n    public String getNameWithOptionalRepository(String optionalRepository) {\n        if (optionalRepository != null) {\n            String simpleName = getFullName();\n            String[] simpleNameParts = simpleName.split(\"/\");\n            if (simpleNameParts.length > 0) {\n                return optionalRepository + \"/\" + simpleNameParts[simpleNameParts.length - 1];\n            }\n        }\n        return getFullName();\n    }\n\n    /**\n     * Check whether the given name validates agains the Docker rules for names\n     *\n     * @param image image name to validate\n     * d@throws IllegalArgumentException if the name doesnt validate\n     */\n    public static void validate(String image) {\n        // Validation will be triggered during construction\n        new ImageName(image);\n    }\n\n    // Validate parts and throw an IllegalArgumentException if a part is not valid\n    private void doValidate() {\n        List<String> errors = new ArrayList<>();\n        // Strip off user from repository name\n        String image = user != null ? repository.substring(user.length() + 1) : repository;\n        Object[] checks = new Object[] {\n            \"registry\", DOMAIN_REGEXP, registry,\n            \"image\", IMAGE_NAME_REGEXP, image,\n            \"user\", NAME_COMP_REGEXP, user,\n            \"tag\", TAG_REGEXP, tag,\n            \"digest\", DIGEST_REGEXP, digest\n        };\n        for (int i = 0; i < checks.length; i +=3) {\n            String value = (String) checks[i + 2];\n            Pattern checkPattern = (Pattern) checks[i + 1];\n            if (value != null &&\n                !checkPattern.matcher(value).matches()) {\n                errors.add(String.format(\"%s part '%s' doesn't match allowed pattern '%s'\",\n                                         checks[i], value, checkPattern.pattern()));\n            }\n        }\n        if (errors.size() > 0) {\n            StringBuilder buf = new StringBuilder();\n            buf.append(String.format(\"Given Docker name '%s' is invalid:\\n\", getFullName()));\n            for (String error : errors) {\n                buf.append(String.format(\"   * %s\\n\",error));\n            }\n            buf.append(\"See http://bit.ly/docker_image_fmt for more details\");\n            throw new IllegalArgumentException(buf.toString());\n        }\n    }\n\n    private void parseComponentsBeforeTag(String rest) {\n        String[] parts = rest.split(\"\\\\s*/\\\\s*\");\n        if (parts.length == 1) {\n            registry = null;\n            user = null;\n            repository = parts[0];\n        } else if (parts.length >= 2) {\n            if (isRegistry(parts[0])) {\n                registry = parts[0];\n                if (parts.length > 2) {\n                    user = parts[1];\n                    repository = joinTail(parts);\n                } else {\n                    user = null;\n                    repository = parts[1];\n                }\n            } else {\n                registry = null;\n                user = parts[0];\n                repository = rest;\n            }\n        }\n    }\n\n    // ================================================================================================\n\n    // Validations patterns, taken directly from the docker source -->\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/reference.go\n\n    // ---------------------------------------------------------------------\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L18\n    private final String nameComponentRegexp = \"[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?\";\n\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L25\n    private final String domainComponentRegexp = \"(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\";\n\n    // ==========================================================\n\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L18\n    private final Pattern NAME_COMP_REGEXP = Pattern.compile(nameComponentRegexp);\n\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L53\n    private final Pattern IMAGE_NAME_REGEXP = Pattern.compile(nameComponentRegexp + \"(?:(?:/\" + nameComponentRegexp + \")+)?\");\n\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L31\n    private final Pattern DOMAIN_REGEXP = Pattern.compile(\"^\" + domainComponentRegexp + \"(?:\\\\.\" + domainComponentRegexp + \")*(?::[0-9]+)?$\");\n\n    // https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L37\n    private final Pattern TAG_REGEXP = Pattern.compile(\"^[\\\\w][\\\\w.-]{0,127}$\");\n\n    private final Pattern DIGEST_REGEXP = Pattern.compile(\"^sha256:[a-z0-9]{32,}$\");\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/ImageNameFormatter.java",
    "content": "package io.fabric8.maven.docker.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.google.common.base.Strings;\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport org.apache.maven.project.MavenProject;\n\n/**\n * Replace placeholders in an image name with certain properties found in the\n * project\n *\n * @author roland\n * @since 07/06/16\n */\npublic class ImageNameFormatter implements ConfigHelper.NameFormatter {\n\n\n    private final FormatParameterReplacer formatParamReplacer;\n\n    private final Date now;\n\n    public ImageNameFormatter(MavenProject project, Date now) {\n        this.now = now;\n        formatParamReplacer = new FormatParameterReplacer(initLookups(project));\n    }\n\n    @Override\n    public String format(String name) {\n        if (name == null) {\n            return null;\n        }\n\n        return formatParamReplacer.replace(name);\n    }\n\n    // =====================================================================================\n\n\n    // Lookup classes\n    private Map<String, FormatParameterReplacer.Lookup> initLookups(final MavenProject project) {\n        // Sanitized group id\n        final Map<String, FormatParameterReplacer.Lookup> lookups = new HashMap<>();\n\n        lookups.put(\"g\", new DefaultUserLookup(project));\n\n        // Sanitized artifact id\n        lookups.put(\"a\", new DefaultNameLookup(project));\n\n        // Various ways for adding a version\n        lookups.put(\"v\", new DefaultTagLookup(project, DefaultTagLookup.Mode.PLAIN, now));\n        lookups.put(\"t\", new DefaultTagLookup(project, DefaultTagLookup.Mode.SNAPSHOT_WITH_TIMESTAMP, now));\n        lookups.put(\"l\", new DefaultTagLookup(project, DefaultTagLookup.Mode.SNAPSHOT_LATEST, now));\n        return lookups;\n    }\n\n    // ==============================================================================================\n\n    public static abstract class AbstractLookup implements FormatParameterReplacer.Lookup {\n        protected final MavenProject project;\n\n        private AbstractLookup(MavenProject project) {\n            this.project = project;\n        }\n\n        protected String getProperty(String key) {\n            return project.getProperties().getProperty(key);\n        }\n    }\n\n\n    private static class DefaultUserLookup extends AbstractLookup {\n\n        /**\n         * Property to lookup for image user which overwrites the calculated default (group).\n         * Used with format modifier %g\n         */\n        private static final String DOCKER_IMAGE_USER = \"docker.image.user\";\n\n        private DefaultUserLookup(MavenProject project) {\n            super(project);\n        }\n\n        public String lookup() {\n            String user = getProperty(DOCKER_IMAGE_USER);\n            if (user != null) {\n                return user;\n            }\n            String groupId = project.getGroupId();\n            while (groupId.endsWith(\".\")) {\n                groupId = groupId.substring(0,groupId.length() - 1);\n            }\n            int idx = groupId.lastIndexOf(\".\");\n            return sanitizeName(groupId.substring(idx != -1 ? idx + 1 : 0));\n        }\n    }\n\n    private static class DefaultNameLookup extends AbstractLookup {\n\n        private DefaultNameLookup(MavenProject project) {\n            super(project);\n        }\n\n        public String lookup() {\n            return sanitizeName(project.getArtifactId());\n        }\n    }\n\n\n    private static class DefaultTagLookup extends AbstractLookup {\n\n        /**\n         * Property to lookup for image name which overwrites the calculated default, which is calculated\n         * on the project version and depends whether it is a snapshot project or not.\n         * Used with format modifier %v\n         */\n        private static final String DOCKER_IMAGE_TAG = \"docker.image.tag\";\n\n        // how to resolve the version\n        private final Mode mode;\n\n        // timestamp indicating now\n        private final Date now;\n\n        private enum Mode {\n            PLAIN,\n            SNAPSHOT_WITH_TIMESTAMP,\n            SNAPSHOT_LATEST\n        }\n\n        private DefaultTagLookup(MavenProject project, Mode mode, Date now) {\n            super(project);\n            this.mode = mode;\n            this.now = now;\n        }\n\n        public String lookup() {\n            String tag = getProperty(DOCKER_IMAGE_TAG);\n            if (!Strings.isNullOrEmpty(tag)) {\n                return tag;\n            }\n\n            tag = project.getVersion();\n            if (mode != Mode.PLAIN) {\n                if (tag.endsWith(\"-SNAPSHOT\")) {\n                    if (mode == Mode.SNAPSHOT_WITH_TIMESTAMP) {\n                        tag = \"snapshot-\" + new SimpleDateFormat(\"yyMMdd-HHmmss-SSSS\").format(now);\n                    } else if (mode == Mode.SNAPSHOT_LATEST) {\n                        tag = \"latest\";\n                    }\n                }\n            }\n            return tag;\n        }\n    }\n\n    // ==========================================================================================\n\n    // See also ImageConfiguration#doValidate()\n    private static String sanitizeName(String name) {\n        StringBuilder ret = new StringBuilder();\n        int underscores = 0;\n        boolean lastWasADot = false;\n        for (char c : name.toCharArray()) {\n            if (c == '_') {\n                underscores++;\n                // Only _ in a row are allowed\n                if (underscores <= 2) {\n                    ret.append(c);\n                }\n                continue;\n            }\n\n            if (c == '.') {\n                // Only one dot in a row is allowed\n                if (!lastWasADot) {\n                    ret.append(c);\n                }\n                lastWasADot = true;\n                continue;\n            }\n\n            underscores = 0;\n            lastWasADot = false;\n            if (Character.isLetter(c) || Character.isDigit(c) || c == '-') {\n                ret.append(c);\n            }\n        }\n\n        // All characters must be lowercase\n        return ret.toString().toLowerCase();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/JibServiceUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.cloud.tools.jib.api.AbsoluteUnixPath;\nimport com.google.cloud.tools.jib.api.CacheDirectoryCreationException;\nimport com.google.cloud.tools.jib.api.Containerizer;\nimport com.google.cloud.tools.jib.api.Credential;\nimport com.google.cloud.tools.jib.api.ImageFormat;\nimport com.google.cloud.tools.jib.api.InvalidImageReferenceException;\nimport com.google.cloud.tools.jib.api.Jib;\nimport com.google.cloud.tools.jib.api.JibContainerBuilder;\nimport com.google.cloud.tools.jib.api.LayerConfiguration;\nimport com.google.cloud.tools.jib.api.LogEvent;\nimport com.google.cloud.tools.jib.api.Port;\nimport com.google.cloud.tools.jib.api.RegistryException;\nimport com.google.cloud.tools.jib.api.RegistryImage;\nimport com.google.cloud.tools.jib.api.TarImage;\nimport com.google.cloud.tools.jib.event.events.ProgressEvent;\nimport com.google.cloud.tools.jib.event.progress.ProgressEventHandler;\nimport io.fabric8.maven.docker.assembly.AssemblyFiles;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.FileVisitor;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport static org.fusesource.jansi.Ansi.ansi;\n\npublic class JibServiceUtil {\n    /**\n     * Line above progress bar.\n     */\n    private static final String HEADER = \"Executing tasks:\";\n\n    /**\n     * Maximum number of bars in the progress display.\n     */\n    private static final int PROGRESS_BAR_COUNT = 30;\n    public static final String JIB_LOG_PREFIX = \"JIB> \";\n\n    private JibServiceUtil() {\n    }\n\n    private static final long JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10L;\n    protected static final String BUSYBOX = \"busybox:latest\";\n\n    /**\n     * Build container image using JIB\n     *\n     * @param jibContainerBuilder jib container builder object\n     * @param image tarball for image\n     * @param logger kit logger\n     * @throws InterruptedException in case thread is interrupted\n     */\n    public static void buildContainer(JibContainerBuilder jibContainerBuilder, TarImage image, Logger logger)\n            throws InterruptedException {\n\n        final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();\n        try {\n            jibContainerBuilder.setCreationTime(Instant.now());\n            jibContainerBuilder.containerize(Containerizer.to(image)\n                    .setAllowInsecureRegistries(true)\n                    .setExecutorService(jibBuildExecutor)\n                    .addEventHandler(LogEvent.class, log(logger))\n                    .addEventHandler(ProgressEvent.class, new ProgressEventHandler(logUpdate())));\n            logUpdateFinished();\n        } catch (CacheDirectoryCreationException | IOException | ExecutionException | RegistryException ex) {\n            logger.error(\"Unable to build the image tarball: \", ex);\n            throw new IllegalStateException(ex);\n        } catch (InterruptedException ex) {\n            Thread.currentThread().interrupt();\n            throw ex;\n        } finally {\n            jibBuildExecutor.shutdown();\n            jibBuildExecutor.awaitTermination(JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        }\n    }\n\n    public static JibContainerBuilder containerFromImageConfiguration(\n            ImageConfiguration imageConfiguration, Credential pullRegistryCredential) throws InvalidImageReferenceException {\n        final JibContainerBuilder containerBuilder = Jib.from(getRegistryImage(getBaseImage(imageConfiguration), pullRegistryCredential))\n                .setFormat(ImageFormat.OCI);\n        return populateContainerBuilderFromImageConfiguration(containerBuilder, imageConfiguration);\n    }\n\n    public static String getFullImageName(ImageConfiguration imageConfiguration, String tag) {\n        ImageName imageName;\n        if (tag != null) {\n            imageName = new ImageName(imageConfiguration.getName(), tag);\n        } else {\n            imageName = new ImageName(imageConfiguration.getName());\n        }\n        return imageName.getFullName();\n    }\n\n    /**\n     * Push Image to registry using JIB\n     *\n     * @param imageConfiguration ImageConfiguration\n     * @param pushCredentials    push credentials\n     * @param tarArchive         tar archive built during build goal\n     * @param log                Logger\n     */\n    public static void jibPush(ImageConfiguration imageConfiguration, Credential pushCredentials, File tarArchive, Logger log) {\n        BuildImageConfiguration buildImageConfiguration = imageConfiguration.getBuildConfiguration();\n        String imageName = getFullImageName(imageConfiguration, null);\n        try {\n            for (String tag : getAllImageTags(buildImageConfiguration.getTags(), imageName)) {\n                String imageNameWithTag = getFullImageName(imageConfiguration, tag);\n                log.info(\"Pushing image: %s\", imageNameWithTag);\n                pushImage(TarImage.at(tarArchive.toPath()), imageNameWithTag, pushCredentials, log);\n            }\n        } catch (IllegalStateException e) {\n            log.error(\"Exception occurred while pushing the image: %s\", imageConfiguration.getName());\n            throw new IllegalStateException(e.getMessage(), e);\n        } catch (InterruptedException e) {\n            log.error(\"Thread interrupted\", e);\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    private static void pushImage(TarImage baseImage, String targetImageName, Credential credential, Logger logger)\n            throws InterruptedException {\n\n        final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();\n        try {\n            submitPushToJib(baseImage, getRegistryImage(targetImageName, credential), jibBuildExecutor, logger);\n        } catch (RegistryException | CacheDirectoryCreationException | InvalidImageReferenceException | IOException | ExecutionException e) {\n            logger.error(\"Exception occurred while pushing the image: %s, %s\", targetImageName, e.getMessage());\n            throw new IllegalStateException(e.getMessage(), e);\n        } catch (InterruptedException ex) {\n            logger.error(\"Thread interrupted\", ex);\n            throw ex;\n        } finally {\n            jibBuildExecutor.shutdown();\n            jibBuildExecutor.awaitTermination(JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);\n        }\n    }\n\n    private static JibContainerBuilder populateContainerBuilderFromImageConfiguration(JibContainerBuilder containerBuilder, ImageConfiguration imageConfiguration) {\n        final Optional<BuildImageConfiguration> bic =\n                Optional.ofNullable(Objects.requireNonNull(imageConfiguration).getBuildConfiguration());\n        bic.map(BuildImageConfiguration::getEntryPoint)\n                .map(Arguments::asStrings)\n                .ifPresent(containerBuilder::setEntrypoint);\n        bic.map(BuildImageConfiguration::getEnv)\n                .ifPresent(containerBuilder::setEnvironment);\n        bic.map(BuildImageConfiguration::getPorts).map(List::stream)\n                .map(s -> s.map(Integer::parseInt).map(Port::tcp))\n                .map(s -> s.collect(Collectors.toSet()))\n                .ifPresent(containerBuilder::setExposedPorts);\n        bic.map(BuildImageConfiguration::getLabels)\n                .map(Map::entrySet)\n                .ifPresent(labels -> labels.forEach(l -> {\n                    if (l.getKey() != null && l.getValue() != null) {\n                        containerBuilder.addLabel(l.getKey(), l.getValue());\n                    }\n                }));\n        bic.map(BuildImageConfiguration::getCmd)\n                .map(Arguments::asStrings)\n                .ifPresent(containerBuilder::setProgramArguments);\n        bic.map(BuildImageConfiguration::getUser)\n                .ifPresent(containerBuilder::setUser);\n        bic.map(BuildImageConfiguration::getVolumes).map(List::stream)\n                .map(s -> s.map(AbsoluteUnixPath::get))\n                .map(s -> s.collect(Collectors.toSet()))\n                .ifPresent(containerBuilder::setVolumes);\n        bic.map(BuildImageConfiguration::getWorkdir)\n                .filter(((Predicate<String>) String::isEmpty).negate())\n                .map(AbsoluteUnixPath::get)\n                .ifPresent(containerBuilder::setWorkingDirectory);\n        return containerBuilder;\n    }\n\n    static Set<String> getAllImageTags(List<String> tags, String imageName) {\n        ImageName tempImage = new ImageName(imageName);\n        Set<String> tagSet = tags.stream().filter(Objects::nonNull).collect(Collectors.toSet());\n        if (!tempImage.getTag().isEmpty()) {\n            tagSet.add(tempImage.getTag());\n        }\n        return tagSet;\n    }\n\n    private static void submitPushToJib(TarImage baseImage, RegistryImage targetImage, ExecutorService jibBuildExecutor, Logger logger) throws InterruptedException, ExecutionException, RegistryException, CacheDirectoryCreationException, IOException {\n        Jib.from(baseImage).containerize(Containerizer.to(targetImage)\n                .setAllowInsecureRegistries(true)\n                .setExecutorService(jibBuildExecutor)\n                .addEventHandler(LogEvent.class, log(logger))\n                .addEventHandler(ProgressEvent.class, new ProgressEventHandler(logUpdate())));\n        logUpdateFinished();\n    }\n\n    private static RegistryImage getRegistryImage(String targetImage, Credential credential) throws InvalidImageReferenceException {\n        RegistryImage registryImage = RegistryImage.named(targetImage);\n        if (credential != null && !credential.getUsername().isEmpty() && !credential.getPassword().isEmpty()) {\n            registryImage.addCredential(credential.getUsername(), credential.getPassword());\n        }\n        return registryImage;\n    }\n\n    private static Consumer<LogEvent> log(Logger logger) {\n        return le -> {\n            if (le.getLevel() != LogEvent.Level.DEBUG || logger.isVerboseEnabled() || logger.isDebugEnabled()) {\n                System.out.println(ansi().cursorUpLine(1).eraseLine().a(JIB_LOG_PREFIX)\n                        .a(StringUtils.rightPad(le.getMessage(), 120)).a(\"\\n\"));\n            }\n        };\n    }\n\n    private static Consumer<ProgressEventHandler.Update> logUpdate() {\n        return update -> {\n            final List<String> progressDisplay =\n                    generateProgressDisplay(update.getProgress(), update.getUnfinishedLeafTasks());\n            if (progressDisplay.size() > 2 && progressDisplay.stream().allMatch(Objects::nonNull)) {\n                final String progressBar = progressDisplay.get(1);\n                final String task = progressDisplay.get(2);\n                System.out.println(ansi().cursorUpLine(1).eraseLine().a(JIB_LOG_PREFIX).a(progressBar).a(\" \").a(task));\n            }\n        };\n    }\n\n    private static void logUpdateFinished() {\n        System.out.println(JIB_LOG_PREFIX + generateProgressBar(1.0F));\n    }\n\n    public static String getBaseImage(ImageConfiguration imageConfiguration) {\n        return Optional.ofNullable(imageConfiguration)\n                .map(ImageConfiguration::getBuildConfiguration)\n                .map(BuildImageConfiguration::getFrom)\n                .filter(((Predicate<String>) String::isEmpty).negate())\n                .orElse(BUSYBOX);\n    }\n\n    public static void copyToContainer(\n            JibContainerBuilder containerBuilder, File directory, String targetDir, Map<File, AssemblyFiles.Entry> files)\n            throws IOException {\n\n        Files.walkFileTree(directory.toPath(), new FileVisitor<Path>() {\n            boolean notParentDir = false;\n\n            @Override\n            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n\n                if (!notParentDir) {\n                    notParentDir = true;\n                    return FileVisitResult.CONTINUE;\n                }\n\n                String fileFullpath = dir.toAbsolutePath().toString();\n                String relativePath = fileFullpath.substring(targetDir.length());\n                AbsoluteUnixPath absoluteUnixPath = AbsoluteUnixPath.fromPath(Paths.get(relativePath));\n                containerBuilder.addLayer(LayerConfiguration.builder()\n                        .addEntryRecursive(dir, absoluteUnixPath)\n                        .build());\n                return FileVisitResult.SKIP_SUBTREE;\n            }\n\n            @Override\n            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {\n                String fileFullpath = file.toAbsolutePath().toString();\n                String relativePath = fileFullpath.substring(targetDir.length());\n                AbsoluteUnixPath absoluteUnixPath = AbsoluteUnixPath.fromPath(Paths.get(relativePath));\n                containerBuilder.addLayer(LayerConfiguration.builder()\n                        .addEntryRecursive(file, absoluteUnixPath/*, filePermissionsProvider*/)\n                        .build());\n                return FileVisitResult.CONTINUE;\n            }\n\n            @Override\n            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {\n                if (exc != null) {\n                    throw new IOException(exc);\n                }\n                return FileVisitResult.TERMINATE;\n            }\n\n            @Override\n            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {\n                if (exc != null) {\n                    throw new IOException(exc);\n                }\n                return FileVisitResult.CONTINUE;\n            }\n        });\n    }\n\n    /**\n     * Generates a progress display.\n     *\n     * Taken from https://github.com/GoogleContainerTools/jib/blob/master/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/logging/ProgressDisplayGenerator.java#L47\n     *\n     * @param progress the overall progress, with {@code 1.0} meaning fully complete\n     * @param unfinishedLeafTasks the unfinished leaf tasks\n     * @return the progress display as a list of lines\n     */\n    private static List<String> generateProgressDisplay(double progress, List<String> unfinishedLeafTasks) {\n        List<String> lines = new ArrayList<>();\n\n        lines.add(HEADER);\n        lines.add(generateProgressBar(progress));\n        for (String task : unfinishedLeafTasks) {\n            lines.add(\"> \" + task);\n        }\n\n        return lines;\n    }\n\n    /**\n     * Generates the progress bar line.\n     *\n     * Taken from https://github.com/GoogleContainerTools/jib/blob/master/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/logging/ProgressDisplayGenerator.java#L66\n     *\n     * @param progress the overall progress, with {@code 1.0} meaning fully complete\n     * @return the progress bar line\n     */\n    private static String generateProgressBar(double progress) {\n        StringBuilder progressBar = new StringBuilder();\n        progressBar.append('[');\n\n        int barsToDisplay = (int) Math.round(PROGRESS_BAR_COUNT * progress);\n        for (int barIndex = 0; barIndex < PROGRESS_BAR_COUNT; barIndex++) {\n            progressBar.append(barIndex < barsToDisplay ? '=' : ' ');\n        }\n\n        return progressBar\n                .append(']')\n                .append(String.format(\" %.1f\", progress * 100))\n                .append(\"% complete\")\n                .toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/JsonFactory.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class JsonFactory {\n    private static final Gson GSON = new Gson();\n\n    private JsonFactory() {\n        // Empty Constructor\n    }\n\n    public static JsonObject newJsonObject(String json) {\n        return GSON.fromJson(json, JsonObject.class);\n    }\n\n    public static JsonArray newJsonArray(String json) {\n        return GSON.fromJson(json, JsonArray.class);\n    }\n\n    public static JsonArray newJsonArray(List<String> list) {\n        final JsonArray jsonArray = new JsonArray();\n\n        for(String element : list)\n        {\n            jsonArray.add(element);\n        }\n\n        return jsonArray;\n    }\n\n    public static JsonObject newJsonObject(Map<String,String> map) {\n        final JsonObject jsonObject = new JsonObject();\n\n        for (Map.Entry<String, String> entry : map.entrySet()) {\n            jsonObject.addProperty(entry.getKey(), entry.getValue());\n        }\n\n        return jsonObject;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/Logger.java",
    "content": "package io.fabric8.maven.docker.util;\n\n\n/**\n * Simple log handler for printing used during the maven build\n *\n * @author roland\n * @since 31.03.14\n */\npublic interface Logger {\n\n    /**\n     * Debug message if debugging is enabled.\n     *\n     * @param format debug message format\n     * @param params parameter for formatting message\n     */\n    void debug(String format, Object ... params);\n\n    /**\n     * Informational message\n     *\n     * @param format info message format\n     * @param params parameter for formatting message\n     */\n    void info(String format, Object ... params);\n\n    /**\n     * Verbose message for build\n     *\n     * @param logVerboseCategory debug level for logging\n     * @param format verbose message format\n     * @param params parameter for formatting message\n     */\n    void verbose(Logger.LogVerboseCategory logVerboseCategory, String format, Object ... params);\n\n    /**\n     * A warning.\n     *\n     * @param format warning message format\n     * @param params parameter for formatting message\n     */\n    void warn(String format, Object ... params);\n\n    /**\n     * Severe errors\n     *\n     * @param format error message format\n     * @param params parameter for formatting message\n     */\n    void error(String format, Object ... params);\n\n    /**\n     * Prepare the given message as an error message to be used in exceptions.\n     *\n     * @param message message to prepare\n     * @return prepared error message\n     */\n    String errorMessage(String message);\n\n    /**\n     * Whether debugging is enabled.\n     */\n    boolean isDebugEnabled();\n\n    /**\n     * Whether verbose is enablee\n     */\n    boolean isVerboseEnabled();\n\n    /**\n     * Start a progress bar* @param total the total number to be expected\n     */\n    void progressStart();\n\n    /**\n     * Update the progress\n     *\n     * @param layerId the image id of the layer fetched\n     * @param status a status message\n     * @param progressMessage the progressBar\n     */\n    void progressUpdate(String layerId, String status, String progressMessage);\n\n    /**\n     * Finis progress meter. Must be always called if {@link #progressStart()} has been\n     * used.\n     */\n    void progressFinished();\n\n    enum LogVerboseCategory {\n        BUILD(\"build\"), API(\"api\");\n\n        private String category;\n\n        LogVerboseCategory(String category) {\n            this.category = category;\n        }\n\n        public String getValue() {\n            return category;\n        }\n    };\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/MojoParameters.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.List;\n\nimport org.apache.maven.archiver.MavenArchiveConfiguration;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.settings.Settings;\nimport org.apache.maven.shared.filtering.MavenFileFilter;\nimport org.apache.maven.shared.filtering.MavenReaderFilter;\n\n/**\n * Helper class for encapsulating Mojo params which are not Plexus components\n *\n * @author roland\n * @since 09.05.14\n */\npublic class MojoParameters {\n    private final MavenArchiveConfiguration archive;\n    private final MavenSession session;\n    private final MavenFileFilter mavenFileFilter;\n    private final MavenReaderFilter mavenFilterReader;\n    private final MavenProject project;\n    private final Settings settings;\n\n    private final String outputDirectory;\n    private final String sourceDirectory;\n    \n    private final List<MavenProject> reactorProjects;\n\n    public MojoParameters(MavenSession session, MavenProject project, MavenArchiveConfiguration archive, MavenFileFilter mavenFileFilter,\n            MavenReaderFilter mavenFilterReader, Settings settings, String sourceDirectory, String outputDirectory, List<MavenProject> reactorProjects) {\n        this.archive = archive;\n        this.session = session;\n        this.mavenFileFilter = mavenFileFilter;\n        this.mavenFilterReader = mavenFilterReader;\n        this.project = project;\n        this.settings = settings;\n\n        this.sourceDirectory = sourceDirectory;\n        this.outputDirectory = outputDirectory;\n        \n        this.reactorProjects = reactorProjects;\n    }\n\n    public MavenArchiveConfiguration getArchiveConfiguration() {\n        return archive;\n    }\n\n    public String getSourceDirectory() {\n        return sourceDirectory;\n    }\n\n    public String getOutputDirectory() {\n        return outputDirectory;\n    }\n\n    public MavenSession getSession() {\n        return session;\n    }\n\n    public MavenFileFilter getMavenFileFilter() {\n        return mavenFileFilter;\n    }\n    \n    public MavenReaderFilter getMavenFilterReader() {\n        return mavenFilterReader;\n    }\n\n    public MavenProject getProject() {\n        return project;\n    }\n\n    public Settings getSettings() {\n        return settings;\n    }\n\n\tpublic List<MavenProject> getReactorProjects() {\n\t\treturn reactorProjects;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/NamePatternUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\n/**\n * Helper functions for pattern matching for image and container names.\n */\npublic class NamePatternUtil {\n    /// Name patterns can be prefixed with image= to have them only apply to image name.\n    public static final String IMAGE_FIELD = \"image\";\n\n    /// Name patterns can be prefixed with name= to have them only apply to container name.\n    public static final String NAME_FIELD = \"name\";\n\n    /**\n     * Accepts an Ant-ish or regular expression pattern and compiles to a regular expression.\n     *\n     * This is similar to SelectorUtils in the Maven codebase, but there the code uses the\n     * platform File.separator, while here we always want to work with forward slashes.\n     * Also, for a more natural fit with repository tags, both * and ** should stop at the colon\n     * that precedes the tag.\n     *\n     * Like SelectorUtils, wrapping a pattern in %regex[pattern] will create a regex from the\n     * pattern provided without translation. Otherwise, or if wrapped in %ant[pattern],\n     * then a regular expression will be created that is anchored at beginning and end,\n     * converts ? to [^/:], * to ([^/:]|:(?=.*:)) and ** to ([^:]|:(?=.*:))*.\n     *\n     * If ** is followed by /, the / is converted to a negative lookbehind for anything\n     * apart from a slash.\n     *\n     * @return a regular expression pattern created from the input pattern\n     */\n    public static String convertNamePattern(String pattern) {\n        final String REGEX_PREFIX = \"%regex[\", ANT_PREFIX = \"%ant[\", PATTERN_SUFFIX=\"]\";\n\n        if(pattern.startsWith(REGEX_PREFIX) && pattern.endsWith(PATTERN_SUFFIX)) {\n            return pattern.substring(REGEX_PREFIX.length(), pattern.length() - PATTERN_SUFFIX.length());\n        }\n\n        if(pattern.startsWith(ANT_PREFIX) && pattern.endsWith(PATTERN_SUFFIX)) {\n            pattern = pattern.substring(ANT_PREFIX.length(), pattern.length() - PATTERN_SUFFIX.length());\n        }\n\n        String[] parts = pattern.split(\"((?=[/:?*])|(?<=[/:?*]))\");\n        Matcher matcher = Pattern.compile(\"[A-Za-z0-9-]+\").matcher(\"\");\n\n        StringBuilder builder = new StringBuilder(\"^\");\n\n        for(int i = 0; i < parts.length; ++i) {\n            if(\"?\".equals(parts[i])) {\n                builder.append(\"[^/:]\");\n            } else if(\"*\".equals(parts[i])) {\n                if (i + 1 < parts.length && \"*\".equals(parts[i + 1])) {\n                    builder.append(\"([^:]|:(?=.*:))*\");\n                    ++i;\n                    if (i + 1 < parts.length && \"/\".equals(parts[i + 1])) {\n                        builder.append(\"(?<![^/])\");\n                        ++i;\n                    }\n                } else {\n                    builder.append(\"([^/:]|:(?=.*:))*\");\n                }\n            } else if(\"/\".equals(parts[i]) || \":\".equals(parts[i]) || matcher.reset(parts[i]).matches()) {\n                builder.append(parts[i]);\n            } else if(parts[i].length() > 0) {\n                builder.append(Pattern.quote(parts[i]));\n            }\n        }\n\n        builder.append(\"$\");\n\n        return builder.toString();\n    }\n\n    /**\n     * Take a string that represents a list of patterns, possibly a mixture of\n     * regular expressions and Ant-style patterns, and return a single regular\n     * expression that matches any of the alternatives.\n     *\n     * To allow users to target some parts of the filter list to some fields on\n     * the object to which the pattern is ultimately applied, it is possible to\n     * prefix patterns in the list with fieldName=pattern, and then the pattern\n     * can be excluded from use on unrelated fields.\n     *\n     * @param patternList the pattern list specification to convert\n     *\n     * @return the combined pattern, or null if there is are no patterns for\n     * the field.\n     *\n     * @throws PatternSyntaxException if any of the individual patterns fails\n     * to compile as a regular expression.\n     */\n    public static String convertNamePatternList(String patternList) {\n        return convertNamePatternList(patternList, null, true);\n    }\n\n    /**\n     * Take a string that represents a list of patterns, possibly a mixture of\n     * regular expressions and Ant-style patterns, and return a single regular\n     * expression that matches any of the alternatives.\n     *\n     * To allow users to target some parts of the filter list to some fields on\n     * the object to which the pattern is ultimately applied, it is possible to\n     * prefix patterns in the list with fieldName=pattern, and then the pattern\n     * can be excluded from use on unrelated fields.\n     *\n     * @param patternList the pattern list specification to convert\n     * @param fieldName the field name for which patterns should be selected\n     * @param includeUnnamed if true, include patterns that do not specify a\n     *                       field name\n     *\n     * @return the combined pattern, or null if there is are no patterns for\n     * the field.\n     *\n     * @throws PatternSyntaxException if any of the individual patterns fails\n     * to compile as a regular expression.\n     */\n    public static String convertNamePatternList(String patternList, String fieldName, boolean includeUnnamed) {\n        String[] patterns = patternList.split(\",\");\n        StringBuilder combinedPattern = new StringBuilder(\"(\");\n        boolean compound = false;\n\n        for(String pattern : patterns) {\n            pattern = pattern.trim();\n            String[] namedFieldPattern = pattern.split(\"=\", 2);\n            if(namedFieldPattern.length == 2) {\n                if(!namedFieldPattern[0].trim().equals(fieldName)) {\n                    continue;\n                }\n                pattern = namedFieldPattern[1].trim();\n            } else if(fieldName != null && !includeUnnamed) {\n                continue;\n            }\n\n            if(pattern.length() > 0) {\n                String converted = convertNamePattern(pattern);\n\n                if(converted.length() == 0) {\n                    continue;\n                }\n\n                try {\n                    Pattern.compile(converted);\n                } catch(PatternSyntaxException e) {\n                    throw new IllegalArgumentException(\"Unable to convert pattern \" + pattern + \" to regular expression. \" + e.getMessage(), e);\n                }\n\n                if(combinedPattern.length() > 1) {\n                    compound = true;\n                    combinedPattern.append('|');\n                }\n\n                combinedPattern.append(converted);\n            }\n        }\n\n        if(compound) {\n            combinedPattern.append(')');\n        } else {\n            combinedPattern.deleteCharAt(0);\n        }\n\n        return combinedPattern.length() == 0 ? null : combinedPattern.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/StartOrderResolver.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.*;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.service.QueryService;\nimport org.codehaus.plexus.util.StringUtils;\n\n/**\n * @author roland\n * @since 16.10.14\n */\npublic class StartOrderResolver {\n\n    public static final int MAX_RESOLVE_RETRIES = 10;\n\n    private final QueryService queryService;\n\n    private final List<Resolvable> secondPass;\n    private final Set<String> processedImages;\n\n    public static List<Resolvable> resolve(QueryService queryService, List<Resolvable> convertToResolvables) {\n        return new StartOrderResolver(queryService).resolve(convertToResolvables);\n    }\n\n    private StartOrderResolver(QueryService queryService) {\n        this.queryService = queryService;\n\n        this.secondPass = new ArrayList<>();\n        this.processedImages = new HashSet<>();\n    }\n\n\n    // Check images for volume / link dependencies and return it in the right order.\n    // Only return images which should be run\n    // Images references via volumes but with no run configuration are started once to create\n    // an appropriate container which can be linked into the image\n    private List<Resolvable> resolve(List<Resolvable> images) {\n        List<Resolvable> resolved = new ArrayList<>();\n        // First pass: Pick all data images and all without dependencies\n        for (Resolvable config : images) {\n            List<String> volumesOrLinks = extractDependentImagesFor(config);\n            if (volumesOrLinks == null) {\n                // A data image only or no dependency. Add it to the list of data image which can be always\n                // created first.\n                updateProcessedImages(config);\n                resolved.add(config);\n            } else {\n                secondPass.add(config);\n            }\n        }\n\n        // Next passes: Those with dependencies are checked whether they already have been visited.\n        return secondPass.size() > 0 ? resolveRemaining(resolved) : resolved;\n    }\n\n    private List<Resolvable> resolveRemaining(List<Resolvable> ret) {\n        int retries = MAX_RESOLVE_RETRIES;\n        String error = null;\n        try {\n            do {\n                resolveImageDependencies(ret);\n            } while (secondPass.size() > 0  && retries-- > 0);\n        } catch (DockerAccessException | ResolveSteadyStateException e) {\n            error = \"Cannot resolve image dependencies for start order\\n\" + remainingImagesDescription();\n        }\n        if (retries == 0 && secondPass.size() > 0) {\n            error = \"Cannot resolve image dependencies after \" + MAX_RESOLVE_RETRIES + \" passes\\n\"\n                    + remainingImagesDescription();\n        }\n        if (error != null) {\n            throw new IllegalStateException(error);\n        }\n        return ret;\n    }\n\n    private void updateProcessedImages(Resolvable config) {\n        processedImages.add(config.getName());\n        if (config.getAlias() != null) {\n            processedImages.add(config.getAlias());\n        }\n    }\n\n    private String remainingImagesDescription() {\n        StringBuffer ret = new StringBuffer();\n        ret.append(\"Unresolved images:\\n\");\n        for (Resolvable config : secondPass) {\n            ret.append(\"* \")\n               .append(config.getAlias())\n               .append(\" depends on \")\n               .append(StringUtils.join(config.getDependencies().toArray(), \",\"))\n               .append(\"\\n\");\n        }\n        return ret.toString();\n    }\n\n    private void resolveImageDependencies(List<Resolvable> resolved) throws DockerAccessException, ResolveSteadyStateException {\n        boolean changed = false;\n        Iterator<Resolvable> iterator = secondPass.iterator();\n\n        while (iterator.hasNext()) {\n            Resolvable config = iterator.next();\n            if (hasRequiredDependencies(config)) {\n                updateProcessedImages(config);\n                resolved.add(config);\n                changed = true;\n                iterator.remove();\n            }\n        }\n\n        if (!changed) {\n            throw new ResolveSteadyStateException();\n        }\n    }\n\n    private boolean hasRequiredDependencies(Resolvable config) throws DockerAccessException {\n        List<String> dependencies = extractDependentImagesFor(config);\n        if (dependencies == null) {\n            return false;\n        }\n\n        for (String dependency : dependencies) {\n            // make sure the container exists, it's state will be verified elsewhere\n            if (processedImages.contains(dependency) ||\n                // Check also whether a *container* with this name exists in which case it is interpreted\n                // as an external dependency which is already running\n                queryService.hasContainer(dependency)) {\n                continue;\n            }\n            return false;\n        }\n\n        return true;\n    }\n\n    private List<String> extractDependentImagesFor(Resolvable config) {\n        LinkedHashSet<String> ret = new LinkedHashSet<>(config.getDependencies());\n        return ret.isEmpty() ? null : new ArrayList<>(ret);\n    }\n\n    // Exception indicating a steady state while resolving start order of images\n    private static class ResolveSteadyStateException extends Throwable { }\n\n    public interface Resolvable {\n        String getName();\n        String getAlias();\n        List<String> getDependencies();\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/SuffixFileFilter.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\n\n/**\n * @author roland\n * @since 19.10.14\n */\npublic class SuffixFileFilter implements FilenameFilter {\n\n    final public static FilenameFilter PEM_FILTER = new SuffixFileFilter(\".pem\");\n\n    private String suffix;\n\n    public SuffixFileFilter(String suffix) {\n        this.suffix = suffix;\n    }\n\n    @Override\n    public boolean accept(File dir, String name) {\n        return name.endsWith(suffix);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/Task.java",
    "content": "package io.fabric8.maven.docker.util;\n\n/**\n * Represents a generic task to be executed on a object.\n */\npublic interface Task<T> {\n\n    void execute(T object) throws Exception;\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/TimestampFactory.java",
    "content": "package io.fabric8.maven.docker.util;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\n\n/**\n * Factory class for timestamp, eg {@link ZonedDateTime}.\n *\n * @author roland\n * @since 25/11/14\n */\npublic class TimestampFactory {\n    private TimestampFactory() {\n        // Empty Constructor\n    }\n\n    /**\n     * Create a timestamp for *now*\n     */\n    public static ZonedDateTime createTimestamp() {\n        return ZonedDateTime.now();\n    }\n\n    /**\n     * Create a timestamp by parsing the given string representation which must be in an extended ISO 8601 Format\n     * with Nanoseconds since this is the format used by Docker for logging (e.g. \"2014-11-24T22:34:00.761764812Z\")\n     *\n     * @param spec date specification to parse\n     */\n    public static ZonedDateTime createTimestamp(String spec) {\n        return ZonedDateTime.parse(spec).truncatedTo(ChronoUnit.MILLIS);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/VolumeBindingUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport static io.fabric8.maven.docker.util.DockerPathUtil.resolveAbsolutely;\n\n/**\n * Utility methods for working with Docker volume bindings.\n * <p>\n * This class provides explicit support for relative binding paths.  This means that the plugin configuration or\n * docker compose file can specify a relative path when configuring a volume binding.  Methods in this class will\n * examine volume binding strings in a {@link RunVolumeConfiguration} and resolve any relative paths in the host portion\n * of volume bindings.  Examples of relative bindings include:\n * <dl>\n *     <dd>A host path relative to the current working directory</dd>\n *     <dt>./relative/path:/absolute/container/path</dt>\n *\n *     <dd>A host path relative to the current working directory</dd>\n *     <dt>relative/path/:/absolute/container/path</dt>\n *\n *     <dd>A host path relative to the parent of the current working directory</dd>\n *     <dt>../relative/path:/absolute/container/path</dt>\n *\n *     <dd>A host path equal to the current user's home directory</dd>\n *     <dt>~:/absolute/container/path</dt>\n *\n *     <dd>A host path relative to the current user's home directory</dd>\n *     <dt>~/relative/path:/absolute/container/path</dt>\n * </dl>\n * </p>\n * <p>\n * Understand that the following is <em>not</em> considered a relative binding path, and is instead interpreted as a\n * <em>named volume</em>:\n * <dl>\n *     <dd>{@code rel} is interpreted as a <em>named volume</em>.  Use {@code ./rel} or {@code rel/} to have it\n *         interpreted as a relative path.</dd>\n *     <dt>rel:/absolute/container/path</dt>\n * </dl>\n * </p>\n * <p>\n * Volume bindings that specify an absolute path for the host portion are preserved and returned unmodified.\n * </p>\n */\npublic class VolumeBindingUtil {\n\n    /**\n     * A dot representing the current working directory\n     */\n    private static final String DOT = \".\";\n\n    /**\n     * A tilde representing the current user's home directory\n     */\n    private static final String TILDE = \"~\";\n\n    /**\n     * The current runtime platform file separator, '/' for *nix, '\\' for Windows\n     */\n    private static final String RUNTIME_SEP = System.getProperty(\"file.separator\");\n\n    /**\n     * Windows file separator: '\\'\n     */\n    private static final String WINDOWS_SEP = \"\\\\\";\n\n    /**\n     * Unix file separator '/'\n     */\n    private static final String UNIX_SEP = \"/\";\n\n    /**\n     * Matches a windows drive letter followed by a colon and backwards slash.  For example, will match:\n     * 'C:\\' or 'x:\\'.\n     */\n    private static final Pattern WINDOWS_DRIVE_PATTERN = Pattern.compile(\"^[A-Za-z]:\\\\\\\\.*\");\n\n    /**\n     * Resolves relative paths in the supplied {@code bindingString}, and returns a binding string that has relative\n     * paths replaced with absolute paths.  If the supplied {@code bindingString} does not contain a relative path, it\n     * is returned unmodified.\n     * <h3>Discussion:</h3>\n     * <p>\n     * Volumes may be defined inside of {@code service} blocks <a href=\"https://docs.docker.com/compose/compose-file/compose-file-v2/#volumes-volume_driver\">\n     * as documented here</a>:\n     * </p>\n     * <pre>\n     * volumes:\n     * # Just specify a path and let the Engine create a volume\n     * - /var/lib/mysql\n     *\n     * # Specify an absolute path mapping\n     * - /opt/data:/var/lib/mysql\n     *\n     * # Path on the host, relative to the Compose file\n     * - ./cache:/tmp/cache\n     *\n     * # User-relative path\n     * - ~/configs:/etc/configs/:ro\n     *\n     * # Named volume\n     * - datavolume:/var/lib/mysql\"\n     * </pre>\n     * <p>\n     * This method only operates on volume strings that are relative: beginning with {@code ./}, {@code ../}, or\n     * {@code ~}.  Relative paths beginning with {@code ./} or {@code ../} are absolutized relative to the supplied\n     * {@code baseDir}, which <em>must</em> be absolute.  Paths beginning with {@code ~} are interpreted relative to\n     * {@code new File(System.getProperty(\"user.home\"))}, and {@code baseDir} is ignored.\n     * </p>\n     * <p>\n     * Volume strings that do not begin with a {@code ./}, {@code ../}, or {@code ~} are returned as-is.\n     * </p>\n     * <h3>Examples:</h3>\n     * <p>\n     * Given {@code baseDir} equal to \"/path/to/basedir\" and a {@code bindingString} string equal to\n     * \"./reldir:/some/other/dir\", this method returns {@code /path/to/basedir/reldir:/some/other/dir}\n     * </p>\n     * <p>\n     * Given {@code baseDir} equal to \"/path/to/basedir\" and a {@code bindingString} string equal to\n     * \"../reldir:/some/other/dir\", this method returns {@code /path/to/reldir:/some/other/dir}\n     * </p>\n     * <p>\n     * Given {@code baseDir} equal to \"/path/to/basedir\" and a {@code bindingString} string equal to\n     * \"~/reldir:/some/other/dir\", this method returns {@code /home/user/reldir:/some/other/dir}\n     * </p>\n     * <p>\n     * Given {@code baseDir} equal to \"/path/to/basedir\" and a {@code bindingString} equal to\n     * \"src/test/docker:/some/other/dir\", this method returns {@code /path/to/basedir/src/test/docker:/some/other/dir}\n     * </p>\n     * <p>\n     * Given a {@code bindingString} equal to \"foo:/some/other/dir\", this method returns {@code foo:/some/other/dir},\n     * because {@code foo} is considered to be a <em>named volume</em>, not a relative path.\n     * </p>\n     *\n     * @param baseDir the base directory used to resolve relative paths (e.g. beginning with {@code ./}, {@code ../},\n     *                {@code ~}) present in the {@code bindingString}; <em>must</em> be absolute\n     * @param bindingString the volume string from the docker-compose file\n     * @return the volume string, with any relative paths resolved as absolute paths\n     * @throws IllegalArgumentException if the supplied {@code baseDir} is not absolute\n     */\n    public static String resolveRelativeVolumeBinding(File baseDir, String bindingString) {\n\n        // a 'services:' -> service -> 'volumes:' may be formatted as:\n        // (https://docs.docker.com/compose/compose-file/compose-file-v2/#volumes-volume_driver)\n        //\n        // volumes:\n        //  # Just specify a path and let the Engine create a volume\n        //  - /var/lib/mysql\n        //\n        //  # Specify an absolute path mapping\n        //  - /opt/data:/var/lib/mysql\n        //\n        //  # Path on the host, relative to the Compose file\n        //  - ./cache:/tmp/cache\n        //\n        //  # User-relative path\n        //  - ~/configs:/etc/configs/:ro\n        //\n        //  # Named volume\n        // - datavolume:/var/lib/mysql\n\n        String[] pathParts = bindingString.split(\":\");\n        String localPath = pathParts[0];\n\n        if (isRelativePath(localPath)) {\n            File resolvedFile;\n            if (isUserHomeRelativePath(localPath)) {\n                resolvedFile = resolveAbsolutely(prepareUserHomeRelativePath(localPath), System.getProperty(\"user.home\"));\n            } else {\n                if (!baseDir.isAbsolute()) {\n                    throw new IllegalArgumentException(\"Base directory '\" + baseDir + \"' must be absolute.\");\n                }\n                resolvedFile = resolveAbsolutely(localPath, baseDir.getAbsolutePath());\n            }\n            try {\n                localPath = resolvedFile.getCanonicalFile().getAbsolutePath();\n            } catch (IOException e) {\n                throw new RuntimeException(\"Unable to canonicalize '\" + resolvedFile + \"'\");\n            }\n        }\n\n        if (pathParts.length > 1) {\n            pathParts[0] = localPath;\n            return join(\":\", pathParts);\n        }\n\n        return localPath;\n    }\n\n    /**\n     * Iterates over each {@link RunVolumeConfiguration#getBind() binding} in the {@code volumeConfiguration}, and\n     * resolves any relative paths in the binding strings using {@link #resolveRelativeVolumeBinding(File, String)}.\n     * The {@code volumeConfiguration} is modified in place, with any relative paths replaced with absolute paths.\n     * <p>\n     * Relative paths are resolved relative to the supplied {@code baseDir}, which <em>must</em> be absolute.\n     * </p>\n     *\n     * @param baseDir the base directory used to resolve relative paths (e.g. beginning with {@code ./}, {@code ../},\n     *                {@code ~}) present in the binding string; <em>must</em> be absolute\n     * @param volumeConfiguration the volume configuration that may contain volume binding specifications\n     * @throws IllegalArgumentException if the supplied {@code baseDir} is not absolute\n     */\n    public static void resolveRelativeVolumeBindings(File baseDir, RunVolumeConfiguration volumeConfiguration) {\n        List<String> bindings = volumeConfiguration.getBind();\n\n        if (bindings == null || bindings.isEmpty()) {\n            return;\n        }\n\n        for (int i = 0; i < bindings.size(); i++) {\n            bindings.set(i, resolveRelativeVolumeBinding(baseDir, bindings.get(i)));\n        }\n    }\n\n    /**\n     * Determines if the supplied volume binding path contains a relative path.  This is subtle, because volume\n     * bindings may specify a named volume per the discussion below.\n     * <h3>Discussion:</h3>\n     * <p>\n     * Volumes may be defined inside of {@code service} blocks <a href=\"https://docs.docker.com/compose/compose-file/compose-file-v2/#volumes-volume_driver\">\n     * as documented here</a>:\n     * </p>\n     * <pre>\n     * volumes:\n     * # Just specify a path and let the Engine create a volume\n     * - /var/lib/mysql\n     *\n     * # Specify an absolute path mapping\n     * - /opt/data:/var/lib/mysql\n     *\n     * # Path on the host, relative to the Compose file\n     * - ./cache:/tmp/cache\n     *\n     * # User-relative path\n     * - ~/configs:/etc/configs/:ro\n     *\n     * # Named volume\n     * - datavolume:/var/lib/mysql\"\n     * </pre>\n     * <p>\n     * Volume binding paths that begin with {@code ./}, {@code ../}, or {@code ~} clearly represent a relative path.\n     * However, binding paths that do not begin with those characters may represent a <em>named volume</em>.  For\n     * example, the binding string {@code rel:/path/to/container/mountpoint} refers to the <em>named volume</em> {@code\n     * rel}. Because it is desirable to fully support relative paths for volumes provided in a run configuration, this\n     * method attempts to resolve the ambiguity between a <em>named volume</em> and a <em>relative path</em>.\n     * </p>\n     * <p>\n     * Therefore, volume binding strings will be considered to contain a relative path when any of the following\n     * conditions are true:\n     * <ul>\n     *     <li>the volume binding path begins with {@code ./}, {@code ../}, or {@code ~}</li>\n     *     <li>the volume binding path contains the character {@code /} <em>and</em> {@code /} is not at index 0 of\n     *         the volume binding path</li>\n     * </ul>\n     * </p>\n     * <p>\n     * If the binding string {@code rel:/path/to/container/mountpoint} is intended to represent {@code rel} as a\n     * <em>relative path</em> and not as a <em>named volume</em>, then the binding string should be modified to contain\n     * a forward slash like so: {@code rel/:/path/to/container/mountpoint}.  Another option would be to prefix {@code\n     * rel} with a {@code ./} like so: {@code ./rel:/path/to/container/mountpoint}\n     * </p>\n     *\n     *\n     * @param candidatePath the candidate volume binding path\n     * @return true if the candidate path is considered to be a relative path\n     */\n    static boolean isRelativePath(String candidatePath) {\n\n        // java.io.File considers Windows paths to be absolute _only_ if they start with a drive letter.  That is,\n        // a Windows path '\\foo\\bar\\baz' is _not_ considered absolute by File#isAbsolute.  This block differs from\n        // java.io.File in that it considers Windows paths to be absolute if they begin with the file separator _or_ a\n        // drive letter\n        if (candidatePath.startsWith(UNIX_SEP) ||\n                candidatePath.startsWith(WINDOWS_SEP) ||\n                WINDOWS_DRIVE_PATTERN.matcher(candidatePath).matches()) {\n            return false;\n        }\n\n        // './' or '../'\n        if (candidatePath.startsWith(DOT + RUNTIME_SEP) || candidatePath.startsWith(DOT + DOT + RUNTIME_SEP)) {\n            return true;\n        }\n\n        if (candidatePath.contains(UNIX_SEP) || candidatePath.contains(WINDOWS_SEP)) {\n            return true;\n        }\n\n        if (isUserHomeRelativePath(candidatePath)) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns true if the supplied path begins with {@code ~}.  This means that the path should be resolved relative\n     * to the user's home directory.\n     *\n     * @param candidatePath the candidate path that may represent a path under the user's home directory\n     * @return true if the path begins with {@code ~}\n     */\n    static boolean isUserHomeRelativePath(String candidatePath) {\n        return candidatePath.startsWith(TILDE);\n    }\n\n    private static String prepareUserHomeRelativePath(String userHomePath) {\n        if (!(isUserHomeRelativePath(userHomePath))) {\n            return userHomePath;\n        }\n\n        // Handle ~user and ~/path and ~\n\n        // '~'\n        if (userHomePath.equals(TILDE)) {\n            return \"\";\n        }\n\n        // '~/'\n        if (userHomePath.startsWith(TILDE + RUNTIME_SEP)) {\n            return userHomePath.substring(2);\n        }\n\n        // '~user' is not supported; no logic to support \"find the home directory for an arbitrary user\".\n        // e.g. '~user' or '~user/foo'\n        throw new IllegalArgumentException(\"'\" + userHomePath + \"' cannot be relativized, cannot resolve arbitrary\" +\n                \" user home paths.\");\n    }\n\n    private static String join(String with, String... components) {\n        StringBuilder result = new StringBuilder();\n        int i = 0;\n        while (i < components.length) {\n            result.append(components[i++]);\n            if (i < components.length) {\n                result.append(with);\n            }\n        }\n\n        return result.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java",
    "content": "package io.fabric8.maven.docker.util.aws;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\npublic class AwsSdkAuthConfigFactory {\n\n    private final Logger log;\n\n    public AwsSdkAuthConfigFactory(Logger log) {\n        this.log = log;\n    }\n\n    public AuthConfig createAuthConfig() {\n        try {\n            Class<?> credentialsProviderChainClass = Class.forName(\"com.amazonaws.auth.DefaultAWSCredentialsProviderChain\");\n            Object credentialsProviderChain = credentialsProviderChainClass.getDeclaredConstructor().newInstance();\n            Object credentials = credentialsProviderChainClass.getMethod(\"getCredentials\").invoke(credentialsProviderChain);\n            if (credentials == null) {\n                return null;\n            }\n\n            Class<?> sessionCredentialsClass = Class.forName(\"com.amazonaws.auth.AWSSessionCredentials\");\n            String sessionToken = sessionCredentialsClass.isInstance(credentials)\n                    ? (String) sessionCredentialsClass.getMethod(\"getSessionToken\").invoke(credentials) : null;\n\n            Class<?> credentialsClass = Class.forName(\"com.amazonaws.auth.AWSCredentials\");\n            return new AuthConfig(\n                    (String) credentialsClass.getMethod(\"getAWSAccessKeyId\").invoke(credentials),\n                    (String) credentialsClass.getMethod(\"getAWSSecretKey\").invoke(credentials),\n                    \"none\",\n                    sessionToken\n            );\n        } catch (Throwable t) {\n            String issueTitle = null;\n            try {\n                issueTitle = URLEncoder.encode(\"Failed calling AWS SDK: \" + t.getMessage(), UTF_8.name());\n            } catch (UnsupportedEncodingException ignore) {\n            }\n            log.warn(\"Failed to fetch AWS credentials: %s\", t.getMessage());\n            if (t.getCause() != null) {\n                log.warn(\"Caused by: %s\", t.getCause().getMessage());\n            }\n            log.warn(\"Please report a bug at https://github.com/fabric8io/docker-maven-plugin/issues/new?%s\",\n                    issueTitle == null ? \"\" : \"title=?\" + issueTitle);\n            log.warn(\"%s\", t);\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/ExitCodeChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.service.QueryService;\n\npublic class ExitCodeChecker implements WaitChecker {\n\n    private final int exitCodeExpected;\n    private final String containerId;\n    private final QueryService queryService;\n\n    public ExitCodeChecker(int exitCodeExpected, QueryService queryService, String containerId) {\n        this.exitCodeExpected = exitCodeExpected;\n        this.containerId = containerId;\n        this.queryService = queryService;\n    }\n\n    @Override\n    public boolean check() {\n        try {\n            Integer exitCodeActual = queryService.getMandatoryContainer(containerId).getExitCode();\n            // container still running\n            return exitCodeActual != null && exitCodeActual == exitCodeExpected;\n        } catch (DockerAccessException e) {\n            return false;\n        }\n    }\n\n    @Override\n    public void cleanUp() {\n        // No cleanup required\n    }\n\n    @Override\n    public String getLogLabel() {\n        return \"on exit code \" + exitCodeExpected;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/HealthCheckChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.util.Logger;\n\n/**\n * @author roland\n * @since 29/03/2017\n */\npublic class HealthCheckChecker implements WaitChecker {\n\n    private boolean first = true;\n\n    private DockerAccess docker;\n    private String containerId;\n    private Logger log;\n    private final String imageConfigDesc;\n\n    public HealthCheckChecker(DockerAccess docker, String containerId, String imageConfigDesc, Logger log) {\n        this.docker = docker;\n        this.containerId = containerId;\n        this.imageConfigDesc = imageConfigDesc;\n        this.log = log;\n    }\n\n    @Override\n    public boolean check() {\n        try {\n            final ContainerDetails container = docker.getContainer(containerId);\n            if (container == null) {\n                log.debug(\"HealthWaitChecker: Container %s not found\");\n                return false;\n            }\n\n            if (container.getHealthcheck() == null) {\n                throw new IllegalArgumentException(\"Can not wait for healthstate of \" + imageConfigDesc +\". No HEALTHCHECK configured.\");\n            }\n\n            if (first) {\n                log.info(\"%s: Waiting to become healthy\", imageConfigDesc);\n                log.debug(\"HealthWaitChecker: Waiting for healthcheck: '%s'\", container.getHealthcheck());\n                first = false;\n            } else if (log.isDebugEnabled()) {\n                log.debug(\"HealthWaitChecker: Waiting on healthcheck '%s'\", container.getHealthcheck());\n            }\n\n            return container.isHealthy();\n        } catch(DockerAccessException e) {\n            log.warn(\"Error while checking health: %s\", e.getMessage());\n            return false;\n        }\n    }\n\n    @Override\n    public void cleanUp() {}\n\n    @Override\n    public String getLogLabel() {\n        try {\n            final ContainerDetails container = docker.getContainer(containerId);\n            return String.format(\"on healthcheck '%s'\",container != null ? container.getHealthcheck() : \"[container not found]\");\n        } catch (DockerAccessException e) {\n            return String.format(\"on healthcheck [error fetching container: %s]\", e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/HttpPingChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.security.KeyManagementException;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.http.client.config.RequestConfig;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.RequestBuilder;\nimport org.apache.http.conn.ssl.NoopHostnameVerifier;\nimport org.apache.http.conn.ssl.SSLConnectionSocketFactory;\nimport org.apache.http.conn.ssl.TrustAllStrategy;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.DefaultHttpRequestRetryHandler;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.ssl.SSLContextBuilder;\n\nimport io.fabric8.maven.docker.config.WaitConfiguration;\n\n/**\n * Check whether a given URL is available\n *\n */\npublic class HttpPingChecker implements WaitChecker {\n\n    private int statusMin;\n    private int statusMax;\n    private String url;\n    private String method;\n    private boolean allowAllHosts;\n\n    // Disable HTTP client retries by default.\n    private static final int HTTP_CLIENT_RETRIES = 0;\n\n    // Timeout for pings\n    private static final int HTTP_PING_TIMEOUT = 500;\n\n    /**\n     * Ping the given URL\n     *\n     * @param url URL to check\n     * @param method HTTP method to use\n     * @param status status code to check\n     */\n    public HttpPingChecker(String url, String method, String status) {\n        this.url = url;\n        this.method = method;\n\n        Matcher matcher = Pattern.compile(\"^(\\\\d+)\\\\s*\\\\.\\\\.+\\\\s*(\\\\d+)$\").matcher(status);\n        if (matcher.matches()) {\n            statusMin = Integer.parseInt(matcher.group(1));\n            statusMax = Integer.parseInt(matcher.group(2));\n        } else {\n            statusMin = statusMax = Integer.parseInt(status);\n        }\n    }\n\n    public HttpPingChecker(String waitUrl) {\n        this(waitUrl, WaitConfiguration.DEFAULT_HTTP_METHOD, WaitConfiguration.DEFAULT_STATUS_RANGE);\n    }\n\n    public HttpPingChecker(String url, String method, String status, boolean allowAllHosts) {\n        this(url, method, status);\n        this.allowAllHosts = allowAllHosts;\n    }\n\n    @Override\n    public boolean check() {\n        try {\n            return ping();\n        } catch (IOException exception) {\n            // Could occur and then the check is always considered as failed\n            return false;\n        }\n    }\n\n    private boolean ping() throws IOException {\n        RequestConfig requestConfig =\n                RequestConfig.custom()\n                        .setSocketTimeout(HTTP_PING_TIMEOUT)\n                        .setConnectTimeout(HTTP_PING_TIMEOUT)\n                        .setConnectionRequestTimeout(HTTP_PING_TIMEOUT)\n                        .setRedirectsEnabled(false)\n                        .build();\n\n        CloseableHttpClient httpClient;\n        if (allowAllHosts) {\n            SSLContextBuilder builder = new SSLContextBuilder();\n            try {\n                builder.loadTrustMaterial(new TrustAllStrategy());\n                SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);\n                httpClient = HttpClientBuilder.create()\n                                              .setDefaultRequestConfig(requestConfig)\n                                              .setRetryHandler(new DefaultHttpRequestRetryHandler(HTTP_CLIENT_RETRIES, false))\n                                              .setSSLSocketFactory(socketFactory)\n                                              .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)\n                                              .build();\n            } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {\n                throw new IOException(\"Unable to set self signed strategy on http wait: \" + e, e);\n            }\n        } else {\n            httpClient = HttpClientBuilder.create()\n                    .setDefaultRequestConfig(requestConfig)\n                    .setRetryHandler(new DefaultHttpRequestRetryHandler(HTTP_CLIENT_RETRIES, false))\n                    .build();\n        }\n\n        try (CloseableHttpResponse response = httpClient.execute(RequestBuilder.create(method.toUpperCase()).setUri(url).build())) {\n            int responseCode = response.getStatusLine().getStatusCode();\n            if (responseCode == HttpURLConnection.HTTP_NOT_IMPLEMENTED) {\n                throw new IllegalArgumentException(\"Invalid or not supported HTTP method '\" + method.toUpperCase() + \"' for checking \" + url);\n            }\n            return responseCode >= statusMin && responseCode <= statusMax;\n        } finally {\n          httpClient.close();\n        }\n    }\n\n    @Override\n    public void cleanUp() {\n        // No cleanup required for this checker\n    }\n\n    @Override\n    public String getLogLabel() {\n        return \"on url \" + url;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/LogMatchCallback.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.time.ZonedDateTime;\nimport java.util.regex.Pattern;\n\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.util.Logger;\n\nclass LogMatchCallback implements LogCallback {\n\n    private final Logger logger;\n    private final LogWaitCheckerCallback callback;\n    private final Pattern pattern;\n    private StringBuilder logBuffer;\n\n    LogMatchCallback(final Logger logger, final LogWaitCheckerCallback callback, final String patternString) {\n        this.logger = logger;\n        this.callback = callback;\n        this.pattern = Pattern.compile(patternString);\n        logBuffer = (pattern.flags() & Pattern.DOTALL) != 0 ? new StringBuilder() : null;\n    }\n\n    @Override\n    public void log(int type, ZonedDateTime timestamp, String txt) throws DoneException {\n        logger.debug(\"LogWaitChecker: Trying to match '%s' [Pattern: %s] [thread: %d]\",\n                  txt, pattern.pattern(), Thread.currentThread().getId());\n\n        final String toMatch;\n        if (logBuffer != null) {\n            logBuffer.append(txt).append(\"\\n\");\n            toMatch = logBuffer.toString();\n        } else {\n            toMatch = txt;\n        }\n\n        if (pattern.matcher(toMatch).find()) {\n            logger.debug(\"Found log-wait pattern in log output\");\n            callback.matched();\n            throw new DoneException();\n        }\n    }\n\n    @Override\n    public void error(String error) {\n        logger.error(\"%s\", error);\n    }\n\n    @Override\n    public void close() {\n        logger.debug(\"Closing LogWaitChecker callback\");\n    }\n\n    @Override\n    public void open() {\n        logger.debug(\"Open LogWaitChecker callback\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/LogWaitChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.log.LogGetHandle;\nimport io.fabric8.maven.docker.util.Logger;\n\n/**\n * @author roland\n * @since 25/03/2017\n */\npublic class LogWaitChecker implements WaitChecker, LogWaitCheckerCallback {\n\n    private final String containerId;\n    private final String logPattern;\n    private final Logger log;\n\n    private final CountDownLatch latch;\n    private final LogGetHandle logHandle;\n\n    public LogWaitChecker(final String logPattern, final DockerAccess dockerAccess, final String containerId, final Logger log) {\n        this.containerId = containerId;\n        this.logPattern = logPattern;\n        this.log = log;\n\n        this.latch = new CountDownLatch(1);\n        this.logHandle = dockerAccess.getLogAsync(containerId, new LogMatchCallback(log, this, logPattern));\n    }\n\n    @Override\n    public void matched() {\n        latch.countDown();\n        log.info(\"Pattern '%s' matched for container %s\", logPattern, containerId);\n    }\n\n    @Override\n    public boolean check() {\n        return latch.getCount() == 0;\n    }\n\n    @Override\n    public void cleanUp() {\n        if (logHandle != null) {\n            logHandle.finish();\n        }\n    }\n\n    @Override\n    public String getLogLabel() {\n        return \"on log out '\" + logPattern + \"'\";\n    }\n}"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/LogWaitCheckerCallback.java",
    "content": "package io.fabric8.maven.docker.wait;\n\n\n/**\n * Interface called during waiting on log when a log line matches\n */\npublic interface LogWaitCheckerCallback {\n    void matched();\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/PreconditionFailedException.java",
    "content": "package io.fabric8.maven.docker.wait;\n\n/**\n * Wait for a certain amount of time\n *\n * @author roland\n * @since 25/03/2017\n */\npublic class PreconditionFailedException extends Exception {\n    private final long waited;\n\n    PreconditionFailedException(String message, long waited) {\n        super(message);\n        this.waited = waited;\n    }\n\n    public long getWaited() {\n        return waited;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/TcpPortChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * Check whether a given TCP port is available\n */\npublic class TcpPortChecker implements WaitChecker {\n\n    private static final int TCP_PING_TIMEOUT = 500;\n\n    private final List<Integer> ports;\n\n    private final List<InetSocketAddress> pending;\n\n    public TcpPortChecker(String host, List<Integer> ports) {\n        this.ports = ports;\n\n        this.pending = new ArrayList<>();\n        for (int port : ports) {\n            this.pending.add(new InetSocketAddress(host, port));\n        }\n\n    }\n\n    public List<Integer> getPorts() {\n        return ports;\n    }\n\n    @Override\n    public boolean check() {\n        Iterator<InetSocketAddress> iter = pending.iterator();\n\n        while (iter.hasNext()) {\n            InetSocketAddress address = iter.next();\n\n            try {\n                Socket s = new Socket();\n                s.connect(address, TCP_PING_TIMEOUT);\n                s.close();\n                iter.remove();\n            } catch (IOException e) {\n                // Ports isn't opened, yet. So don't remove from queue.\n                // Can happen and is part of the flow\n            }\n        }\n        return pending.isEmpty();\n    }\n\n    @Override\n    public void cleanUp() {\n        // No cleanup required\n    }\n\n    @Override\n    public String getLogLabel() {\n        return \"on tcp port '\" + pending + \"'\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/WaitChecker.java",
    "content": "package io.fabric8.maven.docker.wait;\n\n/**\n * Interface for various wait checkers\n *\n * @author roland\n * @since 25/03/2017\n */\npublic interface WaitChecker {\n    /**\n     * @return true if the the check has succeed, false otherwise\n     */\n    boolean check();\n\n    /**\n     * Cleanup hook which is called after the wait phase.\n     */\n    void cleanUp();\n\n    /**\n     * Get the label to be used in the log\n     * @return\n     */\n    String getLogLabel();\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/WaitTimeoutException.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.util.concurrent.TimeoutException;\n\n/**\n * Wait for a certain amount of time\n *\n * @author roland\n * @since 25/03/2017\n */\npublic class WaitTimeoutException extends TimeoutException {\n    private final long waited;\n\n    WaitTimeoutException(String message, long waited) {\n        super(message);\n        this.waited = waited;\n    }\n\n    public long getWaited() {\n        return waited;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/fabric8/maven/docker/wait/WaitUtil.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport java.util.Arrays;\nimport java.util.concurrent.*;\n\n\n/**\n * @author roland\n * @since 18.10.14\n */\npublic class WaitUtil {\n\n    // how long to wait at max when doing a http ping\n    private static final long DEFAULT_MAX_WAIT = 10 * 1000L;\n\n    // How long to wait between pings\n    private static final long WAIT_RETRY_WAIT = 500;\n\n\n    private WaitUtil() {}\n\n    public static long wait(int wait, Callable<Void> callable) throws ExecutionException, WaitTimeoutException {\n        long now = System.currentTimeMillis();\n        if (wait > 0) {\n            try {\n                FutureTask<Void> task = new FutureTask<>(callable);\n                task.run();\n\n                task.get(wait, TimeUnit.SECONDS);\n            } catch (@SuppressWarnings(\"unused\") InterruptedException e) {\n                Thread.currentThread().interrupt();\n            } catch (@SuppressWarnings(\"unused\") TimeoutException e) {\n                throw new WaitTimeoutException(\"timed out waiting for execution to complete: \" + e, delta(now));\n            }\n        }\n        return delta(now);\n    }\n\n    public static long wait(Precondition precondition, int maxWait, WaitChecker ... checkers) throws WaitTimeoutException, PreconditionFailedException {\n        return wait(precondition, maxWait, Arrays.asList(checkers));\n    }\n\n    public static long wait(Precondition precondition, int maxWait, Iterable<WaitChecker> checkers) throws WaitTimeoutException, PreconditionFailedException {\n        long max = maxWait > 0 ? maxWait : DEFAULT_MAX_WAIT;\n        long now = System.currentTimeMillis();\n        try {\n            do {\n                if (!precondition.isOk()) {\n                    // Final check, could be that the check just succeeded\n                    if (check(checkers)) {\n                        return delta(now);\n                    }\n                    throw new PreconditionFailedException(\"Precondition failed\", delta(now));\n                } else {\n                    if (check(checkers)) {\n                        return delta(now);\n                    }\n                }\n                sleep(WAIT_RETRY_WAIT);\n            } while (delta(now) < max);\n            throw new WaitTimeoutException(\"No checker finished successfully\", delta(now));\n        } finally {\n            precondition.cleanup();\n            cleanup(checkers);\n        }\n    }\n\n    private static boolean check(Iterable<WaitChecker> checkers) {\n        for (WaitChecker checker : checkers) {\n            if (checker.check()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    // Give checkers a possibility to clean up\n    private static void cleanup(Iterable<WaitChecker> checkers) {\n        for (WaitChecker checker : checkers) {\n            checker.cleanUp();\n        }\n    }\n\n    /**\n     * Sleep a bit\n     *\n     * @param millis how long to sleep in milliseconds\n     */\n    public static void sleep(long millis) {\n        try {\n            Thread.sleep(millis);\n        } catch (InterruptedException e) {\n            // ...\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    private static long delta(long now) {\n        return System.currentTimeMillis() - now;\n    }\n\n\n    /**\n     * Simple interfact for checking some preconditions\n     */\n    public interface Precondition {\n        // true if precondition is met\n        boolean isOk();\n        // cleanup which might be needed if the check is done.\n        void cleanup();\n    }\n}\n"
  },
  {
    "path": "src/main/resources/META-INF/plexus/components.xml",
    "content": "<component-set>\n  <components>\n    <!-- Lifecycle for a build + integration test -->\n    <component>\n      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>\n      <role-hint>docker</role-hint>\n      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>\n      <configuration>\n        <lifecycles>\n          <lifecycle>\n            <id>default</id>\n            <!--\n            phase mappings extend from jar mappings\n            http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Default_Lifecycle_Bindings_-_Packaging_ejb__ejb3__jar__par__rar__war\n            -->\n            <phases>\n              <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>\n              <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>\n              <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>\n              <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>\n              <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>\n              <package>org.apache.maven.plugins:maven-jar-plugin:jar,${project.groupId}:${project.artifactId}:build</package>\n              <pre-integration-test>${project.groupId}:${project.artifactId}:start</pre-integration-test>\n              <integration-test>org.apache.maven.plugins:maven-failsafe-plugin:integration-test</integration-test>\n              <verify>${project.groupId}:${project.artifactId}:stop,org.apache.maven.plugins:maven-failsafe-plugin:verify</verify>\n              <deploy>${project.groupId}:${project.artifactId}:push</deploy>\n            </phases>\n          </lifecycle>\n        </lifecycles>\n      </configuration>\n    </component>\n    <!-- Lifecycle for a pure docker Build build -->\n    <component>\n      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>\n      <role-hint>docker-build</role-hint>\n      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>\n      <configuration>\n        <lifecycles>\n          <lifecycle>\n            <id>default</id>\n            <phases>\n              <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>\n              <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>\n              <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>\n              <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>\n              <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>\n              <package>org.apache.maven.plugins:maven-jar-plugin:jar,${project.groupId}:${project.artifactId}:build</package>\n              <deploy>${project.groupId}:${project.artifactId}:push</deploy>\n            </phases>\n          </lifecycle>\n        </lifecycles>\n      </configuration>\n    </component>\n    <component>\n      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>\n      <role-hint>docker-tar</role-hint>\n      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>\n      <configuration>\n        <lifecycles>\n          <lifecycle>\n            <id>default</id>\n            <phases>\n              <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>\n              <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>\n              <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>\n              <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>\n              <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>\n              <package>org.apache.maven.plugins:maven-jar-plugin:jar,${project.groupId}:${project.artifactId}:source</package>\n              <install>org.apache.maven.plugins:maven-install-plugin:install</install>\n            </phases>\n          </lifecycle>\n        </lifecycles>\n      </configuration>\n    </component>\n\n    <!-- Components typically detected by annotations, but moved here because of\n         https://github.com/codehaus-plexus/plexus-containers/issues/4 -->\n    <component>\n      <role>io.fabric8.maven.docker.config.handler.compose.DockerComposeConfigHandler</role>\n      <implementation>io.fabric8.maven.docker.config.handler.compose.DockerComposeConfigHandler</implementation>\n      <requirements>\n        <requirement>\n          <role>org.apache.maven.shared.filtering.MavenReaderFilter</role>\n          <field-name>readerFilter</field-name>\n        </requirement>\n      </requirements>\n      <isolated-realm>false</isolated-realm>\n    </component>\n    <component>\n      <role>io.fabric8.maven.docker.config.handler.property.PropertyConfigHandler</role>\n      <implementation>io.fabric8.maven.docker.config.handler.property.PropertyConfigHandler</implementation>\n      <isolated-realm>false</isolated-realm>\n    </component>\n  </components>\n</component-set>"
  },
  {
    "path": "src/main/resources/assemblies/artifact-with-dependencies.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>artifact-with-dependencies</id>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>true</useProjectArtifact>\n      <includes>\n        <include>${project.groupId}:${project.artifactId}</include>\n      </includes>\n      <outputFileNameMapping>${project.build.finalName}.${artifact.extension}</outputFileNameMapping>\n    </dependencySet>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <scope>runtime</scope>\n      <fileMode>0644</fileMode>\n    </dependencySet>\n  </dependencySets>\n  <!-- Add a classpath file, too. Doesn't harm when not existant -->\n  <fileSets>\n    <fileSet>\n      <includes>\n        <include>classpath</include>\n      </includes>\n      <directory>${project.build.directory}/</directory>\n      <outputDirectory>.</outputDirectory>\n      <fileMode>0644</fileMode>\n    </fileSet>\n  </fileSets>\n</assembly>"
  },
  {
    "path": "src/main/resources/assemblies/artifact.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>artifact</id>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>true</useProjectArtifact>\n      <includes>\n        <include>${project.groupId}:${project.artifactId}</include>\n      </includes>\n      <outputFileNameMapping>${project.build.finalName}.${artifact.extension}</outputFileNameMapping>\n    </dependencySet>\n  </dependencySets>\n</assembly>"
  },
  {
    "path": "src/main/resources/assemblies/hawt-app.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>hawt-app</id>\n  <fileSets>\n    <fileSet>\n      <includes>\n        <include>bin/*</include>\n      </includes>\n      <directory>${project.build.directory}/hawt-app</directory>\n      <outputDirectory>.</outputDirectory>\n      <fileMode>0755</fileMode>\n    </fileSet>\n    <fileSet>\n      <includes>\n        <include>lib/*</include>\n      </includes>\n      <directory>${project.build.directory}/hawt-app</directory>\n      <outputDirectory>.</outputDirectory>\n      <fileMode>0644</fileMode>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "src/main/resources/assemblies/project.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/${mdoVersion}\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/${mdoVersion} http://maven.apache.org/xsd/assembly-${mdoVersion}.xsd\">\n  <id>project</id>\n  <fileSets>\n    <fileSet>\n      <directory>${project.basedir}</directory>\n      <useDefaultExcludes>true</useDefaultExcludes>\n      <excludes>\n        <exclude>**/*.log</exclude>\n        <exclude>**/${project.build.directory}/**</exclude>\n      </excludes>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "src/main/resources/assemblies/rootWar.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\n  <id>rootWar</id>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>true</useProjectArtifact>\n      <includes>\n        <include>${project.groupId}:${project.artifactId}</include>\n      </includes>\n      <outputDirectory>.</outputDirectory>\n      <outputFileNameMapping>ROOT.war</outputFileNameMapping>\n    </dependencySet>\n  </dependencySets>\n</assembly>\n"
  },
  {
    "path": "src/test/java/com/amazonaws/auth/AWSCredentials.java",
    "content": "package com.amazonaws.auth;\n\n/*\n * Copyright 2011-2020 Amazon Technologies, Inc.\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://aws.amazon.com/apache2.0\n *\n * This file is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES\n * OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copy of the original class for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}.\n * Based on <a href=\"https://github.com/aws/aws-sdk-java/blob/1.11.707/aws-java-sdk-core/src/main/java/com/amazonaws/auth/AWSCredentials.java\">com.amazonaws:aws-java-sdk-core:1.11.707</a> (also APL licensed).\n * We can't use a direct dependency here, as we have\n * to keep d-m-p agnostic of the AWS SDK and only access it via reflection.\n */\npublic class AWSCredentials {\n    private final String accessKeyId;\n    private final String secretKey;\n\n    public AWSCredentials(String accessKeyId, String secretKey) {\n        this.accessKeyId = accessKeyId;\n        this.secretKey = secretKey;\n    }\n\n    public String getAWSAccessKeyId() {\n        return accessKeyId;\n    }\n\n    public String getAWSSecretKey() {\n        return secretKey;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/com/amazonaws/auth/AWSSessionCredentials.java",
    "content": "package com.amazonaws.auth;\n\n/*\n * Copyright 2011-2020 Amazon Technologies, Inc.\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://aws.amazon.com/apache2.0\n *\n * This file is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES\n * OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Copy of the original class for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}.\n * Based on <a href=\"https://github.com/aws/aws-sdk-java/blob/1.11.707/aws-java-sdk-core/src/main/java/com/amazonaws/auth/AWSSessionCredentials.java\">com.amazonaws:aws-java-sdk-core:1.11.707</a> (also APL licensed).\n * We can't use a direct dependency here, as we have\n * to keep d-m-p agnostic of the AWS SDK and only access it via reflection.\n */\npublic class AWSSessionCredentials extends AWSCredentials {\n\n    private final String sessionKey;\n\n    public AWSSessionCredentials(String accessKeyId, String secretKey, String sessionKey) {\n        super(accessKeyId, secretKey);\n        this.sessionKey = sessionKey;\n    }\n\n    public String getSessionToken() {return sessionKey;}\n\n}\n"
  },
  {
    "path": "src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java",
    "content": "package com.amazonaws.auth;\n\nimport static java.lang.System.getenv;\n\n/*\n * Copyright 2011-2020 Amazon Technologies, Inc.\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://aws.amazon.com/apache2.0\n *\n * This file is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES\n * OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Copy of the original class for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}.\n * Based on <a href=\"https://github.com/aws/aws-sdk-java/blob/1.11.707/aws-java-sdk-core/src/main/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java\">com.amazonaws:aws-java-sdk-core:1.11.707</a> (also APL licensed).\n * We can't use a direct dependency here, as we have\n * to keep d-m-p agnostic of the AWS SDK and only access it via reflection.\n */\n\npublic final class DefaultAWSCredentialsProviderChain {\n\n    public AWSCredentials getCredentials() {\n        String accessKeyId = getenv(\"AWSCredentials.AWSAccessKeyId\");\n        if (accessKeyId == null) {\n            return null;\n        }\n        String secretKey = getenv(\"AWSCredentials.AWSSecretKey\");\n        String sessionToken = getenv(\"AWSSessionCredentials.SessionToken\");\n        return sessionToken == null\n                ? new AWSCredentials(accessKeyId, secretKey)\n                : new AWSSessionCredentials(accessKeyId,secretKey,sessionToken);\n    }\n\n}"
  },
  {
    "path": "src/test/java/integration/DockerAccessIT.java",
    "content": "package integration;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.ContainerHostConfig;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.DockerConnectionDetector;\nimport io.fabric8.maven.docker.access.DockerMachine;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.access.hc.DockerAccessWithHcClient;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.DockerMachineConfiguration;\nimport io.fabric8.maven.docker.model.Container.PortBinding;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport com.google.common.collect.Lists;\n\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/*\n * if run from your ide, this test assumes you have configured the runner w/ the appropriate env variables\n *\n * it also assumes that 'removeImage' does what it's supposed to do as it's used in test setup.\n */\n@Ignore\npublic class DockerAccessIT {\n\n    private static final String CONTAINER_NAME = \"integration-test\";\n    private static final String IMAGE = \"busybox:buildroot-2014.02\";\n\n    private static final String IMAGE_TAG = \"busybox:tagged\";\n    private static final String IMAGE_LATEST = \"busybox:latest\";\n    private static final int PORT = 5677;\n\n    private String containerId;\n    private final DockerAccessWithHcClient dockerClient;\n\n    public DockerAccessIT() throws IOException {\n        AnsiLogger logger = new AnsiLogger(new SystemStreamLog(), true, \"build\");\n        String url = createDockerConnectionDetector(logger).detectConnectionParameter(null,null).getUrl();\n        this.dockerClient = createClient(url, logger);\n    }\n\n    private DockerConnectionDetector createDockerConnectionDetector(Logger logger) {\n        DockerMachineConfiguration machine = new DockerMachineConfiguration(\"default\",\"false\",\"false\");\n        return new DockerConnectionDetector(\n            Collections.<DockerConnectionDetector.DockerHostProvider>singletonList(new DockerMachine(logger, machine)));\n    }\n\n    @Before\n    public void setup() throws DockerAccessException {\n        // yes - this is a big don't do for tests\n        testRemoveImage(IMAGE);\n        testRemoveImage(IMAGE_TAG);\n    }\n\n    @Test\n    @Ignore\n    public void testBuildImage() throws DockerAccessException {\n        File file = new File(\"src/test/resources/integration/busybox-test.tar\");\n        dockerClient.buildImage(IMAGE_TAG, file, null);\n        assertTrue(hasImage(IMAGE_TAG));\n\n        testRemoveImage(IMAGE_TAG);\n    }\n\n    @Test\n    public void testPullStartStopRemove() throws DockerAccessException, InterruptedException {\n        testDoesNotHave();\n\n        try {\n            testPullImage();\n            testTagImage();\n            testCreateContainer();\n            testStartContainer();\n            testExecContainer();\n            testQueryPortMapping();\n            testStopContainer();\n            Thread.sleep(2000);\n            testRemoveContainer();\n        } finally {\n            testRemoveImage(IMAGE);\n        }\n    }\n\n    @Test\n    public void testLoadImage() throws DockerAccessException {\n        testDoesNotHave();\n        dockerClient.loadImage(IMAGE_LATEST, new File(\"integration/busybox-image-test.tar.gz\"));\n        assertTrue(hasImage(IMAGE_LATEST));\n        testRemoveImage(IMAGE_LATEST);\n    }\n\n    private DockerAccessWithHcClient createClient(String baseUrl, Logger logger) {\n        try {\n            String certPath = createDockerConnectionDetector(logger).detectConnectionParameter(null,null).getCertPath();\n            return new DockerAccessWithHcClient(baseUrl, certPath, 20, logger);\n        } catch (@SuppressWarnings(\"unused\") IOException e) {\n            // not using ssl, so not going to happen\n            logger.error(e.getMessage());\n            throw new RuntimeException();\n        }\n    }\n\n    private void testCreateContainer() throws DockerAccessException {\n        PortMapping portMapping = new PortMapping(Arrays.asList(new String[] {PORT + \":\" + PORT }), new Properties());\n        ContainerHostConfig hostConfig = new ContainerHostConfig().portBindings(portMapping);\n        ContainerCreateConfig createConfig = new ContainerCreateConfig(IMAGE).command(new Arguments(\"ping google.com\")).hostConfig(hostConfig);\n\n        containerId = dockerClient.createContainer(createConfig, CONTAINER_NAME);\n        assertNotNull(containerId);\n\n        String name = dockerClient.getContainer(containerId).getName();\n        assertEquals(CONTAINER_NAME, name);\n    }\n\n    private void testDoesNotHave() throws DockerAccessException {\n        assertFalse(hasImage(IMAGE));\n    }\n\n    private void testPullImage() throws DockerAccessException {\n        dockerClient.pullImage(IMAGE, null, null);\n        assertTrue(hasImage(IMAGE));\n    }\n\n    private void testQueryPortMapping() throws DockerAccessException {\n        Map<String, PortBinding> portMap = dockerClient.getContainer(containerId).getPortBindings();\n        assertEquals(5677, portMap.values().iterator().next().getHostPort().intValue());\n    }\n\n    private void testRemoveContainer() throws DockerAccessException {\n        dockerClient.removeContainer(containerId, true);\n    }\n\n    private void testRemoveImage(String image) throws DockerAccessException {\n        dockerClient.removeImage(image, false);\n        assertFalse(hasImage(image));\n    }\n\n    private void testStartContainer() throws DockerAccessException {\n        dockerClient.startContainer(containerId);\n        assertTrue(dockerClient.getContainer(containerId).isRunning());\n    }\n\n    private void testExecContainer() throws DockerAccessException {\n        Arguments arguments = new Arguments();\n        arguments.setExec(Lists.newArrayList(\"echo\", \"test\", \"echo\"));\n        String execContainerId = dockerClient.createExecContainer(this.containerId, arguments);\n        //assertThat(dockerClient.startExecContainer(execContainerId), is(\"test echo\"));\n    }\n\n    private void testStopContainer() throws DockerAccessException {\n        dockerClient.stopContainer(containerId, 0);\n        assertFalse(dockerClient.getContainer(containerId).isRunning());\n    }\n\n    private void testTagImage() throws DockerAccessException {\n        dockerClient.tag(IMAGE, IMAGE_TAG,false);\n        assertTrue(hasImage(IMAGE_TAG));\n\n        dockerClient.removeImage(IMAGE_TAG, false);\n        assertFalse(hasImage(IMAGE_TAG));\n    }\n\n    private boolean hasImage(String image) throws DockerAccessException {\n        return dockerClient.hasImage(image);\n    }\n}\n"
  },
  {
    "path": "src/test/java/integration/DockerAccessWinIT.java",
    "content": "package integration;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.ContainerHostConfig;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.DockerConnectionDetector;\nimport io.fabric8.maven.docker.access.DockerMachine;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.access.hc.DockerAccessWithHcClient;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.model.Container.PortBinding;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.Logger;\n\nimport com.google.common.collect.Lists;\n\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/*\n * if run from your ide, this test assumes you have configured the runner w/ the appropriate env variables\n *\n * it also assumes that 'removeImage' does what it's supposed to do as it's used in test setup.\n */\n@Ignore\npublic class DockerAccessWinIT {\n\n    private static final String CONTAINER_NAME = \"integration-test\";\n    private static final String IMAGE = \"busybox:buildroot-2014.02\";\n\n    private static final String IMAGE_TAG = \"busybox:tagged\";\n    private static final int PORT = 5677;\n\n    private String containerId;\n    private final DockerAccessWithHcClient dockerClient;\n\n    public DockerAccessWinIT() throws IOException {\n        AnsiLogger logger = new AnsiLogger(new SystemStreamLog(), true, \"build\");\n        String url = createDockerConnectionDetector(logger).detectConnectionParameter(null, null).getUrl();\n        this.dockerClient = createClient(url, logger);\n    }\n\n    private DockerConnectionDetector createDockerConnectionDetector(Logger logger) {\n        return new DockerConnectionDetector(\n            Collections.<DockerConnectionDetector.DockerHostProvider>singletonList(new DockerMachine(logger, null)));\n    }\n\n    @Before\n    public void setup() throws DockerAccessException {\n        // yes - this is a big don't do for tests\n        testRemoveImage(IMAGE);\n        testRemoveImage(IMAGE_TAG);\n    }\n\n    @Test\n    @Ignore\n    public void testBuildImage() throws DockerAccessException {\n        File file = new File(\"src/test/resources/integration/busybox-test.tar\");\n        dockerClient.buildImage(IMAGE_TAG, file, null);\n        assertTrue(hasImage(IMAGE_TAG));\n\n        testRemoveImage(IMAGE_TAG);\n    }\n\n    @Test\n    public void testPullStartStopRemove() throws DockerAccessException {\n        testDoesNotHave();\n\n        try {\n            testPullImage();\n            testTagImage();\n            testCreateContainer();\n            testStartContainer();\n            testExecContainer();\n            testQueryPortMapping();\n            testStopContainer();\n            testRemoveContainer();\n        } finally {\n            testRemoveImage(IMAGE);\n        }\n    }\n\n    private DockerAccessWithHcClient createClient(String baseUrl, Logger logger) {\n        try {\n            String certPath = createDockerConnectionDetector(logger).detectConnectionParameter(null, null).getCertPath();\n            return new DockerAccessWithHcClient(baseUrl, certPath, 1, logger);\n        } catch (@SuppressWarnings(\"unused\") IOException e) {\n            // not using ssl, so not going to happen\n            logger.error(e.getMessage());\n            throw new RuntimeException();\n        }\n    }\n\n    private void testCreateContainer() throws DockerAccessException {\n        PortMapping portMapping = new PortMapping(Arrays.asList(new String[] {PORT + \":\" + PORT }), new Properties());\n        ContainerHostConfig hostConfig = new ContainerHostConfig().portBindings(portMapping);\n        ContainerCreateConfig createConfig = new ContainerCreateConfig(IMAGE).command(new Arguments(\"ping google.com\")).hostConfig(hostConfig);\n\n        containerId = dockerClient.createContainer(createConfig, CONTAINER_NAME);\n        assertNotNull(containerId);\n\n        String name = dockerClient.getContainer(containerId).getName();\n        assertEquals(CONTAINER_NAME, name);\n    }\n\n    private void testDoesNotHave() throws DockerAccessException {\n        assertFalse(hasImage(IMAGE));\n    }\n\n    private void testPullImage() throws DockerAccessException {\n        dockerClient.pullImage(IMAGE, null, null);\n        assertTrue(hasImage(IMAGE));\n    }\n\n    private void testQueryPortMapping() throws DockerAccessException {\n        Map<String, PortBinding> portMap = dockerClient.getContainer(containerId).getPortBindings();\n        assertEquals(5677, portMap.values().iterator().next().getHostPort().intValue());\n    }\n\n    private void testRemoveContainer() throws DockerAccessException {\n        dockerClient.removeContainer(containerId, true);\n    }\n\n    private void testRemoveImage(String image) throws DockerAccessException {\n        dockerClient.removeImage(image, false);\n        assertFalse(hasImage(image));\n    }\n\n    private void testStartContainer() throws DockerAccessException {\n        dockerClient.startContainer(containerId);\n        assertTrue(dockerClient.getContainer(containerId).isRunning());\n    }\n\n    private void testExecContainer() throws DockerAccessException {\n        Arguments arguments = new Arguments();\n        arguments.setExec(Lists.newArrayList(\"echo\", \"test\", \"echo\"));\n        String execContainerId = dockerClient.createExecContainer(this.containerId, arguments);\n        //assertThat(dockerClient.startExecContainer(execContainerId), is(\"test echo\"));\n    }\n\n    private void testStopContainer() throws DockerAccessException {\n        dockerClient.stopContainer(containerId, 0);\n        assertFalse(dockerClient.getContainer(containerId).isRunning());\n    }\n\n    private void testTagImage() throws DockerAccessException {\n        dockerClient.tag(IMAGE, IMAGE_TAG,false);\n        assertTrue(hasImage(IMAGE_TAG));\n\n        dockerClient.removeImage(IMAGE_TAG, false);\n        assertFalse(hasImage(IMAGE_TAG));\n    }\n\n    private boolean hasImage(String image) throws DockerAccessException {\n        return dockerClient.hasImage(image);\n    }\n}\n"
  },
  {
    "path": "src/test/java/integration/DockerMachineIT.java",
    "content": "package integration;\n\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.config.DockerMachineConfiguration;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.access.DockerMachine;\n\n/*\n * if run from your ide, this test assumes you have docker-machine installed\n */\n@Ignore\npublic class DockerMachineIT {\n\n    @Test\n    public void testLaunchDockerMachine() throws Exception {\n        DockerMachineConfiguration mc = new DockerMachineConfiguration(\"default\",\"true\",\"true\");\n        DockerMachine de = new DockerMachine(new AnsiLogger(new SystemStreamLog(), true, \"build\"), mc);\n        Assert.assertTrue(de.getConnectionParameter(null) != null);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/BaseMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport static io.fabric8.maven.docker.AbstractDockerMojo.CONTEXT_KEY_LOG_DISPATCHER;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.apache.maven.model.Build;\nimport org.apache.maven.monitor.logging.DefaultLog;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.project.MavenProjectHelper;\nimport org.codehaus.plexus.logging.console.ConsoleLogger;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.log.LogDispatcher;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.RunService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Deencapsulation;\nimport mockit.Expectations;\nimport mockit.Injectable;\nimport mockit.Mocked;\n\npublic class BaseMojoTest {\n\n    @Injectable\n    protected Logger log;\n\n    @Mocked\n    protected ServiceHub serviceHub;\n\n    @Mocked\n    protected QueryService queryService;\n\n    @Mocked\n    protected RunService runService;\n\n    @Mocked\n    protected DockerAccess dockerAccess;\n\n    @Mocked\n    protected MavenProject mavenProject;\n\n    @Mocked\n    protected Build mavenBuild;\n\n    @Mocked\n    protected MavenProjectHelper mavenProjectHelper;\n\n    @Mocked\n    private LogDispatcher logDispatcher;\n\n    protected String projectGroupId;\n    protected String projectArtifactId;\n    protected String projectVersion;\n    protected String projectBuildDirectory;\n\n    protected GavLabel projectGavLabel;\n\n    protected ConsoleLogger consoleLogger;\n    protected AnsiLogger ansiLogger;\n\n    protected BaseMojoTest() {\n        this.consoleLogger = new ConsoleLogger(1, \"console\");\n        this.ansiLogger = new AnsiLogger(new DefaultLog(this.consoleLogger), false, null);\n    }\n\n    protected ImageConfiguration singleImageWithoutBuildOrRun() {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .build();\n    }\n\n    protected ImageConfiguration singleImageWithBuild() {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .build())\n                .build();\n    }\n\n    protected ImageConfiguration singleImageWithBuildWithTags(String... tags) {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .tags(Arrays.asList(tags))\n                        .build())\n                .build();\n    }\n\n    protected ImageConfiguration singleImageWithBuildAndRun() {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .build())\n                .runConfig(new RunImageConfiguration.Builder()\n                        .cmd(\"echo\")\n                        .build())\n                .build();\n    }\n\n    protected ImageConfiguration singleImageWithRun() {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .runConfig(new RunImageConfiguration.Builder()\n                        .cmd(\"echo\")\n                        .build())\n                .build();\n    }\n\n    protected List<ImageConfiguration> twoImagesWithBuild() {\n        ImageConfiguration image1 = new ImageConfiguration.Builder()\n                .name(\"example1:latest\")\n                .alias(\"example1\")\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .build())\n                .build();\n\n        ImageConfiguration image2 = new ImageConfiguration.Builder()\n                .name(\"example2:latest\")\n                .alias(\"example2\")\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .build())\n                .build();\n\n        return Arrays.asList(image1, image2);\n    }\n\n    protected void givenMavenProject(AbstractDockerMojo mojo) {\n        projectGroupId = \"mock.group\";\n        projectArtifactId = \"mock-artifact\";\n        projectVersion = \"1.0.0-MOCK\";\n        projectBuildDirectory = \"mock-target\";\n        projectGavLabel = new GavLabel(projectGroupId, projectArtifactId, projectVersion);\n\n        new Expectations() {{\n            mavenProject.getProperties(); result = new Properties(); minTimes = 0;\n            mavenProject.getBuild(); result = mavenBuild; minTimes = 0;\n            mavenProject.getGroupId(); result = projectGroupId; minTimes = 0;\n            mavenProject.getArtifactId(); result = projectArtifactId; minTimes = 0;\n            mavenProject.getVersion(); result = projectVersion; minTimes = 0;\n            mavenBuild.getDirectory(); result = projectBuildDirectory; minTimes = 0;\n        }};\n\n        Deencapsulation.setField(mojo, \"images\", Collections.emptyList());\n        Deencapsulation.setField(mojo, \"resolvedImages\", Collections.emptyList());\n        Deencapsulation.setField(mojo, \"project\", mavenProject);\n        Deencapsulation.setField(mojo, \"log\", this.ansiLogger);\n\n        mojo.setPluginContext(new HashMap());\n        mojo.getPluginContext().put(CONTEXT_KEY_LOG_DISPATCHER, logDispatcher);\n    }\n\n    protected void givenResolvedImages(AbstractDockerMojo mojo, List<ImageConfiguration> resolvedImages) {\n        Deencapsulation.setField(mojo, \"images\", resolvedImages);\n        Deencapsulation.setField(mojo, \"resolvedImages\", resolvedImages);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/RemoveMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.print.Doc;\n\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.maven.plugin.Mojo;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.model.Image;\nimport io.fabric8.maven.docker.model.ImageDetails;\nimport mockit.Deencapsulation;\nimport mockit.Expectations;\nimport mockit.Tested;\nimport mockit.Verifications;\n\npublic class RemoveMojoTest extends BaseMojoTest {\n    @Tested(fullyInitialized = false)\n    private RemoveMojo removeMojo;\n\n    /**\n     * Mock project with no images, no images are removed.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeWithNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        \n        whenMojoExecutes();\n\n        thenHasImageIsNotCalled();\n        thenListImagesIsNotCalled();\n        thenRemoveImageIsNotCalled();\n    }\n\n    /**\n     * Mock project with no images, but a pattern is provided.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeWithRemovePatternAndNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        givenRemoveNamePattern(\"example:*\");\n        givenListOfImageNames(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsCalled();\n        thenRemoveImageIsCalledFor(\"example:latest\");\n    }\n\n    /**\n     * Mock project with no images, and an empty set of patterns is provided.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeWithEmptyRemovePatternsAndNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        givenRemoveNamePattern(\" , , \");\n        givenListOfImageNames(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsNotCalled();\n        thenRemoveImageIsNotCalledFor(\"example:latest\");\n    }\n\n    /**\n     * Mock project with one image that is removed.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        \n        givenRemoveMode(\"all\");\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one image that is removed.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImageWithoutBuildOrRun() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRun());\n\n        givenRemoveMode(\"all\");\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeBuildWithOneBuildImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenRemoveMode(\"build\");\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeNullWithOneBuildImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenRemoveMode(null);\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeNullWithOneRunImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithRun());\n\n        givenRemoveMode(null);\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsNotCalled();\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeNullWithRemoveAllWithOneBuildImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenRemoveMode(null);\n        givenRemoveAll(true);\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeNullWithRemoveAllFalseWithOneBuildImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenRemoveMode(null);\n        givenRemoveAll(false);\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeNullWithRemoveAllWithOneRunImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithRun());\n\n        givenRemoveMode(null);\n        givenRemoveAll(false);\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsNotCalled();\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeDataWithOneBuildOnlyImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenRemoveMode(\"data\");\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeRunWithOneRunOnlyImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithRun());\n\n        givenRemoveMode(\"run\");\n\n        givenHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeDataWithOneBuildAndRunImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndRun());\n\n        givenRemoveMode(\"data\");\n\n        whenMojoExecutes();\n\n        thenHasImageIsNotCalled();\n        thenRemoveImageIsNotCalledFor(\"example:latest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed, as well as its tags\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeBuildWithOneBuildImageAndTags() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildWithTags(\"fastest\", \"loosest\"));\n\n        givenRemoveMode(\"build\");\n        givenSkipTag(false);\n\n        givenHasImage(\"example:latest\");\n        givenHasImage(\"example:fastest\");\n        givenHasImage(\"example:loosest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenRemoveImageIsCalledFor(\"example:fastest\");\n        thenRemoveImageIsCalledFor(\"example:loosest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with one build image that is removed, as well as its tags\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeBuildWithOneBuildImageAndTagsSkippingTags() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildWithTags(\"fastest\", \"loosest\"));\n\n        givenRemoveMode(\"build\");\n        givenSkipTag(true);\n\n        givenHasImage(\"example:latest\");\n        givenHasImage(\"example:fastest\");\n        givenHasImage(\"example:loosest\");\n\n        whenMojoExecutes();\n\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenRemoveImageIsNotCalledFor(\"example:fastest\");\n        thenRemoveImageIsNotCalledFor(\"example:loosest\");\n        thenListImagesIsNotCalled();\n    }\n\n    /**\n     * Mock project with no build images, no images are removed.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeBuildWithNoBuildImage() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRun());\n\n        givenRemoveMode(\"build\");\n\n        whenMojoExecutes();\n\n        thenHasImageIsNotCalled();\n        thenListImagesIsNotCalled();\n        thenRemoveImageIsNotCalled();\n    }\n\n    /**\n     * Mock project with one image that is removed by pattern\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImageAndPattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRunWithRemoveNamePattern(\"example:*\"));\n\n        givenRemoveMode(\"all\");\n\n        givenListOfImageNames(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsCalled();\n        thenRemoveImageIsCalledFor(\"example:latest\");\n    }\n\n    /**\n     * Mock project with one image that is removed by pattern\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImageAndImagePatternOnly() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRunWithRemoveNamePattern(\"**:latest\"));\n\n        givenRemoveMode(\"all\");\n        givenRemoveNamePattern(null);\n\n        givenListOfImageNames(\"example:latest\", \"example:fastest\", \"example:loosest\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsCalled();\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenRemoveImageIsNotCalledFor(\"example:fastest\");\n        thenRemoveImageIsNotCalledFor(\"example:loosest\");\n    }\n\n    /**\n     * Mock project with one image that is removed by pattern\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImageAndUnspecifiedTags() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRunWithRemoveNamePattern(\"example:*\"));\n\n        givenRemoveMode(\"all\");\n        givenRemoveNamePattern(null);\n\n        givenListOfImageNames(\"example:latest\", \"example:earlier-run\", \"example:1.0.0-MOCK\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsCalled();\n        thenRemoveImageIsCalledFor(\"example:latest\");\n        thenRemoveImageIsCalledFor(\"example:earlier-run\");\n        thenRemoveImageIsCalledFor(\"example:1.0.0-MOCK\");\n    }\n\n    /**\n     * Mock project with one image that is removed by pattern\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test(expected = MojoExecutionException.class)\n    public void removeAllWithOneImageAndInvalidPattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRunWithRemoveNamePattern(\"%regex[[]\"));\n\n        givenRemoveMode(\"all\");\n\n        givenListOfImageNames(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsNotCalled();\n        thenRemoveImageIsNotCalledFor(\"example:latest\");\n    }\n\n    /**\n     * Mock project with one image that is removed by pattern\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void removeAllWithOneImageAndNoApplicablePattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRunWithRemoveNamePattern(\"container=example:*\"));\n\n        givenRemoveMode(\"all\");\n\n        givenListOfImageNames(\"example:earlier-run\");\n\n        whenMojoExecutes();\n\n        thenListImagesIsNotCalled();\n        thenRemoveImageIsNotCalledFor(\"example:latest\");\n    }\n\n    private void givenMavenProject() {\n        givenMavenProject(removeMojo);\n    }\n\n    private void givenProjectWithResolvedImage(ImageConfiguration image) {\n        givenMavenProject(removeMojo);\n        givenResolvedImages(removeMojo, Collections.singletonList(image));\n    }\n\n    private void givenProjectWithResolvedImages(List<ImageConfiguration> resolvedImages) {\n        givenMavenProject(removeMojo);\n        givenResolvedImages(removeMojo, resolvedImages);\n    }\n\n    protected ImageConfiguration singleImageWithoutBuildOrRunWithRemoveNamePattern(String removeNamePattern) {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .removeNamePattern(removeNamePattern)\n                .build();\n    }\n\n    private void givenRemoveMode(String removeMode) {\n        Deencapsulation.setField(removeMojo, \"removeMode\", removeMode);\n    }\n\n    private void givenRemoveAll(boolean removeAll) {\n        Deencapsulation.setField(removeMojo, \"removeAll\", removeAll);\n    }\n\n    private void givenRemoveNamePattern(String removeNamePattern) {\n        Deencapsulation.setField(removeMojo, \"removeNamePattern\", removeNamePattern);\n    }\n\n    private void givenSkipTag(boolean skipTag) {\n        Deencapsulation.setField(removeMojo, \"skipTag\", skipTag);\n    }\n\n    private void givenHasImage(String imageName) throws DockerAccessException {\n        new Expectations() {{\n            queryService.hasImage(imageName); result = true; minTimes = 0;\n        }};\n    }\n\n    private void givenListOfImageNames(String... imageNames) throws DockerAccessException {\n        final List<Image> images = new ArrayList<>();\n\n        for(String imageName : imageNames) {\n            JsonObject json = new JsonObject();\n            json.addProperty(ImageDetails.ID, DigestUtils.sha256Hex(imageName));\n\n            JsonArray repoTags = new JsonArray();\n            repoTags.add(imageName);\n\n            json.add(ImageDetails.REPO_TAGS, repoTags);\n\n            images.add(new ImageDetails(json));\n        }\n\n        new Expectations() {{\n            queryService.listImages(anyBoolean); result = images; minTimes = 0;\n            for(String imageName : imageNames) {\n                queryService.hasImage(imageName); result = true; minTimes = 0;\n                dockerAccess.removeImage(imageName, false); result = true; minTimes = 0;\n                dockerAccess.removeImage(imageName, true); result = true; minTimes = 0;\n            }\n        }};\n    }\n\n    private void whenMojoExecutes() throws ExecException, IOException, MojoExecutionException {\n        removeMojo.executeInternal(serviceHub);\n    }\n    \n    private void thenHasImageIsNotCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.hasImage(anyString); times = 0;\n        }};\n    }\n    \n    private void thenListImagesIsCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.listImages(anyBoolean);\n        }};\n    }\n\n    private void thenListImagesIsNotCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.listImages(anyBoolean); times = 0;\n        }};\n    }\n\n    private void thenRemoveImageIsNotCalled() throws DockerAccessException {\n        new Verifications() {{\n            dockerAccess.removeImage(anyString, anyBoolean); times = 0;\n        }};\n    }\n\n    private void thenRemoveImageIsNotCalledFor(String imageName) throws DockerAccessException {\n        new Verifications() {{\n            dockerAccess.removeImage(imageName, true); times = 0;\n        }};\n    }\n\n    private void thenRemoveImageIsCalledFor(String imageName) throws DockerAccessException {\n        new Verifications() {{\n            dockerAccess.removeImage(imageName, true);\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/SaveMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport mockit.Deencapsulation;\nimport mockit.Expectations;\nimport mockit.Tested;\nimport mockit.Verifications;\n\npublic class SaveMojoTest extends BaseMojoTest {\n    @Tested(fullyInitialized = false)\n    private SaveMojo saveMojo;\n\n    @Test\n    public void saveWithoutNameAliasOrFile() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceHasImage(\"example:latest\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example:latest\", \"mock-target/example-latest.tar.gz\", ArchiveCompression.gzip);\n    }\n\n    @Test\n    public void saveWithoutNameAliasOrFileSkipped() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        Deencapsulation.setField(saveMojo, \"skipSave\", true);\n\n        whenMojoExecutes();\n\n        thenHasImageNotCalled();\n        thenNoImageIsSaved();\n    }\n\n    @Test(expected = MojoExecutionException.class)\n    public void saveMissingWithoutNameAliasOrFile() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceDoesNotHaveImage();\n\n        whenMojoExecutes();\n\n        thenNoImageIsSaved();\n    }\n\n    @Test\n    public void saveAndAttachWithoutNameAliasOrFile() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceHasImage(\"example:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveClassifier\", \"archive\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example:latest\", \"mock-target/example-latest.tar.gz\", ArchiveCompression.gzip);\n        thenArtifactAttached(\"tar.gz\", \"archive\", \"mock-target/example-latest.tar.gz\");\n    }\n\n    @Test\n    public void saveWithFile() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceHasImage(\"example:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveFile\", \"destination/archive-name.tar.bz2\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example:latest\", \"destination/archive-name.tar.bz2\", ArchiveCompression.bzip2);\n    }\n\n    @Test\n    public void saveWithFileInSystemProperty() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceHasImage(\"example:latest\");\n\n        System.setProperty(\"docker.save.file\", \"destination/archive-name.tar.bz2\");\n\n        try {\n            whenMojoExecutes();\n        } finally {\n            System.clearProperty(\"docker.save.file\");\n        }\n\n        thenImageIsSaved(\"example:latest\", \"destination/archive-name.tar.bz2\", ArchiveCompression.bzip2);\n    }\n\n    @Test\n    public void saveAndAttachWithFile() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenQueryServiceHasImage(\"example:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveFile\", \"destination/archive-name.tar.bz2\");\n        Deencapsulation.setField(saveMojo, \"saveClassifier\", \"archive\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example:latest\", \"destination/archive-name.tar.bz2\", ArchiveCompression.bzip2);\n        thenArtifactAttached(\"tar.bz\", \"archive\", \"destination/archive-name.tar.bz2\");\n    }\n\n    @Test\n    public void saveWithAlias() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImages(twoImagesWithBuild());\n        givenQueryServiceHasImage(\"example2:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveAlias\", \"example2\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example2:latest\", \"mock-target/example2-1.0.0-MOCK.tar.gz\", ArchiveCompression.gzip);\n    }\n\n\n    @Test(expected = MojoExecutionException.class)\n    public void saveWithNonExistentAlias() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        Deencapsulation.setField(saveMojo, \"saveAlias\", \"example3\");\n\n        whenMojoExecutes();\n\n        thenHasImageNotCalled();\n        thenNoImageIsSaved();\n    }\n\n    @Test\n    public void saveAndAttachWithAlias() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImages(twoImagesWithBuild());\n        givenQueryServiceHasImage(\"example2:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveAlias\", \"example2\");\n        Deencapsulation.setField(saveMojo, \"saveClassifier\", \"archive-%a\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example2:latest\", \"mock-target/example2-1.0.0-MOCK.tar.gz\", ArchiveCompression.gzip);\n        thenArtifactAttached(\"tar.gz\", \"archive-example2\", \"mock-target/example2-1.0.0-MOCK.tar.gz\");\n    }\n\n    @Test\n    public void saveAndAttachWithAliasButAlsoClassifier() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImages(twoImagesWithBuild());\n        givenQueryServiceHasImage(\"example2:latest\");\n\n        Deencapsulation.setField(saveMojo, \"saveAlias\", \"example2\");\n        Deencapsulation.setField(saveMojo, \"saveClassifier\", \"preferred\");\n\n        whenMojoExecutes();\n\n        thenImageIsSaved(\"example2:latest\", \"mock-target/example2-1.0.0-MOCK.tar.gz\", ArchiveCompression.gzip);\n        thenArtifactAttached(\"tar.gz\", \"preferred\", \"mock-target/example2-1.0.0-MOCK.tar.gz\");\n    }\n\n    @Test\n    public void noFailureWithEmptyImageList() throws DockerAccessException, MojoExecutionException {\n        Deencapsulation.setField(saveMojo, \"images\", Collections.<ImageConfiguration>emptyList());\n        Deencapsulation.setField(saveMojo, \"resolvedImages\", Collections.<ImageConfiguration>emptyList());\n\n        whenMojoExecutes();\n        // no action from mojo\n    }\n\n    @Test\n    public void noFailureWithEmptyBuildImageList() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithoutBuildOrRun());\n\n        whenMojoExecutes();\n        // no failure\n    }\n\n    @Test(expected = MojoExecutionException.class)\n    public void failureWithMultipleBuildImageList() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImages(twoImagesWithBuild());\n\n        whenMojoExecutes();\n        // throws exception\n    }\n\n    @Test(expected = MojoExecutionException.class)\n    public void failureWithSaveAliasAndName() throws DockerAccessException, MojoExecutionException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        Deencapsulation.setField(saveMojo, \"saveAlias\", \"not-null\");\n        Deencapsulation.setField(saveMojo, \"saveName\", \"not-null\");\n\n        whenMojoExecutes();\n        // fails\n    }\n\n    @Override\n    protected void givenMavenProject(AbstractDockerMojo mojo) {\n        super.givenMavenProject(mojo);\n\n        Deencapsulation.setField(mojo, \"projectHelper\", mavenProjectHelper);\n    }\n\n    private void givenProjectWithResolvedImage(ImageConfiguration image) {\n        givenMavenProject(saveMojo);\n        givenResolvedImages(saveMojo, Collections.singletonList(image));\n    }\n\n    private void givenProjectWithResolvedImages(List<ImageConfiguration> resolvedImages) {\n        givenMavenProject(saveMojo);\n        givenResolvedImages(saveMojo, resolvedImages);\n    }\n\n    private void whenMojoExecutes() throws DockerAccessException, MojoExecutionException {\n        saveMojo.executeInternal(serviceHub);\n    }\n\n    private void givenQueryServiceHasImage(final String name) throws DockerAccessException {\n        new Expectations() {{\n            queryService.hasImage(name); result = true;\n        }};\n    }\n\n    private void givenQueryServiceDoesNotHaveImage() throws DockerAccessException {\n        new Expectations() {{\n            queryService.hasImage(anyString); result = false;\n        }};\n    }\n\n    private void givenQueryServiceDoesNotHaveImage(final String name) throws DockerAccessException {\n        new Expectations() {{\n            queryService.hasImage(name); result = false;\n        }};\n    }\n\n    private void thenHasImageNotCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.hasImage(anyString); times = 0;\n        }};\n    }\n\n    private void thenNoImageIsSaved() throws DockerAccessException {\n        new Verifications() {{\n            dockerAccess.saveImage(anyString, anyString, (ArchiveCompression)any); times = 0;\n        }};\n    }\n\n    private void thenImageIsSaved(String name, String fileName, ArchiveCompression compression) throws DockerAccessException {\n        new Verifications() {{\n            dockerAccess.saveImage(name, fileName, compression);\n        }};\n    }\n\n    private void thenArtifactAttached(String type, String classifier, String fileName) {\n        new Verifications() {{\n            mavenProjectHelper.attachArtifact(mavenProject, type, classifier, new File(fileName));\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/StopMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.print.Doc;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.model.Container;\nimport mockit.Deencapsulation;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\n\npublic class StopMojoTest extends BaseMojoTest {\n    @Tested(fullyInitialized = false)\n    private StopMojo stopMojo;\n\n    @Mocked\n    private Container runningInstance;\n\n    /**\n     * Mock project with no images, no containers are stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with no images, but stopNamePattern is set, so containers are checked.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithStopNamePatternButNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        givenStopNamePattern(\"**/example:*\");\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with no images, but stopNamePattern is set to a list that evaluates as empty,\n     * so containers are checked.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithStopNamePatternThatIsActuallyEmpty() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        givenStopNamePattern(\" , , \");\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsNotCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with no images, but stopNamePattern is set, so a container is stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMatchingStopNamePatternButNoImages() throws IOException, MojoExecutionException, ExecException {\n        givenMavenProject();\n        givenStopNamePattern(\"**/example:*\");\n\n        givenRunningContainer(\"container-id\", \"example-1\", \"example:latest\");\n        givenContainerHasGavLabels();\n        givenListOfRunningContainers();\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenContainerIsStopped(\"container-id\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageNotLabelled() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n        givenContainerIsRunningForImage(\"example:latest\", \"container-id\", \"example-1\");\n        // mocking will return empty label map by default, so the GAV label is not present\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, and it is labelled.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageIsLabelled() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenContainerIsRunningForImage(\"example:latest\", \"container-id\", \"example-1\");\n        givenContainerHasGavLabels();\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsStopped(\"container-id\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, and it is labelled.\n     * Removal should pass true for removeVolumes.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageIsLabelledRemoveVolume() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenContainerIsRunningForImage(\"example:latest\", \"container-id\", \"example-1\");\n        givenContainerHasGavLabels();\n\n        Deencapsulation.setField(stopMojo, \"removeVolumes\", true);\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsStopped(\"container-id\", false, true);\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, and it is labelled.\n     * Removal should pass true for keepContainers, and query should exclude stopped containers.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageIsLabelledKeepingContainers() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenContainerIsRunningForImage(\"example:latest\", \"container-id\", \"example-1\");\n        givenContainerHasGavLabels();\n\n        Deencapsulation.setField(stopMojo, \"keepContainer\", true);\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsStopped(\"container-id\", true, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, which is not labelled,\n     * but allContainers is true.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageNotLabelledButAllContainersTrue() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenContainerIsRunningForImage(\"example:latest\", \"container-id\", \"example-1\");\n        // don't configure GAV labels\n\n        givenAllContainersIsTrue();\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsStopped(\"container-id\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices - only the last should be stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesCheckLast(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\");\n        givenContainersAreRunningForImage(\"example:latest\", running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsNotStopped(\"container-id-1\");\n        thenContainerIsNotStopped(\"container-id-2\");\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices, but there's a gap, so the last before\n     * the gap and all the ones after are stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesIndexPatternMissingIndices(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\");\n        // having a gap in the numbering causes the enumeration of images to stop early\n        givenRunningContainer(running3, \"container-id-4\", \"example-4\");\n        givenContainersAreRunningForImage(\"example:latest\", running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsNotStopped(\"container-id-1\");\n        thenContainerIsStopped(\"container-id-2\", false, false);\n        thenContainerIsStopped(\"container-id-4\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices - but this doesn't match the container\n     * name pattern, so all are stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesNoIndexNamePattern(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        this.consoleLogger.setThreshold(0);\n        givenProjectWithResolvedImage(singleImageWithBuild());\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\");\n        givenContainersAreRunningForImage(\"example:latest\", running1, running2, running3);\n\n        // If name pattern doesn't contain index placeholder, all containers are stopped\n        Deencapsulation.setField(stopMojo, \"containerNamePattern\", \"%n\");\n\n        whenMojoExecutes();\n\n        thenContainerLookupByImageOccurs(\"example:latest\");\n        thenListContainersIsNotCalled();\n        thenContainerIsStopped(\"container-id-1\", false, false);\n        thenContainerIsStopped(\"container-id-2\", false, false);\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageAndPatternDoesNotMatch() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"**/example:loudest\"));\n\n        givenRunningContainer(\"container-id-1\", \"example-1\", \"example:latest\");\n\n        givenListOfRunningContainers();\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set for images only and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageAndNoApplicablePattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\" , , \"));\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsNotCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set for images only and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test(expected = MojoExecutionException.class)\n    public void stopWithSingleImageAndImagePatternSyntaxException() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\" , image=%regex[surprise! [], \"));\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsNotCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set for container names only and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test(expected = MojoExecutionException.class)\n    public void stopWithSingleImageAndContainerNamePatternSyntaxException()\n            throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\" ,name= %regex[surprise! [], \"));\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsNotCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set for images only and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageAndImageOnlyPattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"image=**/example:loudest\"));\n\n        givenRunningContainer(\"container-id-1\", \"example-1\", \"example:latest\");\n        givenContainerHasGavLabels();\n        givenListOfRunningContainers();\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates running image, but it is not labelled.\n     * The stopNamePattern is set for container names only and does not match.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithSingleImageAndNameOnlyPattern() throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"name=exemplary-*\"));\n\n        givenRunningContainer(\"container-id-1\", \"example-1\", \"example:latest\");\n        givenContainerHasGavLabels();\n        givenListOfRunningContainers();\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenNoContainerIsStopped();\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices, but pattern matching is in effect, so\n     * they are all stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesImageNamePattern(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"example:*\"));\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\", \"example:v1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\", \"example:v2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\", \"example:v3\");\n        givenListOfRunningContainers(running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenContainerIsStopped(\"container-id-1\", false, false);\n        thenContainerIsStopped(\"container-id-2\", false, false);\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices, but pattern matching is in effect, so\n     * they are all stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesContainerNamePattern(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"example-*\"));\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\", \"example:v1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\", \"example:v2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\", \"example:v3\");\n        givenListOfRunningContainers(running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenContainerIsStopped(\"container-id-1\", false, false);\n        thenContainerIsStopped(\"container-id-2\", false, false);\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices, but pattern matching is in effect, so\n     * they are all stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesContainerAndImageNamePattern(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"name=%regex[example-[14]],image=*:v2,**:v3\"));\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\", \"example:v1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\", \"example:v2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\", \"example:v3\");\n        givenListOfRunningContainers(running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenContainerIsStopped(\"container-id-1\", false, false);\n        thenContainerIsStopped(\"container-id-2\", false, false);\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n    /**\n     * Mock project with one image, query service indicates three running images, none labelled,\n     * but allContainers is true.\n     *\n     * The containers are named with ascending indices, but pattern matching is in effect, so\n     * two of them are stopped.\n     *\n     * @throws IOException\n     * @throws MojoExecutionException\n     * @throws ExecException\n     */\n    @Test\n    public void stopWithMultipleImagesContainerNameRegexPattern(@Mocked Container running1, @Mocked Container running2, @Mocked Container running3) throws IOException, MojoExecutionException, ExecException {\n        givenProjectWithResolvedImage(singleImageWithBuildAndStopNamePattern(\"%regex[example-[13]]\"));\n\n        givenAllContainersIsTrue();\n\n        givenRunningContainer(running1, \"container-id-1\", \"example-1\", \"example:v1\");\n        givenRunningContainer(running2, \"container-id-2\", \"example-2\", \"example:v2\");\n        givenRunningContainer(running3, \"container-id-3\", \"example-3\", \"example:v3\");\n        givenListOfRunningContainers(running1, running2, running3);\n\n        whenMojoExecutes();\n\n        thenNoContainerLookupByImageOccurs();\n        thenListContainersIsCalled();\n        thenContainerIsStopped(\"container-id-1\", false, false);\n        thenContainerIsNotStopped(\"container-id-2\");\n        thenContainerIsStopped(\"container-id-3\", false, false);\n    }\n\n    private void givenMavenProject() {\n        givenMavenProject(stopMojo);\n    }\n\n    private void givenProjectWithResolvedImage(ImageConfiguration image) {\n        givenMavenProject(stopMojo);\n        givenResolvedImages(stopMojo, Collections.singletonList(image));\n    }\n\n    private void givenProjectWithResolvedImages(List<ImageConfiguration> resolvedImages) {\n        givenMavenProject(stopMojo);\n        givenResolvedImages(stopMojo, resolvedImages);\n    }\n\n    protected ImageConfiguration singleImageWithBuildAndStopNamePattern(String stopNamePattern) {\n        return new ImageConfiguration.Builder()\n                .name(\"example:latest\")\n                .stopNamePattern(stopNamePattern)\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"scratch\")\n                        .build())\n                .build();\n    }\n\n    private void givenStopNamePattern(String stopNamePattern) {\n        Deencapsulation.setField(stopMojo, \"stopNamePattern\", stopNamePattern);\n    }\n\n    private void givenAllContainersIsTrue() {\n        Deencapsulation.setField(stopMojo, \"allContainers\", true);\n    }\n\n    private void givenRunningContainer(String containerId, String containerName, String imageName) {\n        givenRunningContainer(this.runningInstance, containerId, containerName, imageName);\n    }\n\n    private void givenRunningContainer(Container instance, String containerId, String containerName) {\n        givenRunningContainer(instance, containerId, containerName, null);\n    }\n\n    private void givenRunningContainer(Container instance, String containerId, String containerName, String imageName) {\n        new Expectations() {{\n            instance.getId(); result = containerId; minTimes = 0;\n            instance.getName(); result = containerName; minTimes = 0;\n            instance.getImage(); result = imageName; minTimes = 0;\n        }};\n    }\n\n    private void givenListOfRunningContainers(Container... instances) throws DockerAccessException {\n        new Expectations() {{\n            queryService.listContainers(anyBoolean);\n            result = (instances.length == 0 ? Collections.singletonList(runningInstance) : Arrays.asList(instances));\n            minTimes = 0;\n        }};\n    }\n\n    private void givenContainerIsRunningForImage(String imageName, String containerId, String containerName) throws DockerAccessException {\n        givenRunningContainer(this.runningInstance, containerId, containerName, imageName);\n        givenContainersAreRunningForImage(imageName, this.runningInstance);\n    }\n\n    private void givenContainersAreRunningForImage(String imageName, Container... containers) throws DockerAccessException {\n        new Expectations() {{\n            queryService.getContainersForImage(imageName, anyBoolean);\n            result = Arrays.asList(containers);\n            minTimes = 0;\n        }};\n    }\n\n    private void givenContainerHasGavLabels() {\n        givenContainerHasGavLabels(this.runningInstance);\n    }\n\n    private void givenContainerHasGavLabels(Container instance) {\n        new Expectations() {{\n            instance.getLabels();\n            result = Collections.singletonMap(projectGavLabel.getKey(), projectGavLabel.getValue());\n            minTimes = 0;\n        }};\n    }\n\n    private void whenMojoExecutes() throws ExecException, IOException, MojoExecutionException {\n        stopMojo.executeInternal(serviceHub);\n    }\n\n    private void thenNoContainerIsStopped() throws ExecException, DockerAccessException {\n        new Verifications() {{\n            runService.stopContainer(anyString, (ImageConfiguration)any, anyBoolean, anyBoolean);\n            times = 0;\n        }};\n    }\n\n    private void thenContainerIsStopped(String containerId, boolean keepContainer, boolean removeVolumes) throws ExecException, DockerAccessException {\n        new Verifications() {{\n            runService.stopContainer(containerId, (ImageConfiguration)any, keepContainer, removeVolumes);\n        }};\n    }\n\n    private void thenContainerIsNotStopped(String containerId) throws ExecException, DockerAccessException {\n        new Verifications() {{\n            runService.stopContainer(containerId, (ImageConfiguration)any, anyBoolean, anyBoolean);\n            times = 0;\n        }};\n    }\n\n    private void thenNoContainerLookupByImageOccurs() throws DockerAccessException {\n        new Verifications() {{\n            queryService.getContainersForImage(anyString, anyBoolean);\n            times = 0;\n        }};\n    }\n\n    private void thenContainerLookupByImageOccurs(String imageName) throws DockerAccessException {\n        new Verifications() {{\n            queryService.getContainersForImage(imageName, anyBoolean);\n            minTimes = 1;\n        }};\n    }\n\n    private void thenListContainersIsCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.listContainers(anyBoolean);\n            minTimes = 1;\n        }};\n    }\n\n    private void thenListContainersIsNotCalled() throws DockerAccessException {\n        new Verifications() {{\n            queryService.listContainers(anyBoolean);\n            times = 0;\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/UrlBuilderTest.java",
    "content": "package io.fabric8.maven.docker;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.UnsupportedEncodingException;\nimport java.net.*;\nimport java.util.HashMap;\n\nimport io.fabric8.maven.docker.access.BuildOptions;\nimport io.fabric8.maven.docker.access.UrlBuilder;\nimport io.fabric8.maven.docker.util.ImageName;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 13/07/16\n */\npublic class UrlBuilderTest {\n\n    @Test\n    public void buildImage() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n        assertEquals(new URI(\"/1.0/build?dockerfile=df&nocache=0&t=image1\"),\n            new URI(builder.buildImage(\"image1\", new BuildOptions().dockerfile(\"df\").noCache(false))));\n        assertEquals(new URI(\"/1.0/build?dockerfile=df&forcerm=1&nocache=1&t=image1\"),\n            new URI(builder.buildImage(\"image1\", new BuildOptions().forceRemove(true).noCache(true).dockerfile(\"df\"))));\n        HashMap<String, String> m = new HashMap<>();\n        m.put(\"k1\", \"v1\");\n        m.put(\"k2\", \"v2\");\n        assertEquals(\"/1.0/build?buildargs=%7B%22k1%22%3A%22v1%22%2C%22k2%22%3A%22v2%22%7D&dockerfile=df&t=image1\",\n            builder.buildImage(\"image1\", new BuildOptions().dockerfile(\"df\").buildArgs(m)));\n        HashMap<String, String> options = new HashMap<>();\n        options.put(\"cpusetcpus\", \"1\");\n        options.put(\"memswap\", \"-1\");\n        assertEquals(\"/1.0/build?buildargs=%7B%22k1%22%3A%22v1%22%2C%22k2%22%3A%22v2%22%7D&cpusetcpus=1&memswap=-1&t=image1\",\n                     builder.buildImage(\"image1\", new BuildOptions(options).buildArgs(m)));\n        options.put(\"dockerfile\",\"blub\");\n        assertEquals(\"/1.0/build?cpusetcpus=1&dockerfile=bla&memswap=-1&t=image1\",\n                     builder.buildImage(\"image1\", new BuildOptions(options).dockerfile(\"bla\")));\n        assertEquals(\"/1.0/build?cpusetcpus=1&dockerfile=holla&memswap=-1&t=image1\",\n                     builder.buildImage(\"image1\", new BuildOptions(options).dockerfile(\"bla\").addOption(\"dockerfile\",\"holla\")));\n        assertEquals(\"/1.0/build?cpusetcpus=1&dockerfile=bla&memswap=-1&squash=1&t=image1\",\n            builder.buildImage(\"image1\", new BuildOptions(options).dockerfile(\"bla\").squash(true)));\n\n    }\n\n    @Test\n    public void copyArchive() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n        assertEquals(new URI(\"/1.0/containers/cid/archive?path=tp\"), new URI(builder.copyArchive(\"cid\", \"tp\")));\n\n    }\n\n    @Test\n    public void containerLogs() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n        assertEquals(new URI(\"/1.0/containers/cid/logs?follow=0&stderr=1&stdout=1&timestamps=1\"),\n                     new URI(builder.containerLogs(\"cid\", false)));\n\n    }\n\n    @Test\n    public void deleteImage() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n        assertEquals(new URI(\"/1.0/images/n1?force=0\"), new URI(builder.deleteImage(\"n1\", false)));\n\n    }\n\n    @Test\n    public void getImage() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n        assertEquals(new URI(\"/1.0/images/n1%3Alatest/get\"), new URI(builder.getImage(new ImageName(\"n1:latest\"))));\n    }\n\n    @Test\n    public void listImages() throws MalformedURLException, UnsupportedEncodingException, URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n\n        assertEquals(new URI(\"/1.0/images/json?all=0\"), new URI(builder.listImages(false)));\n        assertEquals(new URI(\"/1.0/images/json?all=1\"), new URI(builder.listImages(true)));\n    }\n\n    @Test\n    public void listContainers() throws MalformedURLException, UnsupportedEncodingException, URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\",\"1.0\");\n\n        assertEquals(new URI(\"/1.0/containers/json?all=0\"), new URI(builder.listContainers(false)));\n        assertEquals(new URI(\"/1.0/containers/json?all=1&filters=\" + URLEncoder.encode(\"{\\\"ancestor\\\":[\\\"nginx\\\"]}\",\"UTF8\")),\n                     new URI(builder.listContainers(true, \"ancestor\", \"nginx\")));\n\n        try {\n            builder.listContainers(false,\"ancestor\");\n            fail();\n        } catch (IllegalArgumentException exp) {\n            assertTrue(exp.getMessage().contains(\"pair\"));\n        }\n    }\n\n    @Test\n    public void loadImage() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\", \"1.0\");\n        assertEquals(new URI(\"/1.0/images/load\"),new URI(builder.loadImage()));\n    }\n\n    @Test\n    public void pullImage() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\", \"1.0\");\n        assertEquals(new URI(\"/1.0/images/create?fromImage=reg%2Ft1&tag=latest\"),\n                     new URI(builder.pullImage(new ImageName(\"t1:latest\"), \"reg\")));\n        assertEquals(new URI(\"/1.0/images/create?fromImage=reg%2Ft1&tag=latest\"),\n                     new URI(builder.pullImage(new ImageName(\"t1\"), \"reg\")));\n    }\n\n    @Test\n    public void tagContainer() throws URISyntaxException {\n        UrlBuilder builder = new UrlBuilder(\"\", \"1.0\");\n        assertEquals(new URI(\"/1.0/images/t1%3Alatest/tag?force=1&repo=new&tag=tag1\"),\n                     new URI(builder.tagContainer(new ImageName(\"t1:latest\"), new ImageName(\"new:tag1\"), true)));\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/VolumeCreateMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Test;\n\npublic class VolumeCreateMojoTest {\n\n    @Injectable\n    Logger log;\n\n    @Tested(fullyInitialized = false)\n    private VolumeCreateMojo volumeCreateMojo;\n\n    @Mocked\n    ServiceHub serviceHub;\n\n    @Test\n    public void createVolumeGetVolumesReturnsNull() throws DockerAccessException, MojoExecutionException {\n        volumeCreateMojo.executeInternal(serviceHub);\n        new Verifications(){{\n            log.info(\"No volume configuration found.\"); times = 1;\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/VolumeRemoveMojoTest.java",
    "content": "package io.fabric8.maven.docker;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Test;\n\npublic class VolumeRemoveMojoTest {\n\n    @Injectable\n    Logger log;\n\n    @Tested(fullyInitialized = false)\n    private VolumeRemoveMojo volumeRemoveMojo;\n\n    @Mocked\n    ServiceHub serviceHub;\n\n    @Test\n    public void removeVolumeGetVolumesReturnsNull() throws DockerAccessException, MojoExecutionException {\n        volumeRemoveMojo.executeInternal(serviceHub);\n        new Verifications(){{\n            log.info(\"No volume configuration found.\"); times = 1;\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/BuildConfigTest.java",
    "content": "package io.fabric8.maven.docker.access;/*\n *\n * Copyright 2015-2016 Red Hat, Inc.\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\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.util.JsonFactory;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 03/01/17\n */\npublic class BuildConfigTest {\n\n    @Test\n    public void empty() {\n        BuildOptions opts = new BuildOptions();\n        assertEquals(0, opts.getOptions().size());\n    }\n\n    @Test\n    public void forcerm() {\n        BuildOptions opts = new BuildOptions().forceRemove(false);\n        assertEquals(0, opts.getOptions().size());\n        opts = new BuildOptions().forceRemove(true);\n        assertEquals(\"1\", opts.getOptions().get(\"forcerm\"));\n    }\n\n    @Test\n    public void nocache() {\n        BuildOptions opts = new BuildOptions().noCache(true);\n        assertEquals(\"1\", opts.getOptions().get(\"nocache\"));\n        opts = new BuildOptions().noCache(false);\n        assertEquals(\"0\", opts.getOptions().get(\"nocache\"));\n    }\n\n    @Test\n    public void squash() {\n        BuildOptions opts = new BuildOptions().squash(true);\n        assertEquals(1, opts.getOptions().size());\n        assertEquals(\"1\", opts.getOptions().get(\"squash\"));\n        opts.squash(false);\n        assertEquals(\"0\", opts.getOptions().get(\"squash\"));\n        opts.addOption(\"squash\",\"1\");\n        assertEquals(\"1\", opts.getOptions().get(\"squash\"));\n        assertEquals(1, opts.getOptions().size());\n    }\n\n    @Test\n    public void dockerfile() {\n        BuildOptions opts = new BuildOptions().dockerfile(\"blub\");\n        assertEquals(\"blub\", opts.getOptions().get(\"dockerfile\"));\n        opts = new BuildOptions().dockerfile(null);\n        assertEquals(0, opts.getOptions().size());\n    }\n\n    @Test\n    public void buildArgs() {\n        Map<String,String> args = Collections.singletonMap(\"arg1\",\"blub\");\n        BuildOptions opts = new BuildOptions().buildArgs(args);\n        assertEquals(JsonFactory.newJsonObject(args).toString(), opts.getOptions().get(\"buildargs\"));\n        opts = new BuildOptions().buildArgs(null);\n        assertEquals(0, opts.getOptions().size());\n\n    }\n\n    @Test\n    public void override() {\n        BuildOptions opts = new BuildOptions(Collections.singletonMap(\"nocache\",\"1\"));\n        assertEquals(1, opts.getOptions().size());\n        assertEquals(\"1\", opts.getOptions().get(\"nocache\"));\n        opts.noCache(false);\n        assertEquals(\"0\", opts.getOptions().get(\"nocache\"));\n        opts.addOption(\"nocache\",\"1\");\n        assertEquals(\"1\", opts.getOptions().get(\"nocache\"));\n    }\n\n    @Test\n    public void cacheFrom() {\n        BuildOptions opts = new BuildOptions().cacheFrom(Arrays.asList(\"foo/bar:latest\"));\n        assertEquals(\"[\\\"foo/bar:latest\\\"]\", opts.getOptions().get(\"cachefrom\"));\n\n        opts.cacheFrom(Arrays.asList(\"foo/bar:latest\", \"foo/baz:1.0\"));\n        assertEquals(\"[\\\"foo/bar:latest\\\",\\\"foo/baz:1.0\\\"]\", opts.getOptions().get(\"cachefrom\"));\n\n        opts.cacheFrom(Arrays.asList());\n        assertEquals(null, opts.getOptions().get(\"cachefrom\"));\n\n        opts.cacheFrom(null);\n        assertEquals(null, opts.getOptions().get(\"cachefrom\"));\n    }\n\n    @Test\n    public void network() {\n        BuildOptions opts = new BuildOptions().network(null);\n        assertEquals(null, opts.getOptions().get(\"networkmode\"));\n\n        opts.network(\"host\");\n        assertEquals(\"host\", opts.getOptions().get(\"networkmode\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/ContainerCreateConfigTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.util.JsonFactory;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/*\n *\n * Copyright 2014 Roland Huss\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\n/**\n * @author roland\n * @since 27/03/15\n */\npublic class ContainerCreateConfigTest {\n\n    @Test\n    public void testEnvironment() throws Exception {\n        ContainerCreateConfig cc = new ContainerCreateConfig(\"testImage\");\n        Map<String, String> envMap = getEnvMap();\n        cc.environment(copyPropsToFile(), envMap, Collections.<String, String>emptyMap());\n        JsonArray env = getEnvArray(cc);\n        assertNotNull(env);\n        assertEquals(6, env.size());\n        List<String> envAsString = convertToList(env);\n        assertTrue(envAsString.contains(\"JAVA_OPTS=-Xmx512m\"));\n        assertTrue(envAsString.contains(\"TEST_SERVICE=SECURITY\"));\n        assertTrue(envAsString.contains(\"EXTERNAL_ENV=TRUE\"));\n        assertTrue(envAsString.contains(\"TEST_HTTP_ADDR=${docker.container.consul.ip}\"));\n        assertTrue(envAsString.contains(\"TEST_CONSUL_IP=+${docker.container.consul.ip}:8080\"));\n        assertTrue(envAsString.contains(\"TEST_CONSUL_IP_WITHOUT_DELIM=${docker.container.consul.ip}:8225\"));\n    }\n\n    @Test\n    public void testEnvironmentEmptyPropertiesFile() {\n        ContainerCreateConfig cc = new ContainerCreateConfig(\"testImage\");\n        cc.environment(null, getEnvMap(),Collections.<String, String>emptyMap());\n        JsonArray env = getEnvArray(cc);\n        assertEquals(5, env.size());\n    }\n\n    @Test\n    public void testBind() {\n        String[] testData = new String[] {\n            \"c:\\\\this\\\\is\\\\my\\\\path:/data\", \"/data\",\n            \"/home/user:/user\", \"/user\",\n            \"c:\\\\this\\\\too:/data:ro\", \"/data\"};\n        for (int i = 0; i < testData.length; i += 2) {\n            ContainerCreateConfig cc = new ContainerCreateConfig(\"testImage\");\n            cc.binds(Arrays.asList(testData[i]));\n\n            JsonObject volumes = (JsonObject) JsonFactory.newJsonObject(cc.toJson()).get(\"Volumes\");\n            assertEquals(1, volumes.size());\n            assertTrue(volumes.has(testData[i+1]));\n        }\n    }\n\n\n    @Test\n    public void testNullEnvironment() {\n        ContainerCreateConfig cc= new ContainerCreateConfig(\"testImage\");\n        cc.environment(null,null,Collections.<String, String>emptyMap());\n        JsonObject config = JsonFactory.newJsonObject(cc.toJson());\n        assertFalse(config.has(\"Env\"));\n    }\n\n    @Test\n    public void testEnvNoMap() throws IOException {\n        ContainerCreateConfig cc= new ContainerCreateConfig(\"testImage\");\n        cc.environment(copyPropsToFile(),null,Collections.<String, String>emptyMap());\n        JsonArray env = getEnvArray(cc);\n        assertEquals(2, env.size());\n        List<String> envAsString = convertToList(env);\n        assertTrue(envAsString.contains(\"EXTERNAL_ENV=TRUE\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testNoPropFile() {\n        ContainerCreateConfig cc= new ContainerCreateConfig(\"testImage\");\n        cc.environment(\"/not/really/a/file\",null,Collections.<String, String>emptyMap());\n    }\n\n\n    private JsonArray getEnvArray(ContainerCreateConfig cc) {\n        JsonObject config = JsonFactory.newJsonObject(cc.toJson());\n        return (JsonArray) config.get(\"Env\");\n    }\n\n    private String copyPropsToFile() throws IOException {\n        File tempFile = File.createTempFile(\"dockertest\", \"props\");\n        FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(\"test-environment.props\"), tempFile);\n        return tempFile.getAbsolutePath();\n    }\n\n\n    private List<String> convertToList(JsonArray env) {\n        List<String> envAsString = new ArrayList<>();\n        for (int i = 0; i < env.size(); i++) {\n            envAsString.add(env.get(i).getAsString());\n        }\n        return envAsString;\n    }\n\n    private Map<String, String> getEnvMap() {\n        Map<String,String> envMap = new HashMap<>();\n        envMap.put(\"JAVA_OPTS\", \"-Xmx512m\");\n        envMap.put(\"TEST_SERVICE\", \"LOGGING\");\n        envMap.put(\"TEST_HTTP_ADDR\", \"+${docker.container.consul.ip}\");\n        envMap.put(\"TEST_CONSUL_IP\", \"+${docker.container.consul.ip}:8080\");\n        envMap.put(\"TEST_CONSUL_IP_WITHOUT_DELIM\", \"${docker.container.consul.ip}:8225\");\n        return envMap;\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/ContainerHostConfigTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.json.JSONException;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.UlimitConfig;\nimport io.fabric8.maven.docker.util.JsonFactory;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ContainerHostConfigTest {\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testExtraHostsDoesNotResolve() {\n        ContainerHostConfig hc = new ContainerHostConfig();\n        hc.extraHosts(Arrays.asList(\"database.pvt:ahostnamewhichreallyshouldnot.exist.zz\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testExtraHostsInvalidFormat() {\n        ContainerHostConfig hc = new ContainerHostConfig();\n        hc.extraHosts(Arrays.asList(\"invalidFormat\"));\n    }\n\n    @Test\n    public void testMapExtraHosts() {\n        // assumes 'localhost' resolves, which it should\n        ContainerHostConfig hc = new ContainerHostConfig();\n        hc.extraHosts(Arrays.asList(\"database.pvt:localhost\"));\n\n        assertEquals(\"{\\\"ExtraHosts\\\":[\\\"database.pvt:127.0.0.1\\\"]}\", hc.toJson());\n    }\n\n    @Test\n    public void testUlimits() throws JSONException {\n        Object data[] = {\n            \"{Ulimits: [{Name:bla, Hard:2048, Soft: 1024}]}\", \"bla\", 2048, 1024,\n            \"{Ulimits: [{Name:bla, Soft: 1024}]}\", \"bla\", null, 1024,\n            \"{Ulimits: [{Name:bla, Hard: 2048}]}\", \"bla\", 2048, null,\n            \"{Ulimits: [{Name:bla, Hard: 2048}]}\", \"bla=2048\", null, null,\n            \"{Ulimits: [{Name:bla, Soft: 1024}]}\", \"bla=:1024\", null, null,\n            \"{Ulimits: [{Name:bla, Hard: 2048, Soft: 1024}]}\", \"bla=2048:1024\", null, null,\n            \"{Ulimits: [{Name:bla, Hard: 2048}]}\", \"bla=2048:\", null, null\n        };\n\n        for (int i = 0; i < data.length; i += 4) {\n            ContainerHostConfig hc = new ContainerHostConfig();\n            hc.ulimits(Collections.singletonList(\n                data[1].toString().contains(\"=\") ?\n                    new UlimitConfig((String) data[1]) :\n                    new UlimitConfig((String) data[1], (Integer) data[2], (Integer) data[3])));\n            assertEquals(JsonFactory.newJsonObject((String) data[0]),\n                         hc.toJsonObject());\n        }\n    }\n\n    @Test\n    public void testBinds() throws Exception {\n        String[] data = {\n            \"c:\\\\Users\\\\roland\\\\sample:/sample\", \"/c/Users/roland/sample:/sample\",\n            \"M:\\\\Users\\\\roland\\\\sample:/sample:ro\", \"/m/Users/roland/sample:/sample:ro\"\n        };\n        for (int i = 0; i < data.length; i+=2) {\n            ContainerHostConfig hc = new ContainerHostConfig();\n            JsonObject result = hc.binds(Arrays.asList(data[i])).toJsonObject();\n            JsonObject expected = new JsonObject();\n            JsonArray binds = new JsonArray();\n            binds.add(data[i+1]);\n            expected.add(\"Binds\",binds);\n            assertEquals(expected, result);\n        }\n    }\n\n    @Test\n    public void testTmpfs() throws Exception {\n        String[] data = {\n            \"/var/lib/mysql\", \"{Tmpfs: {'/var/lib/mysql': ''}}\",\n            \"/var/lib/mysql:ro\", \"{Tmpfs: {'/var/lib/mysql': 'ro'}}\"\n        };\n        for (int i = 0; i < data.length; i +=2) {\n            ContainerHostConfig hc = new ContainerHostConfig();\n            JsonObject result = hc.tmpfs(Arrays.asList(data[i])).toJsonObject();\n            JsonObject expected = JsonFactory.newJsonObject(data[i + 1]);\n            assertEquals(expected, result);\n        }\n    }\n    \n    @Test\n    public void testReadonlyRootfs() throws Exception {\n        Pair [] data = {\n            Pair.of(Boolean.TRUE, \"{ReadonlyRootfs: true}\"),\n            Pair.of(Boolean.FALSE, \"{ReadonlyRootfs: false}\")\n        };\n        for (int i = 0; i < data.length; i++) {\n            Pair<Boolean, String> d = data[i];\n            ContainerHostConfig hc = new ContainerHostConfig();\n            JsonObject result = hc.readonlyRootfs(d.getLeft()).toJsonObject();\n            JsonObject expected = JsonFactory.newJsonObject(d.getRight());\n            assertEquals(expected, result);\n        }\n    }\n    \n    @Test\n    public void testAutoRemove() throws Exception {\n        Pair [] data = {\n            Pair.of(Boolean.TRUE, \"{AutoRemove: true}\"),\n            Pair.of(Boolean.FALSE, \"{AutoRemove: false}\")\n        };\n        for (int i = 0; i < data.length; i++) {\n            Pair<Boolean, String> d = data[i];\n            ContainerHostConfig hc = new ContainerHostConfig();\n            JsonObject result = hc.autoRemove(d.getLeft()).toJsonObject();\n            JsonObject expected = JsonFactory.newJsonObject(d.getRight());\n            assertEquals(expected, result);\n        }\n    }\n\n    @Test\n    public void testLogConfig() {\n        ContainerHostConfig hc = new ContainerHostConfig();\n        Map<String,String> opts = new HashMap<>();\n        opts.put(\"gelf-address\",\"udp://10.0.0.1:12201\");\n        opts.put(\"labels\",\"label1,label2\");\n        LogConfiguration logConfig = new LogConfiguration.Builder()\n            .logDriverName(\"gelf\")\n            .logDriverOpts(opts)\n            .build();\n        hc.logConfig(logConfig);\n\n    // TODO: Does order matter?\n    assertEquals(\n        \"{\\\"LogConfig\\\":{\\\"Type\\\":\\\"gelf\\\",\\\"Config\\\":{\\\"gelf-address\\\":\\\"udp://10.0.0.1:12201\\\",\\\"labels\\\":\\\"label1,label2\\\"}}}\",\n        hc.toJson());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/DockerConnectionDetectorTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class DockerConnectionDetectorTest {\n\n    DockerConnectionDetector detector = new DockerConnectionDetector(null);\n\n    @Test\n    public void testGetUrlFromHostConfig() throws MojoExecutionException, IOException {\n        DockerConnectionDetector.ConnectionParameter param = detector.detectConnectionParameter(\"hostconfig\", \"certpath\");\n        assertEquals(\"hostconfig\", param.getUrl());\n        assertEquals(\"certpath\", param.getCertPath());\n    }\n\n    @Test\n    public void testGetUrlFromEnvironment() throws MojoExecutionException, IOException {\n        String dockerHost = System.getenv(\"DOCKER_HOST\");\n        if (dockerHost != null) {\n            assertEquals(dockerHost.replaceFirst(\"^tcp:/\",\"\"), detector.detectConnectionParameter(null, null).getUrl().replaceFirst(\"^https?:/\", \"\"));\n        } else if (System.getProperty(\"os.name\").equalsIgnoreCase(\"Windows 10\")) {\n        \ttry {\n                assertEquals(\"npipe:////./pipe/docker_engine\", detector.detectConnectionParameter(null, null).getUrl());\n            } catch (IllegalArgumentException expectedIfNoUnixSocket) {\n        \t    // expected if no unix socket\n            }\n        } else {\n            try {\n                assertEquals(\"unix:///var/run/docker.sock\", detector.detectConnectionParameter(null, null).getUrl());\n            } catch (IllegalArgumentException expectedIfNoUnixSocket) {\n                // expected if no unix socket\n            }\n        }\n    }\n\n    @Test\n    public void testOrderDefaultDockerHostProviders() {\n        Class[] expectedProviders = new Class[] {\n            DockerConnectionDetector.EnvDockerHostProvider.class,\n            DockerConnectionDetector.UnixSocketDockerHostProvider.class,\n            DockerConnectionDetector.WindowsPipeDockerHostProvider.class\n        };\n\n        int i = 0;\n        for (DockerConnectionDetector.DockerHostProvider provider : detector.dockerHostProviders) {\n            assertEquals(expectedProviders[i++], provider.getClass());\n        }\n    }\n\n    @Test\n    public void testGetCertPathFromEnvironment() throws MojoExecutionException, IOException {\n        try {\n            DockerConnectionDetector.ConnectionParameter param = detector.detectConnectionParameter(null, null);\n            String certPath = System.getenv(\"DOCKER_CERT_PATH\");\n            if (certPath != null) {\n                assertEquals(certPath, param.getCertPath());\n            } else {\n                String maybeUserDocker = param.getCertPath();\n                if (maybeUserDocker != null) {\n                    assertEquals(new File(System.getProperty(\"user.home\"), \".docker\").getAbsolutePath(),\n                                 maybeUserDocker);\n                }\n            }\n        } catch (IllegalArgumentException exp) {\n            // Can happen if there is now docker connection configured in the environment\n        }\n    }\n\n    // any further testing requires a 'docker-machine' on the build host ...\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/KeyStoreUtilTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport java.security.GeneralSecurityException;\nimport java.security.KeyStore;\nimport java.security.PrivateKey;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\nimport static org.junit.Assert.assertNotNull;\n\n/**\n * @author Stas Sukhanov\n * @since 08.03.2017\n */\npublic class KeyStoreUtilTest {\n\n    @Rule\n    public ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void createKeyStore() throws Exception {\n        KeyStore keyStore = KeyStoreUtil.createDockerKeyStore(getFile(\"certpath\"));\n        KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(\"docker\",\n                new KeyStore.PasswordProtection(\"docker\".toCharArray()));\n        assertNotNull(pkEntry);\n        assertNotNull(pkEntry.getCertificate());\n        assertNotNull(keyStore.getCertificate(\"cn=ca-test,o=internet widgits pty ltd,st=some-state,c=cr\"));\n        assertNotNull(keyStore.getCertificate(\"cn=ca-test-2,o=internet widgits pty ltd,st=some-state,c=cr\"));\n    }\n\n    @Test\n    public void loadPrivateKeyDefault() throws Exception {\n        PrivateKey privateKey = KeyStoreUtil.loadPrivateKey(getFile(\"keys/pkcs1.pem\"));\n        assertNotNull(privateKey);\n    }\n\n    @Test\n    public void loadPrivateKeyPKCS8() throws Exception {\n        PrivateKey privateKey = KeyStoreUtil.loadPrivateKey(getFile(\"keys/pkcs8.pem\"));\n        assertNotNull(privateKey);\n    }\n\n    @Test\n    public void loadPrivateKeyECDSA() throws Exception {\n        // ecdsa.pem has been created via `openssl ecparam -name secp521r1 -genkey -param_enc explicit -out ecdsa.pem`\n        PrivateKey privateKey = KeyStoreUtil.loadPrivateKey(getFile(\"keys/ecdsa.pem\"));\n        assertNotNull(privateKey);\n    }\n\n    @Test\n    public void loadInvalidPrivateKey() throws Exception {\n        exception.expect(GeneralSecurityException.class);\n        exception.expectMessage(\"Cannot generate private key\");\n        KeyStoreUtil.loadPrivateKey(getFile(\"keys/invalid.pem\"));\n    }\n\n    private String getFile(String path) {\n        return KeyStoreUtilTest.class.getResource(path).getFile();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/PortMappingPropertyWriteHelperTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.model.Container;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PortMappingPropertyWriteHelperTest {\n\n    private Properties loadedProperties;\n    private Properties projProperties;\n\n    private PortMapping.PropertyWriteHelper propertyWriteHelper;\n\n    @Before\n    public void setup() {\n        projProperties = new Properties();\n    }\n\n    @Test\n    public void testWriteGlobalOnly() throws Exception {\n        String globalFile = createTmpFile();\n        PortMapping mapping = createPortMapping(\"jolokia.port:8080\", \"18181:8181\", \"127.0.0.1:9090:9090\", \"127.0.0.1:other.port:5678\");\n\n        givenAPortMappingWriter(globalFile);\n        whenUpdateDynamicMapping(mapping, \"0.0.0.0\", 8080, 49900);\n        whenUpdateDynamicMapping(mapping, \"127.0.0.1\", 5678, 49901);\n        whenWritePortMappings(null, mapping);\n        thenPropsFileExists(globalFile);\n        thenPropsSizeIs(2);\n        thenPropsContains(\"jolokia.port\", 49900);\n        thenPropsContains(\"other.port\", 49901);\n    }\n\n    @Test\n    public void testWriteImageAndGlobal() throws Exception {\n        String imageFile = createTmpFile();\n        String globalFile = createTmpFile();\n\n        PortMapping mapping1 = createPortMapping(\"jolokia.port1:8080\", \"18181:8181\", \"127.0.0.1:9090:9090\", \"+other.ip1:other.port1:5678\");\n        PortMapping mapping2 = createPortMapping(\"jolokia.port2:8080\", \"18182:8181\", \"127.0.0.2:9090:9090\", \"+other.ip2:other.port2:5678\");\n        PortMapping mapping3 = createPortMapping(\"+other.ip3:other.port3:5678\");\n        \n        givenADockerHostAddress(\"5.6.7.8\");\n        givenAHostIpProperty(\"other.ip1\", \"1.2.3.4\");\n        givenAHostIpProperty(\"other.ip2\", \"1.2.3.4\");\n        givenAPortMappingWriter(globalFile);\n\n        whenUpdateDynamicMapping(mapping1, \"0.0.0.0\", 8080, 49900);\n        whenUpdateDynamicMapping(mapping1, \"1.2.3.4\", 5678, 49901);\n        whenUpdateDynamicMapping(mapping2, \"0.0.0.0\", 8080, 49902);\n        whenUpdateDynamicMapping(mapping2, \"1.2.3.4\", 5678, 49903);\n        whenUpdateDynamicMapping(mapping3, \"0.0.0.0\", 5678, 49904);\n        \n        whenWritePortMappings(imageFile, mapping1);\n        whenWritePortMappings(null, mapping2);\n        whenWritePortMappings(null, mapping3);\n\n        // test all file criteria in order as 'properties' is reset via the exists check\n\n        thenPropsFileExists(globalFile);\n        thenPropsSizeIs(8);\n        thenPropsContains(\"jolokia.port1\", 49900);\n        thenPropsContains(\"other.port1\", 49901);\n        thenPropsContains(\"other.ip1\", \"1.2.3.4\");\n        thenPropsContains(\"jolokia.port2\", 49902);\n        thenPropsContains(\"other.port2\", 49903);\n        thenPropsContains(\"other.ip2\", \"1.2.3.4\");\n        thenPropsContains(\"other.port3\", 49904);\n        thenPropsContains(\"other.ip3\", \"5.6.7.8\");\n        \n        thenPropsFileExists(imageFile);\n        thenPropsSizeIs(3);\n        thenPropsContains(\"jolokia.port1\", 49900);\n        thenPropsContains(\"other.port1\", 49901);\n        thenPropsContains(\"other.ip1\", \"1.2.3.4\");\n    }\n\n    private void givenADockerHostAddress(String host) {\n        projProperties.setProperty(\"docker.host.address\", host);\n    }\n    \n    \n    @Test\n    public void testWriteImageOnly() throws Exception {\n        String imageFile = createTmpFile();\n        PortMapping mapping = createPortMapping(\"jolokia.port:8080\", \"18181:8181\", \"127.0.0.1:9090:9090\", \"127.0.0.1:other.port:5678\");\n\n        givenAPortMappingWriter(null);\n        whenUpdateDynamicMapping(mapping, \"0.0.0.0\", 8080, 49900);\n        whenUpdateDynamicMapping(mapping, \"127.0.0.1\", 5678, 49901);\n        whenWritePortMappings(imageFile, mapping);\n        thenPropsFileExists(imageFile);\n        thenPropsSizeIs(2);\n        thenPropsContains(\"jolokia.port\", 49900);\n        thenPropsContains(\"other.port\", 49901);\n    }\n\n    private PortMapping createPortMapping(Properties properties, String... mappings) throws IllegalArgumentException {\n        return new PortMapping(Arrays.asList(mappings), properties);\n    }\n\n    private PortMapping createPortMapping(String... mappings) throws IllegalArgumentException {\n        return createPortMapping(projProperties, mappings);\n    }\n\n    private String createTmpFile() throws IOException {\n        File propFile = File.createTempFile(\"dmpl-\", \".properties\");\n        propFile.deleteOnExit();\n\n        return propFile.getAbsolutePath();\n    }\n\n    private void givenAHostIpProperty(String property, String hostIp) {\n        projProperties.put(property, hostIp);\n    }\n\n    private void givenAPortMappingWriter(String globalFile) {\n        propertyWriteHelper = new PortMapping.PropertyWriteHelper(globalFile);\n    }\n\n    private void thenPropsContains(String variable, Object port) {\n        assertEquals(String.valueOf(port), loadedProperties.get(variable));\n    }\n\n    private void thenPropsFileExists(String propertyFile) throws Exception {\n        File file = new File(propertyFile);\n        assertTrue(file.exists());\n\n        loadedProperties = new Properties();\n        loadedProperties.load(new FileInputStream(file));\n    }\n\n    private void thenPropsSizeIs(int size) {\n        assertEquals(size, loadedProperties.size());\n    }\n\n    private void whenUpdateDynamicMapping(PortMapping mapping, String ip, int... ports) {\n        Map<String, Container.PortBinding> dynMapping = new HashMap<>();\n        for (int i = 0; i < ports.length; i += 2) {\n            dynMapping.put(ports[i] + \"/tcp\", new Container.PortBinding(ports[i + 1], ip));\n        }\n        mapping.updateProperties(dynMapping);\n    }\n\n    private void whenWritePortMappings(String imageFile, PortMapping portMapping) throws IOException {\n        propertyWriteHelper.add(portMapping, imageFile);\n        propertyWriteHelper.write();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/PortMappingTest.java",
    "content": "package io.fabric8.maven.docker.access;\n\nimport com.google.gson.JsonArray;\n\nimport org.apache.commons.text.StrSubstitutor;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.util.JsonFactory;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author roland\n * @since 04.04.14\n */\npublic class PortMappingTest {\n\n    private PortMapping mapping;\n\n    private Properties properties;\n\n    @Before\n    public void setup() {\n        properties = new Properties();\n    }\n\n    @Test\n    public void testComplexMapping() {\n\n        givenAHostIpProperty(\"other.ip\", \"127.0.0.1\");\n\n        givenAPortMapping(\"jolokia.port:8080\", \"18181:8181\", \"127.0.0.1:9090:9090\", \"+other.ip:${other.port}:5678\");\n        whenUpdateDynamicMapping(443);\n\n        whenUpdateDynamicMapping(8080, 49900, \"0.0.0.0\");\n        whenUpdateDynamicMapping(5678, 49901, \"127.0.0.1\");\n\n        thenMapAndVerifyReplacement(\"http://localhost:49900/\", \"http://localhost:${jolokia.port}/\",\n                \"http://pirx:49900/\", \"http://pirx:${jolokia.port}/\");\n        thenMapAndVerifyReplacement(\"http://localhost:49901/\", \"http://localhost:${other.port}/\",\n                \"http://pirx:49901/\", \"http://pirx:${other.port}/\",\n                \"http://49900/49901\", \"http://${jolokia.port}/${other.port}\");\n\n        thenNeedsPropertyUpdate();\n\n        thenDynamicHostPortsSizeIs(2);\n        thenHostPortVariableEquals(\"jolokia.port\", 49900);\n        thenHostPortVariableEquals(\"other.port\", 49901);\n\n        thenDynamicHostIpsSizeIs(1);\n        thenHostIpVariableEquals(\"other.ip\", \"127.0.0.1\");\n\n        thenContainerPortToHostPortMapSizeIs(4);\n        thenContainerPortToHostPortMapHasOnlyPortSpec(\"8080/tcp\");\n        thenContainerPortToHostPortMapHasOnlyPortSpec(\"5678/tcp\");\n        thenContainerPortToHostPortMapHasPortSpecAndPort(\"8181/tcp\", 18181);\n        thenContainerPortToHostPortMapHasPortSpecAndPort(\"9090/tcp\", 9090);\n\n        thenBindToHostMapSizeIs(2);\n        thenBindToHostMapContains(\"9090/tcp\", \"127.0.0.1\");\n        thenBindToHostMapContains(\"5678/tcp\", \"127.0.0.1\");\n    }\n\n\n    @Test\n    public void testHostIpAsPropertyOnly() {\n        givenADockerHostAddress(\"1.2.3.4\");\n        givenAPortMapping(\"${other.ip}:5677:5677\");\n        whenUpdateDynamicMapping(5677, 5677, \"0.0.0.0\");\n\n        thenContainerPortToHostPortMapSizeIs(1);\n\n        thenDynamicHostPortsSizeIs(0);\n        thenDynamicHostIpsSizeIs(1);\n        thenBindToHostMapSizeIs(0);\n\n        thenHostIpVariableEquals(\"other.ip\", \"1.2.3.4\");\n    }\n\n    @Test\n    public void testHostIpPortAsProperties() {\n        givenADockerHostAddress(\"5.6.7.8\");\n        givenAPortMapping(\"+other.ip:other.port:5677\");\n        whenUpdateDynamicMapping(5677, 49900, \"1.2.3.4\");\n\n        thenContainerPortToHostPortMapHasOnlyPortSpec(\"5677/tcp\");\n\n        thenDynamicHostPortsSizeIs(1);\n        thenDynamicHostIpsSizeIs(1);\n\n        thenHostPortVariableEquals(\"other.port\", 49900);\n        thenHostIpVariableEquals(\"other.ip\", \"1.2.3.4\");\n    }\n\n    @Test\n    public void testHostIpVariableReplacement() {\n        givenAPortMapping(\"jolokia.port:8080\");\n        whenUpdateDynamicMapping(8080, 49900, \"0.0.0.0\");\n\n        thenNeedsPropertyUpdate();\n        thenDynamicHostPortsSizeIs(1);\n        thenHostPortVariableEquals(\"jolokia.port\", 49900);\n    }\n\n    @Test\n    public void testHostnameAsBindHost() {\n        givenAPortMapping(\"localhost:80:80\");\n        thenBindToHostMapContainsValue(\"127.0.0.1\");\n    }\n\n    @Test\n    public void testSingleContainerPort() {\n        givenAPortMapping(\"8080\");\n        thenContainerPortToHostPortMapSizeIs(1);\n        thenContainerPortToHostPortMapHasOnlyPortSpec(\"8080\");\n    }\n\n    @Test\n    public void testHostPortAsPropertyOnly() {\n        givenAPortMapping(\"other.port:5677\");\n        whenUpdateDynamicMapping(5677, 49900, \"0.0.0.0\");\n\n        thenContainerPortToHostPortMapSizeIs(1);\n\n        thenDynamicHostPortsSizeIs(1);\n        thenDynamicHostIpsSizeIs(0);\n\n        thenHostPortVariableEquals(\"other.port\", 49900);\n    }\n\n    @Ignore\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidHostnameWithDynamicPort() {\n        givenAPortMapping(\"does-not-exist.pvt:web.port:80\");\n    }\n\n    @Ignore\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidHostnameWithFixedPort() {\n        givenAPortMapping(\"does-not-exist.pvt:80:80\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidMapping() {\n        givenAPortMapping(\"bla\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidMapping2() {\n        givenAPortMapping(\"jolokia.port:bla\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidProtocol() {\n        givenAPortMapping(\"49000:8080/abc\");\n    }\n\n    @Test\n    public void testIpAsBindHost() {\n        givenAPortMapping(\"127.0.0.1:80:80\");\n        thenBindToHostMapContainsValue(\"127.0.0.1\");\n    }\n\n    @Test\n    public void testUdpAsProtocol() {\n        givenAPortMapping(\"49000:8080/udp\", \"127.0.0.1:49001:8081/udp\");\n        thenContainerPortToHostPortMapSizeIs(2);\n        thenContainerPortToHostPortMapHasPortSpecAndPort(\"8080/udp\", 49000);\n        thenContainerPortToHostPortMapHasPortSpecAndPort(\"8081/udp\", 49001);\n        thenBindToHostMapContains(\"8081/udp\", \"127.0.0.1\");\n        thenBindToHostMapContainsOnlySpec(\"8080/udp\");\n    }\n\n    @Test\n    public void testVariableReplacementWithProps() {\n        givenExistingProperty(\"jolokia.port\", \"50000\");\n        givenAPortMapping(\"jolokia.port:8080\");\n        whenUpdateDynamicMapping(8080, 49900, \"0.0.0.0\");\n        thenMapAndVerifyReplacement(\"http://localhost:50000/\", \"http://localhost:${jolokia.port}/\");\n    }\n\n    @Test\n    public void testVariableReplacementWithSystemPropertyOverwrite() {\n        try {\n            System.setProperty(\"jolokia.port\", \"99999\");\n            givenExistingProperty(\"jolokia.port\", \"50000\");\n            givenAPortMapping(\"jolokia.port:8080\");\n            thenMapAndVerifyReplacement(\"http://localhost:99999/\", \"http://localhost:${jolokia.port}/\");\n        } finally {\n            System.getProperties().remove(\"jolokia.port\");\n        }\n    }\n\n    @Test\n    public void testToJson() {\n        givenAPortMapping(\"49000:8080/udp\", \"127.0.0.1:49001:8081\");\n        thenAssertJsonEquals(\"[{ hostPort: 49000, containerPort: 8080, protocol: udp },\" +\n                             \" { hostIP: '127.0.0.1', hostPort: 49001, containerPort: 8081, protocol: tcp}]\");\n    }\n\n    private void thenAssertJsonEquals(String json) {\n        JsonArray jsonArray = JsonFactory.newJsonArray(json);\n        assertEquals(jsonArray, mapping.toJson().getAsJsonArray());\n    }\n\n    private void givenADockerHostAddress(String host) {\n        properties.setProperty(\"docker.host.address\", host);\n    }\n\n    private void givenAHostIpProperty(String property, String hostIp) {\n        properties.put(property, hostIp);\n    }\n\n    private void givenAPortMapping(String... mappings) {\n        mapping = new PortMapping(Arrays.asList(mappings), properties);\n    }\n\n    private void givenExistingProperty(String... p) {\n        for (int i = 0; i < p.length; i += 2) {\n            properties.setProperty(p[i], p[i + 1]);\n        }\n    }\n\n    private void thenBindToHostMapContains(String portSpec, String hostIp) {\n        assertEquals(hostIp, mapping.getBindToHostMap().get(portSpec));\n    }\n\n    private void thenBindToHostMapContainsOnlySpec(String portSpec) {\n        assertNull(mapping.getBindToHostMap().get(portSpec));\n    }\n\n    private void thenBindToHostMapContainsValue(String host) {\n        assertTrue(mapping.getBindToHostMap().values().contains(host));\n    }\n\n    private void thenBindToHostMapSizeIs(int size) {\n        assertEquals(size, mapping.getBindToHostMap().size());\n    }\n\n    private void thenContainerPortToHostPortMapHasOnlyPortSpec(String portSpec) {\n        assertNull(mapping.getContainerPortToHostPortMap().get(portSpec));\n    }\n\n    private void thenContainerPortToHostPortMapHasPortSpecAndPort(String portSpec, Integer port) {\n        assertTrue(mapping.getContainerPorts().contains(portSpec));\n        assertEquals(port, mapping.getPortsMap().get(portSpec));\n    }\n\n    private void thenContainerPortToHostPortMapSizeIs(int size) {\n        assertEquals(size, mapping.getContainerPortToHostPortMap().size());\n    }\n\n    private void thenDynamicHostIpsSizeIs(int size) {\n        assertEquals(size, mapping.getHostIpVariableMap().size());\n    }\n\n    private void thenDynamicHostPortsSizeIs(int size) {\n        assertEquals(size, mapping.getHostPortVariableMap().size());\n    }\n\n    private void thenNeedsPropertyUpdate() {\n        assertTrue(mapping.needsPropertiesUpdate());\n    }\n\n    private void thenHostIpVariableEquals(String key, String ip) {\n        assertEquals(ip, mapping.getHostIpVariableMap().get(key));\n    }\n\n    private void thenHostPortVariableEquals(String key, Integer port) {\n        assertEquals(port, mapping.getHostPortVariableMap().get(key));\n    }\n\n    private void thenMapAndVerifyReplacement(String... args) {\n        for (int i = 0; i < args.length; i += 2) {\n            assertEquals(args[i], StrSubstitutor.replace(args[i + 1], mapping.getHostPortVariableMap()));\n        }\n    }\n\n    private void whenUpdateDynamicMapping(int cPort) {\n        Map<String, Container.PortBinding> dynMapping = new HashMap<>();\n        dynMapping.put(cPort + \"/tcp\", null);\n\n        mapping.updateProperties(dynMapping);\n    }\n\n    private void whenUpdateDynamicMapping(int cPort, int hPort, String hIp) {\n        Map<String, Container.PortBinding> dynMapping = new HashMap<>();\n        dynMapping.put(cPort + \"/tcp\", new Container.PortBinding(hPort, hIp));\n\n        mapping.updateProperties(dynMapping);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/ecr/AwsSigner4RequestTest.java",
    "content": "package io.fabric8.maven.docker.access.ecr;\n\nimport io.fabric8.maven.docker.access.util.RequestUtil;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Date;\n\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.entity.StringEntity;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\n\n/**\n * Test aws request signing\n *\n * @author chas\n * @since 2016-12-21\n */\npublic class AwsSigner4RequestTest {\n\n    private static final String TASK1 = \"POST\\n\"\n            + \"/\\n\"\n            + \"\\n\"\n            + \"content-type:application/x-amz-json-1.1\\n\"\n            + \"host:ecr.us-east-1.amazonaws.com\\n\"\n            + \"x-amz-target:AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken\\n\"\n            + \"\\n\"\n            + \"content-type;host;x-amz-target\\n\"\n            + \"1531fbe1f1c4e3437223a1583a6d21d404acf9d910262f629d73d6bf55a545bd\";\n\n    private static final String TASK2 = \"AWS4-HMAC-SHA256\\n\"\n            + \"20150830T123600Z\\n\"\n            + \"20150830/us-east-1/service/aws4_request\\n\"\n            + \"7c945a283983ca83301de46103427b7035344a4b0cfddd886e3d94b4bed7df5e\";\n\n    private static final String TASK3 = \"89cd649587898a1913ced5c519425905b192c4662212d37e689e6c20e53edbbd\";\n\n    private static final String TASK4 = \"AWS4-HMAC-SHA256 \"\n            + \"Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, \"\n            + \"SignedHeaders=content-type;host;x-amz-target, \"\n            + \"Signature=89cd649587898a1913ced5c519425905b192c4662212d37e689e6c20e53edbbd\";\n\n    @Test\n    public void testSign() throws Exception {\n        HttpPost request = new HttpPost(\"https://ecr.us-east-1.amazonaws.com/\");\n        request.setHeader(\"host\", \"ecr.us-east-1.amazonaws.com\");\n        request.setHeader(\"Content-Type\", \"application/x-amz-json-1.1\");\n        request.setHeader(\"X-Amz-Target\", \"AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken\");\n        request.setEntity(new StringEntity(\"{\\\"registryIds\\\":[\\\"012345678901\\\"]}\", StandardCharsets.UTF_8));\n\n        AwsSigner4 signer = new AwsSigner4(\"us-east-1\", \"ecr\");\n\n        Date signingTime = AwsSigner4Request.TIME_FORMAT.parse(\"20150830T123600Z\");\n        AwsSigner4Request sr = new AwsSigner4Request(\"us-east-1\", \"service\", request, signingTime);\n        AuthConfig credentials = new AuthConfig(\"AKIDEXAMPLE\", \"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY\", null, null);\n \n        Assert.assertEquals(TASK1, signer.task1(sr));\n\n        Assert.assertEquals(TASK2, signer.task2(sr));\n\n        StringBuilder dst = new StringBuilder();\n        AwsSigner4.hexEncode(dst, signer.task3(sr, credentials));\n        Assert.assertEquals(TASK3, dst.toString());\n\n        Assert.assertEquals(TASK4, signer.task4(sr, credentials));\n    }\n\n    @Test\n    public void includesAuthTokenAsAwsSecurityToken() {\n        HttpUriRequest request = RequestUtil.newGet(\"https://someService.us-east-1.amazonaws.com/\");\n        request.setHeader(\"host\", request.getURI().getHost());\n        String awsSecurityToken = \"securityToken\";\n        AuthConfig credentials = new AuthConfig(\"awsAccessKeyId\", \"awsSecretAccessKey\", null, awsSecurityToken);\n\n        AwsSigner4 signer = new AwsSigner4(\"us-east-1\", \"someService\");\n        signer.sign(request, credentials, new Date());\n\n        Assert.assertEquals(request.getFirstHeader(\"X-Amz-Security-Token\").getValue(), awsSecurityToken);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/ecr/EcrExtendedAuthTest.java",
    "content": "package io.fabric8.maven.docker.access.ecr;\n\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport mockit.Verifications;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.Date;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.maven.plugin.MojoExecutionException;\n\n/**\n * Test exchange of local stored credentials for temporary ecr credentials\n *\n * @author chas\n * @since 2016-12-21\n */\npublic class EcrExtendedAuthTest {\n\n    @Mocked\n    private Logger logger;\n\n    @Test\n    public void testIsNotAws() {\n        assertFalse(new EcrExtendedAuth(logger, \"jolokia\").isAwsRegistry());\n    }\n\n    @Test\n    public void testIsAws() {\n        assertTrue(new EcrExtendedAuth(logger, \"123456789012.dkr.ecr.eu-west-1.amazonaws.com\").isAwsRegistry());\n    }\n\n    @Test\n    public void testHeaders() throws ParseException {\n        EcrExtendedAuth eea = new EcrExtendedAuth(logger, \"123456789012.dkr.ecr.eu-west-1.amazonaws.com\");\n        AuthConfig localCredentials = new AuthConfig(\"username\", \"password\", null, null);\n        Date signingTime = AwsSigner4Request.TIME_FORMAT.parse(\"20161217T211058Z\");\n        HttpPost request = eea.createSignedRequest(localCredentials, signingTime);\n        assertEquals(\"api.ecr.eu-west-1.amazonaws.com\", request.getFirstHeader(\"host\").getValue());\n        assertEquals(\"20161217T211058Z\", request.getFirstHeader(\"X-Amz-Date\").getValue());\n        assertEquals(\"AWS4-HMAC-SHA256 Credential=username/20161217/eu-west-1/ecr/aws4_request, SignedHeaders=content-type;host;x-amz-target, Signature=2ae11d499499cc951900aac0fbec96009382ba4f735bd14baa375c3e51d50aa9\", request.getFirstHeader(\"Authorization\").getValue());\n    }\n\n    @Test\n    public void testClientClosedAndCredentialsDecoded(@Mocked final CloseableHttpClient closeableHttpClient,\n            @Mocked final CloseableHttpResponse closeableHttpResponse,\n            @Mocked final StatusLine statusLine)\n            throws IOException, MojoExecutionException {\n\n        final HttpEntity entity = new StringEntity(\"{\\\"authorizationData\\\": [{\"\n          +\"\\\"authorizationToken\\\": \\\"QVdTOnBhc3N3b3Jk\\\",\"\n          +\"\\\"expiresAt\\\": 1448878779.809,\"\n          +\"\\\"proxyEndpoint\\\": \\\"https://012345678910.dkr.ecr.eu-west-1.amazonaws.com\\\"}]}\");\n\n        new Expectations() {{\n            statusLine.getStatusCode(); result = 200;\n            closeableHttpResponse.getEntity(); result = entity;\n        }};\n        EcrExtendedAuth eea = new EcrExtendedAuth(logger, \"123456789012.dkr.ecr.eu-west-1.amazonaws.com\") {\n            CloseableHttpClient createClient() {\n                return closeableHttpClient;\n            }\n        };\n\n        AuthConfig localCredentials = new AuthConfig(\"username\", \"password\", null, null);\n        AuthConfig awsCredentials = eea.extendedAuth(localCredentials);\n        assertEquals(\"AWS\", awsCredentials.getUsername());\n        assertEquals(\"password\", awsCredentials.getPassword());\n\n        new Verifications() {{\n             closeableHttpClient.close();\n         }};\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClientTest.java",
    "content": "package io.fabric8.maven.docker.access.hc;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.hc.util.ClientBuilder;\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.model.ContainersListElement;\nimport io.fabric8.maven.docker.model.Image;\nimport io.fabric8.maven.docker.model.ImageDetails;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\n\nimport mockit.Verifications;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.http.client.HttpResponseException;\nimport org.apache.http.client.ResponseHandler;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static java.net.HttpURLConnection.HTTP_CREATED;\nimport static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;\nimport static java.net.HttpURLConnection.HTTP_NOT_FOUND;\nimport static java.net.HttpURLConnection.HTTP_OK;\nimport static org.junit.Assert.*;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\npublic class DockerAccessWithHcClientTest {\n\n    private static final String BASE_URL = \"tcp://1.2.3.4:2375\";\n\n    private AuthConfig authConfig;\n\n    private DockerAccessWithHcClient client;\n\n    private String imageName;\n\n    @Mocked\n    private ApacheHttpClientDelegate mockDelegate;\n\n    @Mocked\n    private Logger mockLogger;\n\n    private int pushRetries;\n\n    private String registry;\n\n    private Exception thrownException;\n    private String archiveFile;\n    private String filename;\n    private ArchiveCompression compression;\n    private List<Container> containers;\n    private List<Image> images;\n\n    @Before\n    public void setup() throws IOException {\n        client = new DockerAccessWithHcClient(BASE_URL, null, 1, mockLogger) {\n            @Override\n            ApacheHttpClientDelegate createHttpClient(ClientBuilder builder) throws IOException {\n                return mockDelegate;\n            }\n        };\n    }\n\n    @Test\n    public void testPushImage_replacementOfExistingOfTheSameTag() throws Exception {\n        String image = \"test-image\";\n        String tag = \"test-tag\";\n        String taggedImageName = String.format(\"%s:%s\", image, tag);\n\n        givenAnImageName(taggedImageName);\n        givenRegistry(\"test-registry\");\n        givenThatGetWillSucceedWithOk();\n\n        whenPushImage();\n\n        thenAlreadyHasImageMessageIsLogged();\n        thenImageWasTagged(image, tag);\n        thenPushSucceeded(image, tag);\n    }\n\n    @Test\n    public void testPushImage_imageOfTheSameTagDoesNotExist() throws Exception {\n        String image = \"test-image\";\n        String tag = \"test-tag\";\n        String taggedImageName = String.format(\"%s:%s\", image, tag);\n\n        givenAnImageName(taggedImageName);\n        givenRegistry(\"test-registry\");\n        givenThatGetWillSucceedWithNotFound();\n        givenThatDeleteWillSucceed();\n\n        whenPushImage();\n\n        thenImageWasTagged(image, tag);\n        thenPushSucceeded(image, tag);\n    }\n\n    @Test\n    public void testPushFailes_noRetry() throws Exception {\n        givenAnImageName(\"test\");\n        givenThePushWillFail(0);\n        whenPushImage();\n        thenImageWasNotPushed();\n    }\n\n    @Test\n    public void testRetryPush() throws Exception {\n        givenAnImageName(\"test\");\n        givenANumberOfRetries(1);\n        givenThePushWillFailAndEventuallySucceed(1);\n        whenPushImage();\n        thenImageWasPushed();\n    }\n\n    @Test\n    public void testRetriesExceeded() throws Exception {\n        givenAnImageName(\"test\");\n        givenANumberOfRetries(1);\n        givenThePushWillFail(1);\n        whenPushImage();\n        thenImageWasNotPushed();\n    }\n\n    @Test\n    public void testListContainers() throws IOException {\n        String containerId1 = UUID.randomUUID().toString().replace(\"-\", \"\");\n        String containerId2 = UUID.randomUUID().toString().replace(\"-\", \"\");\n\n        givenContainerIdImagePairs(Pair.of(containerId1, \"image:tag\"), Pair.of(containerId2, \"image:tag\"));\n        whenListContainers();\n        thenNoException();\n        thenContainerIdImagePairsMatch(Pair.of(containerId1.substring(0, 12), \"image:tag\"), Pair.of(containerId2.substring(0, 12), \"image:tag\"));\n    }\n\n    @Test\n    public void testListContainersFail() throws IOException {\n        givenTheGetWithoutResponseHandlerWillFail();\n        whenListContainers();\n        thenContainerListNotReturned();\n    }\n\n    @Test\n    public void testListImages() throws IOException {\n        String imageId1 = UUID.randomUUID().toString().replace(\"-\", \"\");\n        String imageId2 = UUID.randomUUID().toString().replace(\"-\", \"\");\n\n        givenImageIdRepoTagPairs(Pair.of(imageId1, \"image:tag1\"), Pair.of(imageId2, \"image:tag2\"));\n        whenListImages();\n        thenNoException();\n        thenImageIdRepoTagPairsMatch(Pair.of(imageId1, \"image:tag1\"), Pair.of(imageId2, \"image:tag2\"));\n    }\n\n    @Test\n    public void testListImagesFail() throws IOException {\n        givenTheGetWithoutResponseHandlerWillFail();\n        whenListImages();\n        thenImageListNotReturned();\n    }\n\n    @Test\n    public void testLoadImage() {\n        givenAnImageName(\"test\");\n        givenArchiveFile(\"test.tar\");\n        whenLoadImage();\n        thenNoException();\n    }\n    @Test\n    public void testLoadImageFail() throws IOException {\n        givenAnImageName(\"test\");\n        givenArchiveFile(\"test.tar\");\n        givenThePostWillFail();\n        whenLoadImage();\n        thenImageWasNotLoaded();\n    }\n\n    @Test\n    public void testSaveImage() throws IOException {\n        givenAnImageName(\"test\");\n        givenFilename(\"test.tar\");\n        givenCompression(ArchiveCompression.none);\n        whenSaveImage();\n        thenNoException();\n    }\n\n    @Test\n    public void testSaveImageFail() throws IOException {\n        givenAnImageName(\"test\");\n        givenFilename(\"test.tar\");\n        givenCompression(ArchiveCompression.none);\n        givenTheGetWillFail();\n        whenSaveImage();\n        thenImageWasNotSaved();\n    }\n\n    private void givenAnImageName(String imageName) {\n        this.imageName = imageName;\n    }\n\n    private void givenRegistry(String registry) {\n        this.registry = registry;\n    }\n\n    private void givenANumberOfRetries(int retries) {\n        this.pushRetries = retries;\n    }\n\n    private void givenArchiveFile(String archiveFile) {\n        this.archiveFile = archiveFile;\n    }\n\n    private void givenFilename(String filename) {\n    \tthis.filename = filename;\n    }\n\n    private void givenCompression(ArchiveCompression compression) {\n    \tthis.compression = compression;\n    }\n\n    private void givenContainerIdImagePairs(Pair<String, String>... idNamePairs) throws IOException {\n        final JsonArray array = new JsonArray();\n        for(Pair<String, String> idNamePair : idNamePairs) {\n            JsonObject idNameObject = new JsonObject();\n            idNameObject.addProperty(ContainersListElement.ID, idNamePair.getLeft());\n            idNameObject.addProperty(ContainersListElement.IMAGE, idNamePair.getRight());\n            array.add(idNameObject);\n        }\n\n        new Expectations() {{\n            mockDelegate.get(anyString, 200);\n            result = array.toString();\n        }};\n    }\n\n    private void givenImageIdRepoTagPairs(Pair<String, String>... idRepoTagPairs) throws IOException {\n        final JsonArray array = new JsonArray();\n        for(Pair<String, String> idNamePair : idRepoTagPairs) {\n            JsonObject imageObject = new JsonObject();\n            imageObject.addProperty(ImageDetails.ID, idNamePair.getLeft());\n            JsonArray repoTags = new JsonArray();\n            repoTags.add(idNamePair.getRight());\n            imageObject.add(ImageDetails.REPO_TAGS, repoTags);\n            array.add(imageObject);\n        }\n\n        new Expectations() {{\n            mockDelegate.get(anyString, 200);\n            result = array.toString();\n        }};\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private void givenThePushWillFailAndEventuallySucceed(final int retries) throws IOException {\n        new Expectations() {{\n            int fail = retries;\n            mockDelegate.post(anyString, null, (Map<String, String>) any, (ResponseHandler) any,  200);\n            minTimes = fail; maxTimes = fail;\n            result = new HttpResponseException(HTTP_INTERNAL_ERROR, \"error\");\n            mockDelegate.post(anyString, null, (Map<String, String>) any, (ResponseHandler) any, 200);\n            minTimes = 1; maxTimes = 1;\n        }};\n    }\n\n    private void givenThePushWillFail(final int retries) throws IOException {\n        new Expectations() {{\n            int fail = retries + 1;\n            mockDelegate.post(anyString, null, (Map<String, String>) any, (ResponseHandler) any,  200);\n            minTimes = fail; maxTimes = fail;\n            result = new HttpResponseException(HTTP_INTERNAL_ERROR, \"error\");\n        }};\n    }\n\n    private void givenThePostWillFail() throws IOException {\n        new Expectations() {{\n            mockDelegate.post(anyString, any, (ResponseHandler) any, 200);\n            result = new HttpResponseException(HTTP_INTERNAL_ERROR, \"error\");\n        }};\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void givenThatGetWillSucceedWithOk() throws IOException {\n        new Expectations() {{\n            mockDelegate.get(anyString, (ResponseHandler<Integer>) any, HTTP_OK, HTTP_NOT_FOUND);\n            result = HTTP_OK;\n        }};\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void givenThatGetWillSucceedWithNotFound() throws IOException {\n        new Expectations() {{\n            mockDelegate.get(anyString, (ResponseHandler<Integer>) any, HTTP_OK, HTTP_NOT_FOUND);\n            result = HTTP_NOT_FOUND;\n        }};\n    }\n\n    private void givenTheGetWillFail() throws IOException {\n        new Expectations() {{\n            mockDelegate.get(anyString, (ResponseHandler) any, 200);\n            result = new HttpResponseException(HTTP_INTERNAL_ERROR, \"error\");\n        }};\n    }\n\n    private void givenTheGetWithoutResponseHandlerWillFail() throws IOException {\n        new Expectations() {{\n            mockDelegate.get(anyString, 200);\n            result = new HttpResponseException(HTTP_INTERNAL_ERROR, \"error\");\n        }};\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void givenThatDeleteWillSucceed() throws IOException {\n        new Expectations() {{\n            mockDelegate.delete(anyString, (ResponseHandler<ApacheHttpClientDelegate.HttpBodyAndStatus>) any, HTTP_OK,\n                HTTP_NOT_FOUND);\n            result = new ApacheHttpClientDelegate.HttpBodyAndStatus(HTTP_OK, anyString);\n        }};\n    }\n\n    private void thenImageWasNotPushed() {\n        assertNotNull(thrownException);\n    }\n\n    private void thenImageWasPushed() {\n       assertNull(thrownException);\n    }\n\n    private void thenPushSucceeded(String imageNameWithoutTag, String tag) throws IOException {\n        new Verifications() {{\n            String url;\n            mockDelegate.post(url = withCapture(), null, (Map<String, String>) any, (ResponseHandler<Object>) any,\n                HTTP_OK);\n\n            String expectedUrl = String.format(\"%s/vnull/images/%s%%2F%s/push?force=1&tag=%s\", BASE_URL, registry,\n                imageNameWithoutTag, tag);\n            assertEquals(expectedUrl, url);\n        }};\n    }\n\n    private void thenAlreadyHasImageMessageIsLogged() {\n        new Verifications() {{\n            mockLogger.warn(\"Target image '%s' already exists. Tagging of '%s' will replace existing image\",\n                getImageNameWithRegistry(), imageName);\n        }};\n    }\n\n    private void thenImageWasTagged(String imageNameWithoutTag, String tag) throws IOException {\n        new Verifications() {{\n            String url;\n            mockDelegate.post(url = withCapture(), HTTP_CREATED);\n\n            String expectedUrl = String.format(\"%s/vnull/images/%s%%3A%s/tag?force=0&repo=%s%%2F%s&tag=%s\", BASE_URL,\n                imageNameWithoutTag, tag, registry, imageNameWithoutTag, tag);\n            assertEquals(expectedUrl, url);\n        }};\n    }\n\n    private void whenListContainers() {\n        containers = null;\n        try {\n            containers = client.listContainers(true);\n        } catch (Exception e) {\n            thrownException = e;\n        }\n    }\n\n    private void whenListImages() {\n        images = null;\n        try {\n            images = client.listImages(false);\n        } catch (Exception e) {\n            thrownException = e;\n        }\n    }\n\n    private void whenPushImage() {\n        try {\n            client.pushImage(imageName, authConfig, registry, pushRetries);\n        } catch (Exception e) {\n            thrownException = e;\n        }\n    }\n    private void whenLoadImage() {\n        try {\n            client.loadImage(imageName, new File(archiveFile));\n        } catch (Exception e) {\n            thrownException = e;\n        }\n    }\n\n    private void whenSaveImage() {\n        try {\n            client.saveImage(imageName, filename, compression);\n        } catch (Exception e) {\n            thrownException = e;\n        }\n    }\n\n    private void thenNoException() {\n        assertNull(thrownException);\n    }\n\n    private void thenImageWasNotLoaded() {\n        assertNotNull(thrownException);\n    }\n\n    private void thenImageWasNotSaved() {\n        assertNotNull(thrownException);\n    }\n\n    private void thenContainerListNotReturned() {\n        assertNotNull(thrownException);\n    }\n\n    private void thenImageListNotReturned() {\n        assertNotNull(thrownException);\n    }\n\n    private void thenContainerIdImagePairsMatch(Pair<String, String>... idNamePairs) {\n        assertEquals(idNamePairs.length, this.containers.size());\n        for (int i = 0; i < idNamePairs.length; ++i) {\n            assertNotNull(this.containers.get(i));\n            assertEquals(idNamePairs[i].getLeft(), this.containers.get(i).getId());\n            assertEquals(idNamePairs[i].getRight(), this.containers.get(i).getImage());\n        }\n    }\n\n    private void thenImageIdRepoTagPairsMatch(Pair<String, String>... idRepoTagPairs) {\n        assertEquals(idRepoTagPairs.length, this.images.size());\n        for (int i = 0; i < idRepoTagPairs.length; ++i) {\n            assertNotNull(this.images.get(i));\n            assertEquals(idRepoTagPairs[i].getLeft(), this.images.get(i).getId());\n            assertEquals(Collections.singletonList(idRepoTagPairs[i].getRight()), this.images.get(i).getRepoTags());\n        }\n    }\n\n    private String getImageNameWithRegistry() {\n        return registry + \"/\" + imageName;\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/access/log/LogRequestorTest.java",
    "content": "package io.fabric8.maven.docker.access.log;\n\nimport com.google.common.base.Charsets;\nimport io.fabric8.maven.docker.access.UrlBuilder;\nimport io.fabric8.maven.docker.access.util.RequestUtil;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport mockit.Verifications;\nimport org.apache.commons.text.RandomStringGenerator;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.junit.Test;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.CharBuffer;\nimport java.nio.charset.CharsetEncoder;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.regex.Matcher;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport java.time.ZonedDateTime;\n\npublic class LogRequestorTest {\n    private static final String containerId = new RandomStringGenerator.Builder().build().generate(64);\n\n    @Mocked(stubOutClassInitialization = true)\n    final RequestUtil unused = null;\n\n    @Mocked\n    CloseableHttpResponse httpResponse;\n\n    @Mocked\n    UrlBuilder urlBuilder;\n\n    @Mocked\n    StatusLine statusLine;\n\n    @Mocked\n    HttpEntity httpEntity;\n\n    @Mocked\n    HttpUriRequest httpUriRequest;\n\n    @Mocked\n    LogCallback callback;\n\n    @Mocked\n    CloseableHttpClient client;\n\n    @Test\n    public void testEmptyMessage() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final ByteBuffer headerBuffer = ByteBuffer.allocate(8);\n        headerBuffer.put((byte) type.type);\n        headerBuffer.putInt(4, 0);\n        final InputStream inputStream = new ByteArrayInputStream(headerBuffer.array());\n\n        setupMocks(inputStream);\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            callback.log(type.type, (ZonedDateTime) any, anyString);\n            times = 0;\n        }};\n    }\n\n    @Test\n    public void testStdoutMessage() throws Exception {\n        final Streams type = Streams.STDOUT;\n        RandomStringGenerator randomGenerator = new RandomStringGenerator.Builder().build();\n        final String message0 = randomGenerator.generate(257);\n        final String message1 = \"test test\";\n        final String message2 = randomGenerator.generate(666);\n\n        final ByteBuffer body = responseContent(type, message0, message1, message2);\n        final InputStream inputStream = new ByteArrayInputStream(body.array());\n\n        setupMocks(inputStream);\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            callback.log(type.type, (ZonedDateTime) any, message0);\n            callback.log(type.type, (ZonedDateTime) any, message1);\n            callback.log(type.type, (ZonedDateTime) any, message2);\n        }};\n    }\n\n    @Test\n    public void testMessageWithLeadingWhitespace() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final String message0 = \" I have a leading space\";\n        final String message1 = \"\\tI have a leading tab\";\n\n        final ByteBuffer body = responseContent(type, message0, message1);\n        final InputStream inputStream = new ByteArrayInputStream(body.array());\n\n        setupMocks(inputStream);\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            callback.log(type.type, (ZonedDateTime) any, message0);\n            callback.log(type.type, (ZonedDateTime) any, message1);\n        }};\n    }\n\n\n    @Test\n    public void testAllStreams() throws Exception {\n        final Random rand = new Random();\n        final int upperBound = 1024;\n\n        RandomStringGenerator randomGenerator = new RandomStringGenerator.Builder().build();\n\n        final Streams type0 = Streams.STDIN;\n        final String msg0 = randomGenerator.generate(rand.nextInt(upperBound));\n        final ByteBuffer buf0 = messageToBuffer(type0, msg0);\n\n        final Streams type1 = Streams.STDOUT;\n        final String msg1 = randomGenerator.generate(rand.nextInt(upperBound));\n        final ByteBuffer buf1 = messageToBuffer(type1, msg1);\n\n        final Streams type2 = Streams.STDERR;\n        final String msg2 = randomGenerator.generate(rand.nextInt(upperBound));\n        final ByteBuffer buf2 = messageToBuffer(type2, msg2);\n\n        final ByteBuffer body = combineBuffers(buf0, buf1, buf2);\n        final InputStream inputStream = new ByteArrayInputStream(body.array());\n\n        setupMocks(inputStream);\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            callback.log(type0.type, (ZonedDateTime) any, msg0);\n            callback.log(type1.type, (ZonedDateTime) any, msg1);\n            callback.log(type2.type, (ZonedDateTime) any, msg2);\n        }};\n    }\n\n    @Test\n    public void testGarbageMessage() throws Exception {\n        final Streams type = Streams.STDERR;\n        final ByteBuffer buf0 = messageToBuffer(type, \"This is a test message\");\n        final ByteBuffer buf1 = messageToBuffer(type, \"This is another test message!\");\n\n        // Add one extra byte to buf0.\n        int l0 = buf0.getInt(4);\n        buf0.putInt(4, l0 + 1);\n\n        // Set incorrect length in buf1.\n        int l1 = buf1.getInt(4);\n        buf1.putInt(4, l1 + 512);\n\n        final ByteBuffer messages = ByteBuffer.allocate(buf0.limit() + buf1.limit());\n        buf0.position(0);\n        buf1.position(0);\n        messages.put(buf0);\n        messages.put(buf1);\n\n        final InputStream inputStream = new ByteArrayInputStream(messages.array());\n\n        setupMocks(inputStream);\n\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            // Should have called log() one time (for the first message). The message itself would\n            // have been incorrect, since we gave it the wrong buffer length. The second message\n            // fails to parse as the buffer runs out.\n            callback.log(type.type, (ZonedDateTime) any, anyString);\n            times = 1;\n        }};\n    }\n\n    @Test\n    public void testMessageTooShort() throws Exception {\n        final Streams type = Streams.STDIN;\n        final ByteBuffer buf = messageToBuffer(type, \"A man, a plan, a canal, Panama!\");\n\n        // Set length too long so reading buffer overflows.\n        int l = buf.getInt(4);\n        buf.putInt(4, l + 1);\n\n        final InputStream inputStream = new ByteArrayInputStream(buf.array());\n        setupMocks(inputStream);\n\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        new Verifications() {{\n            // No calls to .log() should be made, as message parsing fails.\n            callback.log(type.type, (ZonedDateTime) any, anyString);\n            times = 0;\n        }};\n    }\n\n    @Test\n    public void testMessageWithExtraBytes() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final String message = \"A man, a plan, a canal, Panama!\";\n        final ByteBuffer buf = messageToBuffer(type, message);\n\n        // Set length too short so there is extra buffer left after reading.\n        int l = buf.getInt(4);\n        buf.putInt(4, l - 1);\n\n        final InputStream inputStream = new ByteArrayInputStream(buf.array());\n        setupMocks(inputStream);\n\n        new LogRequestor(client, urlBuilder, containerId, callback).fetchLogs();\n\n        assertThat(\"Entire InputStream read.\", ((ByteArrayInputStream) inputStream).available(), equalTo(0));\n        new Verifications() {{\n            // .log() is only called once. The one byte that is left off is lost and never recorded.\n            callback.log(type.type, (ZonedDateTime) any, message.substring(0, message.length() - 1));\n            times = 1;\n        }};\n    }\n\n    @Test\n    public void checkMutlilinePattern() {\n        String line = \"2016-07-15T20:34:06.024029849Z remote: Compressing objects:   4% (1/23)           \\n\" +\n                      \"remote: Compressing objects:   8% (2/23)           \\n\";\n        String matched = \"remote: Compressing objects:   4% (1/23)           \\n\" +\n                      \"remote: Compressing objects:   8% (2/23)\";\n        Matcher matcher = LogRequestor.LOG_LINE.matcher(line);;\n        assertTrue(matcher.matches());\n        assertEquals(matched, matcher.group(\"entry\"));\n    }\n\n    @Test\n    public void runCallsOpenAndCloseOnHandler() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final String message = \"\";\n        final ByteBuffer buf = messageToBuffer(type, message);\n        final InputStream inputStream = new ByteArrayInputStream(buf.array());\n        setupMocks(inputStream);\n\n        new Expectations() {{\n            callback.open();\n            times = 1;\n\n            callback.close();\n            times = 1;\n\n        }};\n\n        new LogRequestor(client, urlBuilder, containerId, callback).run();\n    }\n\n    @Test\n    public void runCanConsumeEmptyStream() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final String message = \"\";\n        final ByteBuffer buf = messageToBuffer(type, message);\n        final InputStream inputStream = new ByteArrayInputStream(buf.array());\n        setupMocks(inputStream);\n\n        new LogRequestor(client, urlBuilder, containerId, callback).run();\n    }\n\n    @Test\n    public void runCanConsumeSingleLineStream() throws Exception {\n        final Streams type = Streams.STDOUT;\n        final String message = \"Hello, world!\";\n        final ByteBuffer buf = messageToBuffer(type, message);\n        final InputStream inputStream = new ByteArrayInputStream(buf.array());\n        setupMocks(inputStream);\n\n        new Expectations() {{\n            callback.log(type.type, (ZonedDateTime) any, anyString);\n            times = 1;\n        }};\n\n        new LogRequestor(client, urlBuilder, containerId, callback).run();\n    }\n\n    @Test\n    public void runCanHandleIOException() throws Exception {\n        final IOExcpetionStream stream = new IOExcpetionStream();\n        setupMocks(stream);\n\n        new Expectations() {{\n            callback.error(anyString);\n            times = 1;\n        }};\n\n        new LogRequestor(client, urlBuilder, containerId, callback).run();\n    }\n\n    private void setupMocks(final InputStream inputStream) throws Exception {\n        new Expectations() {{\n            RequestUtil.newGet(anyString);\n\n            client.execute((HttpUriRequest) any);\n\n            httpResponse.getStatusLine();\n\n            statusLine.getStatusCode();\n            result = 200;\n\n            httpResponse.getEntity();\n\n            httpEntity.getContent();\n            result = inputStream;\n        }};\n    }\n\n    private enum Streams {\n        STDIN(0),\n        STDOUT(1),\n        STDERR(2);\n\n        public final int type;\n\n        Streams(int type) {\n            this.type = type;\n        }\n    }\n\n    /**\n     * Create a bytebuffer with all the messages. Timestamps will be added to each one.\n     */\n    private static ByteBuffer responseContent(Streams stream, String... messages) throws Exception {\n        List<ByteBuffer> buffers = new ArrayList<>(messages.length);\n        for (String message : messages) {\n            buffers.add(messageToBuffer(stream, message));\n        }\n\n        ByteBuffer[] bufArray = new ByteBuffer[buffers.size()];\n        return combineBuffers(buffers.toArray(bufArray));\n    }\n\n    private static ByteBuffer combineBuffers(ByteBuffer... buffers) {\n        int length = 0;\n        for (ByteBuffer b : buffers) {\n            length += b.limit();\n        }\n\n        ByteBuffer result = ByteBuffer.allocate(length);\n        for (ByteBuffer b : buffers) {\n            b.position(0);\n            result = result.put(b);\n        }\n\n        return result;\n    }\n\n    /**\n     * Create a bytebuffer for a single string message. A timestamp will be added.\n     */\n    private static ByteBuffer messageToBuffer(Streams stream, String message) throws Exception {\n        String logMessage = logMessage(message);\n\n        CharsetEncoder encoder = Charsets.UTF_8.newEncoder();\n        ByteBuffer payload = encoder.encode(CharBuffer.wrap(logMessage.toCharArray()));\n        assert payload.order() == ByteOrder.BIG_ENDIAN;\n        int length = payload.limit();\n\n        ByteBuffer result = ByteBuffer.allocate(length + 8);\n        result.order(ByteOrder.BIG_ENDIAN);\n\n        result.put((byte) stream.type);\n        result.position(result.position() + 3);\n        result.putInt(length);\n        result.put(payload);\n\n        return result;\n    }\n\n    /**\n     * Create a new string from message that has a timestamp prefix.\n     */\n    private static String logMessage(String message) {\n        return String.format(\"[2015-08-05T12:34:56Z] %s\", message);\n    }\n\n    private class IOExcpetionStream extends InputStream {\n        public int read() throws IOException {\n            throw new IOException(\"Something bad happened\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/assembly/DockerAssemblyConfigurationSourceTest.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.File;\nimport java.util.Arrays;\n\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.util.EnvUtil;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class DockerAssemblyConfigurationSourceTest {\n\n    private AssemblyConfiguration assemblyConfig;\n\n    @Before\n    public void setup() {\n        // set 'ignorePermissions' to something other then default\n        this.assemblyConfig = new AssemblyConfiguration.Builder()\n                .descriptor(\"assembly.xml\")\n                .descriptorRef(\"project\")\n                .permissions(\"keep\")\n                .build();\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Test\n    public void permissionMode() {\n        try {\n            new AssemblyConfiguration.Builder().permissions(\"blub\").build();\n        } catch (IllegalArgumentException exp) {\n            assertTrue(exp.getMessage().contains(\"blub\"));\n        }\n\n        AssemblyConfiguration config = new AssemblyConfiguration.Builder().ignorePermissions(false).permissions(\"ignore\").build();\n        assertTrue(config.isIgnorePermissions());;\n    }\n\n    @Test\n    public void testCreateSourceAbsolute() {\n        testCreateSource(buildParameters(\".\", \"/src/docker\".replace(\"/\", File.separator), \"/output/docker\".replace(\"/\", File.separator)));\n    }\n\n    @Test\n    public void testCreateSourceRelative() {\n        testCreateSource(buildParameters(\".\",\"src/docker\".replace(\"/\", File.separator), \"output/docker\".replace(\"/\", File.separator)));\n    }\n\n    @Test\n    public void testOutputDirHasImage() {\n        String image = \"image\";\n        MojoParameters params = buildParameters(\".\", \"src/docker\", \"output/docker\");\n        DockerAssemblyConfigurationSource source = new DockerAssemblyConfigurationSource(params,\n                                                                                         new BuildDirs(image, params),assemblyConfig);\n\n        assertTrue(containsDir(image, source.getOutputDirectory()));\n        assertTrue(containsDir(image, source.getWorkingDirectory()));\n        assertTrue(containsDir(image, source.getTemporaryRootDirectory()));\n    }\n\n    private MojoParameters buildParameters(String projectDir, String sourceDir, String outputDir) {\n        MavenProject mavenProject = new MavenProject();\n        mavenProject.setFile(new File(projectDir));\n        return new MojoParameters(null, mavenProject, null, null, null, null, sourceDir, outputDir, null);\n    }\n\n    @Test\n    public void testEmptyAssemblyConfig() {\n        DockerAssemblyConfigurationSource source = new DockerAssemblyConfigurationSource(\n               new MojoParameters(null, null, null, null, null, null, \"/src/docker\", \"/output/docker\", null),\n               null,null\n        );\n        assertEquals(0,source.getDescriptors().length);\n    }\n\n    private void testCreateSource(MojoParameters params) {\n        DockerAssemblyConfigurationSource source =\n                new DockerAssemblyConfigurationSource(params, new BuildDirs(\"image\", params), assemblyConfig);\n\n        String[] descriptors = source.getDescriptors();\n        String[] descriptorRefs = source.getDescriptorReferences();\n\n        assertEquals(\"count of descriptors\", 1, descriptors.length);\n        Assert.assertEquals(\"directory of assembly\", EnvUtil.prepareAbsoluteSourceDirPath(params, \"assembly.xml\").getAbsolutePath(), descriptors[0]);\n\n        assertEquals(\"count of descriptors references\", 1, descriptorRefs.length);\n        assertEquals(\"reference must be project\", \"project\", descriptorRefs[0]);\n\n        assertFalse(\"we must not ignore permissions when creating the archive\", source.isIgnorePermissions());\n\n        String outputDir = params.getOutputDirectory();\n\n        assertStartsWithDir(outputDir, source.getOutputDirectory());\n        assertStartsWithDir(outputDir, source.getWorkingDirectory());\n        assertStartsWithDir(outputDir, source.getTemporaryRootDirectory());\n    }\n\n    private boolean containsDir(String outputDir, File path) {\n        return path.toString().contains(outputDir + File.separator);\n    }\n\n    private void assertStartsWithDir(String outputDir, File path) {\n        String expectedStartsWith = outputDir + File.separator;\n        int length = expectedStartsWith.length();\n        assertEquals(expectedStartsWith, path.toString().substring(0, length));\n    }\n\n    @Test\n    public void testReactorProjects() {\n    \tMavenProject reactorProject1 = new MavenProject();\n    \treactorProject1.setFile(new File(\"../reactor-1\"));\n\n        MavenProject reactorProject2 = new MavenProject();\n        reactorProject2.setFile(new File(\"../reactor-2\"));\n\n        DockerAssemblyConfigurationSource source = new DockerAssemblyConfigurationSource(\n               new MojoParameters(null, null, null, null, null, null, \"/src/docker\", \"/output/docker\", Arrays.asList(new MavenProject[] { reactorProject1, reactorProject2 })),\n               null,null\n        );\n        assertEquals(2,source.getReactorProjects().size());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/assembly/DockerAssemblyManagerTest.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\n\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport io.fabric8.maven.docker.util.DockerFileUtil;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport mockit.Expectations;\nimport mockit.Injectable;\nimport mockit.Mock;\nimport mockit.MockUp;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.artifact.repository.ArtifactRepository;\nimport org.apache.maven.artifact.repository.MavenArtifactRepository;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.assembly.AssemblerConfigurationSource;\nimport org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;\nimport org.apache.maven.plugins.assembly.archive.ArchiveCreationException;\nimport org.apache.maven.plugins.assembly.archive.AssemblyArchiver;\nimport org.apache.maven.plugins.assembly.format.AssemblyFormattingException;\nimport org.apache.maven.plugins.assembly.io.AssemblyReadException;\nimport org.apache.maven.plugins.assembly.io.AssemblyReader;\nimport org.apache.maven.plugins.assembly.model.Assembly;\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.settings.Settings;\nimport org.codehaus.plexus.archiver.manager.ArchiverManager;\nimport org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;\nimport org.codehaus.plexus.util.ReflectionUtils;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertFalse;\n\npublic class DockerAssemblyManagerTest {\n\n    @Tested\n    private DockerAssemblyManager assemblyManager;\n\n    @Injectable\n    private AssemblyArchiver assemblyArchiver;\n\n    @Injectable\n    private AssemblyReader assemblyReader;\n\n    @Injectable\n    private ArchiverManager archiverManager;\n\n    @Injectable\n    private MappingTrackArchiver trackArchiver;\n\n    @Test\n    public void testNoAssembly() {\n        BuildImageConfiguration buildConfig = new BuildImageConfiguration();\n        AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n\n        DockerFileBuilder builder = assemblyManager.createDockerFileBuilder(buildConfig, assemblyConfig);\n        String content = builder.content();\n\n        assertFalse(content.contains(\"COPY\"));\n        assertFalse(content.contains(\"VOLUME\"));\n    }\n\n    @Test\n    public void assemblyFiles(@Injectable final MojoParameters mojoParams,\n                              @Injectable final MavenProject project,\n                              @Injectable final Assembly assembly) throws AssemblyFormattingException, ArchiveCreationException, InvalidAssemblerConfigurationException, MojoExecutionException, AssemblyReadException, IllegalAccessException {\n\n        ReflectionUtils.setVariableValueInObject(assemblyManager, \"trackArchiver\", trackArchiver);\n\n        new Expectations() {{\n            mojoParams.getOutputDirectory();\n            result = \"target/\"; times = 3;\n\n            mojoParams.getProject();\n            project.getBasedir();\n            result = \".\";\n\n            assemblyReader.readAssemblies((AssemblerConfigurationSource) any);\n            result = Arrays.asList(assembly);\n\n        }};\n\n        BuildImageConfiguration buildConfig = createBuildConfig();\n\n        assemblyManager.getAssemblyFiles(\"testImage\", buildConfig, mojoParams, new AnsiLogger(new SystemStreamLog(),true,\"build\"));\n    }\n\n    @Test\n    public void testCopyValidVerifyGivenDockerfile(@Injectable final Logger logger) throws IOException {\n        BuildImageConfiguration buildConfig = createBuildConfig();\n\n        assemblyManager.verifyGivenDockerfile(\n            new File(getClass().getResource(\"/docker/Dockerfile_assembly_verify_copy_valid.test\").getPath()),\n            buildConfig,\n            createInterpolator(buildConfig),\n            logger);\n\n        new Verifications() {{\n            logger.warn(anyString, (Object []) any); times = 0;\n        }};\n\n    }\n\n    @Test\n    public void testCopyInvalidVerifyGivenDockerfile(@Injectable final Logger logger) throws IOException {\n        BuildImageConfiguration buildConfig = createBuildConfig();\n\n        assemblyManager.verifyGivenDockerfile(\n            new File(getClass().getResource(\"/docker/Dockerfile_assembly_verify_copy_invalid.test\").getPath()),\n            buildConfig, createInterpolator(buildConfig),\n            logger);\n\n        new Verifications() {{\n            logger.warn(anyString, (Object []) any); times = 1;\n        }};\n\n    }\n\n    @Test\n    public void testCopyChownValidVerifyGivenDockerfile(@Injectable final Logger logger) throws IOException {\n        BuildImageConfiguration buildConfig = createBuildConfig();\n\n        assemblyManager.verifyGivenDockerfile(\n            new File(getClass().getResource(\"/docker/Dockerfile_assembly_verify_copy_chown_valid.test\").getPath()),\n            buildConfig,\n            createInterpolator(buildConfig),\n            logger);\n\n        new Verifications() {{\n            logger.warn(anyString, (Object []) any); times = 0;\n        }};\n\n    }\n\n    private BuildImageConfiguration createBuildConfig() {\n        return new BuildImageConfiguration.Builder()\n                .assembly(new AssemblyConfiguration.Builder()\n                        .descriptorRef(\"artifact\")\n                        .build())\n                .build();\n    }\n\n    private FixedStringSearchInterpolator createInterpolator(BuildImageConfiguration buildConfig) {\n        MavenProject project = new MavenProject();\n        project.setArtifactId(\"docker-maven-plugin\");\n\n        return DockerFileUtil.createInterpolator(mockMojoParams(project), buildConfig.getFilter());\n    }\n\n\n    private MojoParameters mockMojoParams(MavenProject project) {\n        Settings settings = new Settings();\n        ArtifactRepository localRepository = new MavenArtifactRepository() {\n            @Mock\n            public String getBasedir() {\n                return \"repository\";\n            }\n        };\n        @SuppressWarnings(\"deprecation\")\n        MavenSession session = new MavenSession(null, settings, localRepository, null, null, Collections.<String>emptyList(), \".\", null, null, new Date());\n        return new MojoParameters(session, project, null, null, null, settings, \"src\", \"target\", Collections.singletonList(project));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/assembly/DockerFileBuilderTest.java",
    "content": "package io.fabric8.maven.docker.assembly;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.regex.Pattern;\n\nimport io.fabric8.maven.docker.config.*;\nimport com.google.common.collect.ImmutableMap;\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Test;\n\nimport static org.hamcrest.Matchers.hasEntry;\nimport static org.junit.Assert.*;\n\n\npublic class DockerFileBuilderTest {\n\n    @Test\n    public void testBuildDockerFile() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        Arguments b = Arguments.Builder.get().withParam(\"/bin/sh\").withParam(\"-c\").build();\n        String dockerfileContent = new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .env(ImmutableMap.of(\"foo\", \"bar\"))\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .labels(ImmutableMap.of(\"com.acme.foobar\", \"How are \\\"you\\\" ?\"))\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .shell(b)\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n        String expected = loadFile(\"docker/Dockerfile.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test\n    public void testBuildDockerShellArgumentsForShell() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        Arguments b = new Arguments(\"/bin/sh -c\");\n        String dockerfileContent = new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .env(ImmutableMap.of(\"foo\", \"bar\"))\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .labels(ImmutableMap.of(\"com.acme.foobar\", \"How are \\\"you\\\" ?\"))\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .shell(b)\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n        String expected = loadFile(\"docker/Dockerfile.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test\n    public void testBuildDockerFileMultilineLabel() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        String dockerfileContent = new DockerFileBuilder()\n                .add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .labels(ImmutableMap.of(\"key\", \"unquoted\",\n                        \"flag\", \"\",\n                        \"with_space\", \"1.fc nuremberg\",\n                        \"some-json\", \"{\\n  \\\"key\\\": \\\"value\\\"\\n}\\n\"))\n                .content();\n        String expected = loadFile(\"docker/Dockerfile.multiline_label.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test\n    public void testBuildLabelWithSpace() throws Exception {\n        String dockerfileContent = new DockerFileBuilder()\n                .labels(ImmutableMap.of(\"key\", \"label with space\"))\n                .content();\n        assertTrue(stripCR(dockerfileContent).contains(\"LABEL key=\\\"label with space\\\"\"));\n    }\n\n    @Test\n    public void testBuildDockerFileUDPPort() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        String dockerfileContent = new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080/udp\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n        String expected = loadFile(\"docker/Dockerfile_udp.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test\n    public void testBuildDockerFileExplicitTCPPort() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        String dockerfileContent = new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080/tcp\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n        String expected = loadFile(\"docker/Dockerfile_tcp.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test(expected=IllegalArgumentException.class)\n    public void testBuildDockerFileBadPort() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .env(ImmutableMap.of(\"foo\", \"bar\"))\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080aaa/udp\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .labels(ImmutableMap.of(\"com.acme.foobar\", \"How are \\\"you\\\" ?\"))\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n    }\n\n    @Test(expected=IllegalArgumentException.class)\n    public void testBuildDockerFileBadProtocol() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .env(ImmutableMap.of(\"foo\", \"bar\"))\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080/bogusdatagram\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .labels(ImmutableMap.of(\"com.acme.foobar\", \"How are \\\"you\\\" ?\"))\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .run(Arrays.asList(\"echo something\", \"echo second\"))\n                .content();\n    }\n\n    @Test\n    public void testDockerFileOptimisation() throws Exception {\n        Arguments a = Arguments.Builder.get().withParam(\"c1\").withParam(\"c2\").build();\n        String dockerfileContent = new DockerFileBuilder().add(\"/src\", \"/dest\")\n                .baseImage(\"image\")\n                .cmd(a)\n                .env(ImmutableMap.of(\"foo\", \"bar\"))\n                .basedir(\"/export\")\n                .expose(Collections.singletonList(\"8080\"))\n                .maintainer(\"maintainer@example.com\")\n                .workdir(\"/tmp\")\n                .labels(ImmutableMap.of(\"com.acme.foobar\", \"How are \\\"you\\\" ?\"))\n                .volumes(Collections.singletonList(\"/vol1\"))\n                .run(Arrays.asList(\"echo something\", \"echo second\", \"echo third\", \"echo fourth\", \"echo fifth\"))\n                .optimise()\n                .content();\n        String expected = loadFile(\"docker/Dockerfile_optimised.test\");\n        assertEquals(expected, stripCR(dockerfileContent));\n    }\n\n    @Test\n    public void testMaintainer() {\n        String dockerfileContent = new DockerFileBuilder().maintainer(\"maintainer@example.com\").content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"MAINTAINER\", \"maintainer@example.com\"));\n    }\n\n    @Test\n    public void testOptimise() {\n        String dockerfileContent = new DockerFileBuilder().optimise().run(Arrays.asList(\"echo something\", \"echo two\")).content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"RUN\", \"echo something && echo two\"));\n    }\n\n    @Test\n    public void testOptimiseOnEmptyRunCommandListDoesNotThrowException() {\n        new DockerFileBuilder().optimise().content();\n    }\n\n    @Test\n    public void testEntryPointShell() {\n        Arguments a = Arguments.Builder.get().withShell(\"java -jar /my-app-1.1.1.jar server\").build();\n        String dockerfileContent = new DockerFileBuilder().entryPoint(a).content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"ENTRYPOINT\", \"java -jar /my-app-1.1.1.jar server\"));\n    }\n\n    @Test\n    public void testEntryPointParams() {\n        Arguments a = Arguments.Builder.get().withParam(\"java\").withParam(\"-jar\").withParam(\"/my-app-1.1.1.jar\").withParam(\"server\").build();\n        String dockerfileContent = new DockerFileBuilder().entryPoint(a).content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"ENTRYPOINT\", \"[\\\"java\\\",\\\"-jar\\\",\\\"/my-app-1.1.1.jar\\\",\\\"server\\\"]\"));\n    }\n\n    @Test\n    public void testHealthCheckCmdParams() {\n        HealthCheckConfiguration hc = new HealthCheckConfiguration.Builder().cmd(new Arguments(\"echo hello\")).interval(\"5s\").timeout(\"3s\").startPeriod(\"30s\").retries(4).build();\n        String dockerfileContent = new DockerFileBuilder().healthCheck(hc).content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"HEALTHCHECK\", \"--interval=5s --timeout=3s --start-period=30s --retries=4 CMD echo hello\"));\n    }\n\n    @Test\n    public void testHealthCheckNone() {\n        HealthCheckConfiguration hc = new HealthCheckConfiguration.Builder().mode(HealthCheckMode.none).build();\n        String dockerfileContent = new DockerFileBuilder().healthCheck(hc).content();\n        assertThat(dockerfileToMap(dockerfileContent), hasEntry(\"HEALTHCHECK\", \"NONE\"));\n    }\n\n    @Test\n    public void testNoRootExport() {\n        assertFalse(new DockerFileBuilder().add(\"/src\", \"/dest\").basedir(\"/\").content().contains(\"VOLUME\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void illegalNonAbsoluteBaseDir() {\n        new DockerFileBuilder().basedir(\"blub\").content();\n    }\n\n    @Test\n    public void testAssemblyUserWithChown() {\n        String dockerFile = new DockerFileBuilder().assemblyUser(\"jboss:jboss:jboss\")\n                                                   .add(\"a\",\"a/nested\").add(\"b\",\"b/deeper/nested\").content();\n        assertThat(dockerfileToMap(dockerFile), hasEntry(\"COPY\", \"--chown=jboss:jboss b /maven/b/deeper/nested\"));\n    }\n\n    @Test\n    public void testUser() {\n        String dockerFile = new DockerFileBuilder().assemblyUser(\"jboss:jboss:jboss\").user(\"bob\")\n                                                   .add(\"a\",\"a/nested\").add(\"b\",\"b/deeper/nested\").content();\n        String EXPECTED_REGEXP = \"USER bob$\";\n        Pattern pattern = Pattern.compile(EXPECTED_REGEXP);\n        assertTrue(pattern.matcher(dockerFile).find());\n    }\n\n\n    @Test\n    public void testExportBaseDir() {\n        assertTrue(new DockerFileBuilder().basedir(\"/export\").content().contains(\"/export\"));\n        assertFalse(new DockerFileBuilder().baseImage(\"java\").basedir(\"/export\").content().contains(\"/export\"));\n        assertTrue(new DockerFileBuilder().baseImage(\"java\").exportTargetDir(true).basedir(\"/export\").content().contains(\"/export\"));\n        assertFalse(new DockerFileBuilder().baseImage(\"java\").exportTargetDir(false).basedir(\"/export\").content().contains(\"/export\"));\n    }\n\n    @Test\n    public void testTargetDirStartsWithEnvVar() {\n        assertTrue(new DockerFileBuilder().basedir(\"${FOO}\").content().contains(\"${FOO}\"));\n        assertTrue(new DockerFileBuilder().basedir(\"$FOO\").content().contains(\"$FOO\"));\n        assertTrue(new DockerFileBuilder().basedir(\"${FOO}/\").content().contains(\"${FOO}\"));\n        assertTrue(new DockerFileBuilder().basedir(\"$FOO/\").content().contains(\"$FOO\"));\n        assertTrue(new DockerFileBuilder().basedir(\"${FOO}/bar\").content().contains(\"${FOO}/bar\"));\n        assertTrue(new DockerFileBuilder().basedir(\"$FOO/bar\").content().contains(\"$FOO/bar\"));\n    }\n\n    @Test\n    public void testDockerFileKeywords() {\n        StringBuilder b = new StringBuilder();\n        DockerFileKeyword.RUN.addTo(b, \"apt-get\", \"update\");\n        assertEquals(\"RUN apt-get update\\n\", b.toString());\n\n        b = new StringBuilder();\n        DockerFileKeyword.EXPOSE.addTo(b, new String[]{\"1010\", \"2020\"});\n        assertEquals(\"EXPOSE 1010 2020\\n\",b.toString());\n\n        b = new StringBuilder();\n        DockerFileKeyword.USER.addTo(b, \"roland\");\n        assertEquals(\"USER roland\\n\",b.toString());\n    }\n\n    private String stripCR(String input){\n    \treturn input.replaceAll(\"\\r\", \"\");\n    }\n\n    private String loadFile(String fileName) throws IOException {\n        return stripCR(IOUtils.toString(getClass().getClassLoader().getResource(fileName)));\n    }\n\n    private static Map<String, String> dockerfileToMap(String dockerFile) {\n        final Map<String, String> dockerfileMap = new HashMap<>();\n        final Scanner scanner = new Scanner(dockerFile);\n        while (scanner.hasNextLine()) {\n            String line = scanner.nextLine();\n            if (line.trim().length() == 0) {\n                continue;\n            }\n            String[] commandAndArguments = line.trim().split(\"\\\\s+\", 2);\n            if (commandAndArguments.length < 2) {\n                continue;\n            }\n            dockerfileMap.put(commandAndArguments[0], commandAndArguments[1]);\n        }\n        scanner.close();\n        return dockerfileMap;\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/assembly/MappingTrackArchiverTest.java",
    "content": "package io.fabric8.maven.docker.assembly;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.File;\nimport java.util.List;\n\nimport mockit.Injectable;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 02/07/15\n */\npublic class MappingTrackArchiverTest {\n\n    @Injectable\n    private MavenSession session;\n\n    private MappingTrackArchiver archiver;\n\n    @Before\n    public void setup() throws IllegalAccessException {\n        archiver = new MappingTrackArchiver();\n        archiver.init(new AnsiLogger(new SystemStreamLog(),false,\"build\"), \"maven\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void noDirectory() throws Exception {\n        archiver.setDestFile(new File(\".\"));\n        archiver.addDirectory(new File(System.getProperty(\"user.home\")), \"tmp\");\n        AssemblyFiles files = archiver.getAssemblyFiles(session);\n    }\n\n    @Test\n    public void simple() throws Exception {\n        archiver.setDestFile(new File(\"target/test-data/maven.tracker\"));\n        new File(archiver.getDestFile(), \"maven\").mkdirs();\n\n        File tempFile = File.createTempFile(\"tracker\", \"txt\");\n        File destination = new File(\"target/test-data/maven/test.txt\");\n        org.codehaus.plexus.util.FileUtils.copyFile(tempFile, destination);\n\n        archiver.addFile(tempFile, \"test.txt\");\n        AssemblyFiles files = archiver.getAssemblyFiles(session);\n        assertNotNull(files);\n        List<AssemblyFiles.Entry> entries = files.getUpdatedEntriesAndRefresh();\n        assertEquals(0, entries.size());\n        Thread.sleep(1000);\n        FileUtils.touch(tempFile);\n        entries = files.getUpdatedEntriesAndRefresh();\n        assertEquals(1, entries.size());\n        AssemblyFiles.Entry entry = entries.get(0);\n        assertEquals(tempFile, entry.getSrcFile());\n        assertEquals(destination, entry.getDestFile());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/BuildImageConfigurationTest.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.io.File;\nimport java.io.IOException;\n\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Test;\n\nimport static io.fabric8.maven.docker.config.ArchiveCompression.gzip;\nimport static io.fabric8.maven.docker.config.ArchiveCompression.none;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * @author roland\n * @since 04/04/16\n */\n\npublic class BuildImageConfigurationTest {\n\n    @Mocked\n    Logger logger;\n\n    @Test\n    public void empty() {\n        BuildImageConfiguration config = new BuildImageConfiguration();\n        config.initAndValidate(logger);\n        assertFalse(config.isDockerFileMode());\n    }\n\n    @Test\n    public void simpleDockerfile() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerFile(\"src/main/docker/Dockerfile\").build();\n        config.initAndValidate(logger);\n        assertTrue(config.isDockerFileMode());\n        assertEquals(config.getDockerFile(),new File(\"src/main/docker/Dockerfile\"));\n        assertEquals(config.getContextDir(),new File(\"src/main/docker\"));\n    }\n\n    @Test\n    // Tests fix for #1200\n    public void simpleDockerfileWithoutParentDir() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerFile(\"Dockerfile\").build();\n        config.initAndValidate(logger);\n        assertTrue(config.isDockerFileMode());\n        assertEquals(config.getDockerFile(),new File(\"Dockerfile\"));\n        assertEquals(config.getContextDir(), new File(\"\"));\n    }\n\n\n    @Test\n    public void simpleDockerfileDir() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerFileDir(\"src/docker/\").build();\n        config.initAndValidate(logger);\n        assertTrue(config.isDockerFileMode());\n        assertEquals(config.getDockerFile(),new File(\"src/docker/Dockerfile\"));\n        assertFalse(config.getContextDir().isAbsolute());\n    }\n\n    @Test\n    public void DockerfileDirAndDockerfileAlsoSet() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerFileDir(\"/tmp/\").\n                dockerFile(\"Dockerfile\").build();\n        config.initAndValidate(logger);\n        assertTrue(config.isDockerFileMode());\n        assertEquals(config.getDockerFile(),new File(\"/tmp/Dockerfile\"));\n    }\n\n    @Test(expected=IllegalArgumentException.class)\n    public void DockerfileDirAndDockerfileAlsoSetButDockerfileIsAbsoluteExceptionThrown() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerFileDir(\"/tmp/\").\n                dockerFile(new File(\"Dockerfile\").getAbsolutePath()).build();\n        config.initAndValidate(logger);\n    }\n\n    @Test\n    public void contextDir() {\n        BuildImageConfiguration config =\n                new BuildImageConfiguration.Builder().\n                        contextDir(\"target\").build();\n        config.initAndValidate(logger);\n        assertEquals(new File(\"target\"), config.getContextDir());\n    }\n\n    @Test\n    public void contextDirAndDockerfile() {\n        BuildImageConfiguration config =\n                new BuildImageConfiguration.Builder().\n                        dockerFile(\"src/docker/Dockerfile\").\n                        contextDir(\"target\").build();\n        config.initAndValidate(logger);\n        assertEquals(new File(\"target/src/docker/Dockerfile\"), config.getDockerFile());\n        assertEquals(new File(\"target\"), config.getContextDir());\n    }\n\n    @Test\n    public void contextDirAndDockerfileDir() {\n        BuildImageConfiguration config =\n                new BuildImageConfiguration.Builder().\n                        dockerFileDir(\"src/docker\").\n                        contextDir(\"target\").build();\n        config.initAndValidate(logger);\n        assertEquals(new File(\"target/Dockerfile\"), config.getDockerFile());\n        assertEquals(new File(\"target\"), config.getContextDir());\n    }\n\n    @Test\n    public void contextDirAndAbsoluteDockerfile() throws IOException {\n        File tempDockerFile = File.createTempFile(\"Dockerfile\", \"\");\n        tempDockerFile.deleteOnExit();\n        BuildImageConfiguration config = new BuildImageConfiguration.Builder()\n                .dockerFile(tempDockerFile.getAbsolutePath())\n                .contextDir(\"target\")\n                .build();\n\n        // If contextDir is given and the dockerFile is an absolute path.\n        // The Dockerfile should then be copied over.\n        config.initAndValidate(logger);\n        assertEquals(new File(tempDockerFile.getAbsolutePath()), config.getDockerFile());\n        assertEquals(new File(\"target\"), config.getContextDir());\n    }\n\n    @Test\n    public void deprecatedDockerfileDir() {\n        AssemblyConfiguration assemblyConfig = new AssemblyConfiguration.Builder().dockerFileDir(\"src/docker\").build();\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                assembly(assemblyConfig).build();\n\n        new Expectations() {{\n            logger.warn(withSubstring(\"deprecated\"));\n        }};\n\n        config.initAndValidate(logger);\n        assertTrue(config.isDockerFileMode());\n        assertEquals(config.getDockerFile(),new File(\"src/docker/Dockerfile\"));\n    }\n\n    @Test\n    public void dockerFileAndArchive() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerArchive(\"this\").\n                dockerFile(\"that\").build();\n\n        try {\n            config.initAndValidate(logger);\n        } catch (IllegalArgumentException expected) {\n            return;\n        }\n        fail(\"Should have failed.\");\n    }\n\n    @Test\n    public void dockerArchive() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                dockerArchive(\"this\").build();\n        config.initAndValidate(logger);\n\n        assertFalse(config.isDockerFileMode());\n        assertEquals(new File(\"this\"), config.getDockerArchive());\n    }\n\n    @Test\n    public void compression() {\n        BuildImageConfiguration config =\n            new BuildImageConfiguration.Builder().\n                compression(\"gzip\").build();\n        assertEquals(gzip, config.getCompression());\n\n        config = new BuildImageConfiguration.Builder().build();\n        assertEquals(none, config.getCompression());\n\n        config =\n            new BuildImageConfiguration.Builder().\n                compression(null).build();\n        assertEquals(none, config.getCompression());\n    }\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/BuildImageConfigurationWithMavenDepsTest.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.io.File;\n\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author roland\n * @since 2019-04-21\n */\npublic class BuildImageConfigurationWithMavenDepsTest {\n\n    @Mocked\n    private MojoParameters params;\n\n    @Mocked\n    private MavenProject project;\n\n    @Mocked\n    Logger logger;\n\n    @Test\n    public void simpleDockerfileWithoutParentDir() {\n        new Expectations() {{\n           params.getSourceDirectory(); result = \"src/main/docker\";\n           params.getProject(); result = project;\n           project.getBasedir(); result = \"/project\";\n        }};\n\n        String[] data = new String[] {\n            null, \"Dockerfile\", \"/project/src/main/docker\", \"/project/src/main/docker/Dockerfile\",\n            null, \"/context/Dockerfile\", \"/context\", \"/context/Dockerfile\",\n            \"/context\", \"Dockerfile\", \"/context\", \"/context/Dockerfile\",\n            \"context\", \"Dockerfile\", \"/project/src/main/docker/context\", \"/project/src/main/docker/context/Dockerfile\",\n            \"context\", \"/other/Dockerfile\", \"/project/src/main/docker/context\", \"/other/Dockerfile\",\n            \"/context\", \"/other/Dockerfile\", \"/context\", \"/other/Dockerfile\"\n        };\n\n        // If the tests are run on Windows, the expected paths need to be adjusted.\n        // On platforms that use the Unix convention, the following does not actually change\n        // the test data.\n        for (int i = 0; i < data.length; ++i) {\n            if(data[i] != null) {\n                File file = new File(data[i]);\n                if(data[i].startsWith(\"/\")) {\n                    file = file.getAbsoluteFile();\n                }\n                data[i] = file.getPath();\n            }\n        }\n\n        for (int i = 0; i < data.length; i+= 4) {\n            BuildImageConfiguration config =\n                new BuildImageConfiguration.Builder()\n                    .contextDir(data[i])\n                    .dockerFile(data[i + 1]).build();\n            config.initAndValidate(logger);\n\n            assertEquals(data[i + 2], config.getAbsoluteContextDirPath(params).getAbsolutePath());\n            assertEquals(data[i + 3], config.getAbsoluteDockerFilePath(params).getAbsolutePath());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/CleanupModeTest.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n * \n * Copyright 2016 Roland Huss\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\nimport org.junit.Test;\nimport static io.fabric8.maven.docker.config.CleanupMode.*;\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 01/03/16\n */\npublic class CleanupModeTest {\n\n    @Test\n    public void parse() {\n\n        Object[] data = {\n            null, TRY_TO_REMOVE,\n            \"try\", TRY_TO_REMOVE,\n            \"FaLsE\", NONE,\n            \"NONE\", NONE,\n            \"true\", REMOVE,\n            \"removE\", REMOVE\n        };\n\n        for (int i = 0; i < data.length; i += 2) {\n            assertEquals(data[i+1],CleanupMode.parse((String) data[i]));\n        }\n    }\n\n    @Test\n    public void invalid() {\n        try {\n            CleanupMode.parse(\"blub\");\n            fail();\n        } catch (IllegalArgumentException exp) {\n            assertTrue(exp.getMessage().contains(\"blub\"));\n            assertTrue(exp.getMessage().contains(\"try\"));\n            assertTrue(exp.getMessage().contains(\"none\"));\n            assertTrue(exp.getMessage().contains(\"remove\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/ConfigHelperTest.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.util.*;\n\nimport io.fabric8.maven.docker.util.AnsiLogger;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 17/05/16\n */\npublic class ConfigHelperTest {\n\n    private boolean resolverCalled;\n    private boolean customizerCalled;\n\n    @Before\n    public void setUp() throws Exception {\n        resolverCalled = false;\n        customizerCalled = false;\n    }\n\n    @Test\n    public void noName() throws Exception {\n        try {\n            List<ImageConfiguration> configs = Arrays.asList(new ImageConfiguration.Builder().build());\n            ConfigHelper.resolveImages(null, configs, createResolver(), null, createCustomizer());\n            fail();\n        } catch (IllegalArgumentException exp) {\n            assertTrue(exp.getMessage().contains(\"name\"));\n        }\n    }\n\n    @Test\n    public void externalPropertyActivation() throws MojoFailureException {\n        MavenProject project = new MavenProject();\n        project.getProperties().put(ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY, \"anything\");\n\n        List<ImageConfiguration> images = Arrays.asList(new ImageConfiguration.Builder().name(\"test\").build());\n        ConfigHelper.validateExternalPropertyActivation(project, images);\n\n        images = Arrays.asList(\n                new ImageConfiguration.Builder().name(\"test\").build(),\n                new ImageConfiguration.Builder().name(\"test2\").build());\n\n        try {\n            ConfigHelper.validateExternalPropertyActivation(project, images);\n            fail();\n        }catch(MojoFailureException ex) {\n            assertTrue(ex.getMessage().contains(\"Cannot use property \" + ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY + \" on projects with multiple images\"));\n        }\n\n        // When one of the images are configured externally from other source, it is OK with two images.\n        Map<String, String> externalConfig = new HashMap<>();\n        images.get(0).setExternalConfiguration(externalConfig);\n        externalConfig.put(\"type\", \"othermagic\");\n\n        ConfigHelper.validateExternalPropertyActivation(project, images);\n\n        // Or if prefix is set explicitly\n        externalConfig.put(\"type\", \"properties\");\n        externalConfig.put(\"prefix\", \"docker\");\n\n        ConfigHelper.validateExternalPropertyActivation(project, images);\n\n        // But with default prefix it fails\n        externalConfig.remove(\"prefix\");\n\n        try {\n            ConfigHelper.validateExternalPropertyActivation(project, images);\n            fail();\n        }catch(MojoFailureException ex) {\n            assertTrue(ex.getMessage().contains(\"Cannot use property \" + ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY + \" on projects with multiple images\"));\n        }\n\n        // With no external properly, it works.\n        project.getProperties().clear();\n        ConfigHelper.validateExternalPropertyActivation(project, images);\n\n        // And if explicitly set to \"skip\" it works too.\n        project.getProperties().put(ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY, \"skip\");\n        ConfigHelper.validateExternalPropertyActivation(project, images);\n    }\n\n    @Test\n    public void simple() throws Exception {\n        List<ImageConfiguration> configs = Arrays.asList(new ImageConfiguration.Builder().name(\"test\").build());\n        List<ImageConfiguration> result = ConfigHelper.resolveImages(null, configs, createResolver(), null, createCustomizer());\n        assertEquals(1,result.size());\n        assertTrue(resolverCalled);\n        assertTrue(customizerCalled);\n    }\n\n    @Test\n    public void registry() throws Exception {\n        List<ImageConfiguration> configs = Arrays.asList(new ImageConfiguration.Builder().registry(\"docker.io\").name(\"test\").build());\n        List<ImageConfiguration> result = ConfigHelper.resolveImages(null, configs, createResolver(), null, createCustomizer());\n        assertEquals(1,result.size());\n        assertTrue(resolverCalled);\n        assertTrue(customizerCalled);\n        assertEquals(\"docker.io\", configs.get(0).getRegistry());\n    }\n\n    @Test\n    public void filter() throws Exception {\n        List<ImageConfiguration> configs = Arrays.asList(new ImageConfiguration.Builder().name(\"test\").build());\n        CatchingLog logCatcher = new CatchingLog();\n        List<ImageConfiguration> result = ConfigHelper.resolveImages(\n            new AnsiLogger(logCatcher, true, \"build\"),\n            configs, createResolver(), \"bla\", createCustomizer());\n        assertEquals(0,result.size());\n        assertTrue(resolverCalled);\n        assertTrue(customizerCalled);\n        assertTrue(logCatcher.getWarnMessage().contains(\"test\"));\n        assertTrue(logCatcher.getWarnMessage().contains(\"bla\"));\n    }\n\n    @Test\n    public void initAndValidate() throws Exception {\n        List<ImageConfiguration> configs = Arrays.asList(new ImageConfiguration.Builder().name(\"test\").build());\n        String api = ConfigHelper.initAndValidate(configs, \"v1.16\", ConfigHelper.NameFormatter.IDENTITY, null);\n        assertEquals(\"v1.16\",api);\n    }\n\n    private ConfigHelper.Customizer createCustomizer() {\n        return new ConfigHelper.Customizer() {\n            @Override\n            public List<ImageConfiguration> customizeConfig(List<ImageConfiguration> configs) {\n                customizerCalled = true;\n                return configs;\n            }\n        };\n    }\n\n    private ConfigHelper.Resolver createResolver() {\n        return new ConfigHelper.Resolver() {\n            @Override\n            public List<ImageConfiguration> resolve(ImageConfiguration image) {\n                resolverCalled = true;\n                return Collections.singletonList(image);\n            }\n        };\n    }\n\n    private class CatchingLog extends SystemStreamLog {\n        private String warnMessage;\n\n        @Override\n        public void warn(CharSequence content) {\n            this.warnMessage = content.toString();\n            super.warn(content);\n        }\n\n        void reset() {\n            warnMessage = null;\n        }\n\n        public String getWarnMessage() {\n            return warnMessage;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/HealthCheckConfigTest.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport org.junit.Test;\n\n/**\n * Tests the health check configuration\n */\npublic class HealthCheckConfigTest {\n\n    @Test\n    public void testGoodHealthCheck1() {\n        new HealthCheckConfiguration.Builder()\n                .cmd(new Arguments(\"exit 0\"))\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck2() {\n        new HealthCheckConfiguration.Builder()\n                .cmd(new Arguments(\"exit 0\"))\n                .retries(1)\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck3() {\n        new HealthCheckConfiguration.Builder()\n                .cmd(new Arguments(\"exit 0\"))\n                .retries(1)\n                .interval(\"2s\")\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck4() {\n        new HealthCheckConfiguration.Builder()\n                .cmd(new Arguments(\"exit 0\"))\n                .retries(1)\n                .interval(\"2s\")\n                .timeout(\"3s\")\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck5() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.cmd)\n                .cmd(new Arguments(\"exit 0\"))\n                .retries(1)\n                .interval(\"2s\")\n                .timeout(\"3s\")\n                .startPeriod(\"30s\")\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck6() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.cmd)\n                .cmd(new Arguments(\"exit 0\"))\n                .retries(1)\n                .interval(\"2s\")\n                .timeout(\"3s\")\n                .startPeriod(\"4s\")\n                .build()\n                .validate();\n    }\n\n    @Test\n    public void testGoodHealthCheck7() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck1() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .interval(\"2s\")\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck2() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .retries(1)\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck3() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .timeout(\"3s\")\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck4() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .startPeriod(\"30s\")\n                .cmd(new Arguments(\"echo a\"))\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck5() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.none)\n                .cmd(new Arguments(\"echo a\"))\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck6() {\n        new HealthCheckConfiguration.Builder()\n                .build()\n                .validate();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testBadHealthCheck7() {\n        new HealthCheckConfiguration.Builder()\n                .mode(HealthCheckMode.cmd)\n                .build()\n                .validate();\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/ImageConfigResolverTest.java",
    "content": "package io.fabric8.maven.docker.config;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.handler.ExternalConfigHandler;\nimport io.fabric8.maven.docker.config.handler.ImageConfigResolver;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Mocked;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;\nimport org.codehaus.plexus.util.ReflectionUtils;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 18/11/14\n */\npublic class ImageConfigResolverTest {\n\n    private ImageConfigResolver resolver;\n\n    @Mocked\n    private Logger log;\n\n    @Before\n    public void setUp() throws Exception {\n        resolver = new ImageConfigResolver();\n        ReflectionUtils.setVariableValueInObject(resolver, \"propertyConfigHandler\", new TestHandler(3));\n        resolver.initialize();\n        resolver.setLog(log);\n    }\n\n    @Test\n    public void direct() throws IllegalAccessException, InitializationException {\n        List<ImageConfiguration> rest = resolver.resolve(getImageConfiguration(\"vanilla\"), new MavenProject(), null);\n        assertEquals(1, rest.size());\n        assertEquals(\"vanilla\", rest.get(0).getName());\n    }\n\n    @Test\n    public void withReference() throws Exception {\n        Map<String,String> refConfig = Collections.singletonMap(\"type\", \"test\");\n        ImageConfiguration config = new ImageConfiguration.Builder().name(\"reference\").externalConfig(refConfig).build();\n        List<ImageConfiguration> rest = resolver.resolve(config,new MavenProject(), null);\n        assertEquals(3,rest.size());\n        for (int i = 0; i < 3;i++) {\n            assertEquals(\"image \" + i,rest.get(i).getName());\n        }\n    }\n\n    @Test\n    public void withExternalConfigActivation() throws Exception {\n        MavenProject project = new MavenProject();\n        // Value is not verified since we're only using our TestHandler\n        project.getProperties().put(ConfigHelper.EXTERNALCONFIG_ACTIVATION_PROPERTY, \"notactuallyverified\");\n\n        List<ImageConfiguration> rest = resolver.resolve(getImageConfiguration(\"vanilla\"), project, null);\n        assertEquals(3,rest.size());\n        for (int i = 0; i < 3;i++) {\n            assertEquals(\"image \" + i,rest.get(i).getName());\n        }\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void noType() {\n        Map<String,String> refConfig = Collections.singletonMap(\"notAType\",\"test\");\n        ImageConfiguration config = new ImageConfiguration.Builder()\n                .name(\"reference\")\n                .externalConfig(refConfig).build();\n        resolver.resolve(config,new MavenProject(), null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void unknownType() {\n        Map<String,String> refConfig = Collections.singletonMap(\"type\",\"unknown\");\n        ImageConfiguration config = new ImageConfiguration.Builder()\n                .name(\"reference\")\n                .externalConfig(refConfig).build();\n        resolver.resolve(config,new MavenProject(), null);\n    }\n\n    private static class TestHandler implements ExternalConfigHandler {\n\n        int nr;\n\n        public TestHandler(int nr) {\n            this.nr = nr;\n        }\n\n        @Override\n        public String getType() {\n            return \"test\";\n        }\n\n        @Override\n        public List<ImageConfiguration> resolve(ImageConfiguration referenceConfig, MavenProject project, MavenSession session) {\n            List<ImageConfiguration> ret = new ArrayList<>();\n            for (int i = 0; i < nr;i++) {\n                ImageConfiguration config = getImageConfiguration(\"image \" + i);\n                ret.add(config);\n            }\n            return ret;\n        }\n    }\n\n    private static ImageConfiguration getImageConfiguration(String name) {\n        return new ImageConfiguration.Builder().name(name).build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/LogConfigurationTest.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Tests LogConfiguration\n */\npublic class LogConfigurationTest {\n    @Test\n    public void testDefaultConfiguration() {\n        LogConfiguration cfg = LogConfiguration.DEFAULT;\n        assertNull(cfg.isEnabled());\n        assertFalse(cfg.isActivated());\n    }\n\n    @Test\n    public void testEmptyBuiltConfiguration() {\n        LogConfiguration cfg = new LogConfiguration.Builder()\n                .build();\n        assertNull(cfg.isEnabled());\n        assertFalse(cfg.isActivated());\n    }\n\n    @Test\n    public void testNonEmptyBuiltConfiguration() {\n        LogConfiguration cfg = new LogConfiguration.Builder()\n                .file(\"test\")\n                .build();\n        assertNull(cfg.isEnabled());\n        assertTrue(cfg.isActivated());\n\n        cfg = new LogConfiguration.Builder()\n                .color(\"test\")\n                .build();\n        assertNull(cfg.isEnabled());\n        assertTrue(cfg.isActivated());\n\n        cfg = new LogConfiguration.Builder()\n                .logDriverName(\"test\")\n                .build();\n        assertNull(cfg.isEnabled());\n        assertTrue(cfg.isActivated());\n\n        cfg = new LogConfiguration.Builder()\n                .date(\"1234\")\n                .build();\n        assertNull(cfg.isEnabled());\n        assertTrue(cfg.isActivated());\n    }\n\n    @Test\n    public void testEnabled() {\n        for (Boolean enabled : new Boolean[]{Boolean.TRUE, new Boolean(true)}) {\n            LogConfiguration cfg = new LogConfiguration.Builder()\n                .enabled(enabled)\n                .build();\n            assertTrue(cfg.isEnabled());\n            assertTrue(cfg.isActivated());\n\n            cfg = new LogConfiguration.Builder()\n                .enabled(true)\n                .color(\"red\")\n                .build();\n            assertTrue(cfg.isEnabled());\n            assertTrue(cfg.isActivated());\n            assertEquals(\"red\", cfg.getColor());\n        }\n    }\n\n    @Test\n    public void testDisabled() {\n        for (Boolean disabled : new Boolean[]{Boolean.FALSE, new Boolean(false)}) {\n            LogConfiguration cfg = new LogConfiguration.Builder()\n                .color(\"red\")\n                .enabled(disabled)\n                .build();\n            assertFalse(cfg.isEnabled());\n            assertFalse(cfg.isActivated());\n            assertEquals(\"red\", cfg.getColor());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/NetworkingConfigTest.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.util.Arrays;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\nimport static io.fabric8.maven.docker.config.NetworkConfig.Mode.*;\n\n/**\n * @author roland\n * @since 12/02/16\n */\npublic class NetworkingConfigTest {\n\n    @Test\n    public void simple() {\n        Object[] data = {\n            bridge, null, \"BRiDge\", \"bridge\", \"true\", \"false\", null, null,\n            host, null, \"host\", \"host\", \"true\", \"false\", null, null,\n            container, \"alpha\", \"container:alpha\", \"container:containerId\", \"true\", \"false\", \"alpha\", null,\n            null, \"blubber\", \"blubber\", \"custom\", \"false\", \"true\", null, \"blubber\",\n            custom, \"blubber\", \"blubber\", \"custom\", \"false\", \"true\", null, \"blubber\",\n            none, null, \"None\", \"none\", \"true\", \"false\", null, null\n        };\n        for (int i = 0; i < data.length; i += 8) {\n            for (NetworkConfig config : new NetworkConfig[]{\n                new NetworkConfig((NetworkConfig.Mode) data[i],(String) data[i + 1]),\n                new NetworkConfig((String) data[i + 2])}) {\n                if (config.isStandardNetwork()) {\n                    assertEquals(data[i + 3], config.getStandardMode(\"containerId\"));\n                } else {\n                    try {\n                        config.getStandardMode(\"fail\");\n                        fail(\"Test \" + i % 8);\n                    } catch (IllegalArgumentException exp) {\n                        // expected\n                    }\n                }\n                assertEquals(Boolean.parseBoolean((String) data[i + 4]), config.isStandardNetwork());\n                assertEquals(Boolean.parseBoolean((String) data[i + 5]), config.isCustomNetwork());\n                assertEquals(data[i + 6], config.getContainerAlias());\n                assertEquals(data[i + 7], config.getCustomNetwork());\n            }\n        }\n    }\n\n    @Test\n    public void empty() {\n        for (String str : new String[]{ null, \"\" }) {\n            NetworkConfig config = new NetworkConfig(str);\n            assertFalse(config.isStandardNetwork());\n            assertFalse(config.isCustomNetwork());\n            assertNull(config.getContainerAlias());\n            assertNull(config.getCustomNetwork());\n        }\n    }\n\n    @Test\n    public void builder() {\n        NetworkConfig config = new NetworkConfig.Builder().build();\n        assertNull(config);\n\n        config = new NetworkConfig.Builder().name(\"hello\").aliases(Arrays.asList(\"alias1\", \"alias2\")).build();\n        assertTrue(config.isCustomNetwork());\n        assertEquals(\"hello\",config.getCustomNetwork());\n        assertEquals(2,config.getAliases().size());\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/RegistryAuthConfigurationTest.java",
    "content": "package io.fabric8.maven.docker.config;\n\nimport java.lang.reflect.Field;\nimport java.util.Map;\n\nimport org.junit.Test;\nimport static org.junit.Assert.*;\n\npublic class RegistryAuthConfigurationTest {\n\n    @Test\n    public void deprecatedAuthTokenTest() throws ReflectiveOperationException {\n        RegistryAuthConfiguration config = new RegistryAuthConfiguration();\n        setField(config, \"authToken\", \"foo\");\n        Map map = config.toMap();\n        assertNull(map.get(\"authToken\"));\n        assertEquals(\"foo\", map.get(\"auth\"));\n    }\n\n    @Test\n    public void invalidAuthTokenConfigTest() throws ReflectiveOperationException {\n        RegistryAuthConfiguration config = new RegistryAuthConfiguration();\n        setField(config, \"authToken\", \"foo\");\n        setField(config, \"auth\", \"bar\");\n        try {\n            config.toMap();\n            fail(\"Should throw an exception because both 'auth' and 'authToken' is specified\");\n        } catch (IllegalStateException exp) {\n\n        }\n    }\n\n        @Test\n    public void authTest() throws ReflectiveOperationException {\n        RegistryAuthConfiguration config = new RegistryAuthConfiguration();\n        setField(config, \"auth\", \"bar\");\n        Map map = config.toMap();\n        assertNull(map.get(\"authToken\"));\n        assertEquals(\"bar\", map.get(\"auth\"));\n    }\n\n\n    private void setField(Object obj, String name, Object value) throws ReflectiveOperationException {\n        Field field  = obj.getClass().getDeclaredField(name);\n        field.setAccessible(true);\n        field.set(obj, value);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/UlimitConfigTest.java",
    "content": "package io.fabric8.maven.docker.config;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport org.junit.Test;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\n/**\n * @author roland\n * @since 19/07/16\n */\npublic class UlimitConfigTest {\n\n    @Test\n    public void simple() {\n        Object[] data = new Object[] {\n            \"memlock=1024:2048\", \"memlock\", 1024, 2048,\n            \"memlock=:2048\", \"memlock\", null, 2048,\n            \"memlock=1024\", \"memlock\", 1024, null,\n            \"memlock=1024:\", \"memlock\", 1024, null\n        };\n\n        for (int i = 0; i < data.length; i+=4) {\n            UlimitConfig config = new UlimitConfig(data[0].toString());\n            assertEquals(data[1], config.getName());\n            assertEquals(data[2], config.getHard());\n            assertEquals(data[3], config.getSoft());\n        }\n    }\n\n    @Test\n    public void illegalFormat() {\n        String data[] = new String[] {\n            \"memlock\",\n            \"memlock:1024\",\n        };\n\n        for (String test : data) {\n            try {\n                new UlimitConfig(test);\n                fail();\n            } catch (IllegalArgumentException exp) {\n                // expected\n            }\n        }\n    }\n\n    @Test\n    public void invalidNumber() {\n        String data[] = new String[] {\n            \"memlock=bla\",\n            \"memlock=bla:blub\",\n            \"memlock=1024:blub\"\n        };\n\n        for (String test : data) {\n            try {\n                new UlimitConfig(test);\n                fail();\n            } catch (NumberFormatException exp) {\n                // expected\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/AbstractConfigHandlerTest.java",
    "content": "package io.fabric8.maven.docker.config.handler;\n\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\n\npublic abstract class AbstractConfigHandlerTest {\n\n    protected List<String> a(String ... args) {\n        return Arrays.asList(args);\n    }\n\n    protected abstract String getEnvPropertyFile();\n    \n    protected abstract RunImageConfiguration.NamingStrategy getRunNamingStrategy();\n    \n    protected abstract void validateEnv(Map<String, String> env);\n    \n    protected void validateRunConfiguration(RunImageConfiguration runConfig) {\n        assertEquals(a(\"/foo\", \"/tmp:/tmp\"), runConfig.getVolumeConfiguration().getBind());\n        assertEquals(a(\"CAP\"), runConfig.getCapAdd());\n        assertEquals(a(\"CAP\"), runConfig.getCapDrop());\n        assertEquals(\"command.sh\", runConfig.getCmd().getShell());\n        assertEquals(a(\"8.8.8.8\"), runConfig.getDns());\n        assertEquals(a(\"example.com\"), runConfig.getDnsSearch());\n        assertEquals(\"domain.com\", runConfig.getDomainname());\n        assertEquals(\"entrypoint.sh\", runConfig.getEntrypoint().getShell());\n        assertEquals(a(\"localhost:127.0.0.1\"), runConfig.getExtraHosts());\n        assertEquals(\"subdomain\", runConfig.getHostname());\n        assertEquals(a(\"redis\"), runConfig.getLinks());\n        assertEquals((Long) 1L, runConfig.getMemory());\n        assertEquals((Long) 1L, runConfig.getMemorySwap());\n        assertEquals((Long) 1000000000L, runConfig.getCpus());\n        assertEquals((Long) 1L, runConfig.getCpuShares());\n        assertEquals(\"0,1\", runConfig.getCpuSet());\n        assertEquals(getEnvPropertyFile(),runConfig.getEnvPropertyFile());\n        \n        assertEquals(\"/tmp/props.txt\", runConfig.getPortPropertyFile());\n        assertEquals(a(\"8081:8080\"), runConfig.getPorts());\n        assertEquals(true, runConfig.getPrivileged());\n        assertEquals(\"tomcat\", runConfig.getUser());\n        assertEquals(a(\"from\"), runConfig.getVolumeConfiguration().getFrom());\n        assertEquals(\"foo\", runConfig.getWorkingDir());\n    \n        validateEnv(runConfig.getEnv());\n    \n        // not sure it's worth it to implement 'equals/hashcode' for these\n        RestartPolicy policy = runConfig.getRestartPolicy();\n        assertEquals(\"on-failure\", policy.getName());\n        assertEquals(1, policy.getRetry());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/ArchiveCompressionTest.java",
    "content": "package io.fabric8.maven.docker.config.handler;\n\nimport io.fabric8.maven.docker.config.ArchiveCompression;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ArchiveCompressionTest {\n\n    @Test\n    public void fromFileName() throws Exception {\n        ArchiveCompression c = ArchiveCompression.fromFileName(\"test.tar\");\n        assertEquals(\"tar\", c.getFileSuffix());\n\n        c = ArchiveCompression.fromFileName(\"test.tar.bzip2\");\n        assertEquals(\"tar.bz\", c.getFileSuffix());\n\n        c = ArchiveCompression.fromFileName(\"test.tar.bz2\");\n        assertEquals(\"tar.bz\", c.getFileSuffix());\n\n        c = ArchiveCompression.fromFileName(\"test.tar.gz\");\n        assertEquals(\"tar.gz\", c.getFileSuffix());\n\n        c = ArchiveCompression.fromFileName(\"test.tgz\");\n        assertEquals(\"tar.gz\", c.getFileSuffix());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/compose/ComposeUtilsTest.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport java.io.File;\n\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport mockit.VerificationsInOrder;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Test;\n\nimport static io.fabric8.maven.docker.util.PathTestUtil.DOT;\nimport static io.fabric8.maven.docker.util.PathTestUtil.SEP;\nimport static io.fabric8.maven.docker.util.PathTestUtil.createTmpFile;\nimport static io.fabric8.maven.docker.util.PathTestUtil.join;\nimport static org.junit.Assert.assertEquals;\n\n/**\n *\n */\n\npublic class ComposeUtilsTest {\n\n    private final String className = ComposeUtilsTest.class.getSimpleName();\n\n    private final String ABS_BASEDIR = createTmpFile(className).getAbsolutePath();\n\n    @Mocked\n    private MavenProject project;\n\n    @Test\n    public void resolveComposeFileWithAbsoluteComposeFile() throws Exception {\n        String absComposeFile = createTmpFile(className).getAbsolutePath() + SEP + \"docker-compose.yaml\";\n\n        assertEquals(new File(absComposeFile),\n                ComposeUtils.resolveComposeFileAbsolutely(null, absComposeFile, null));\n    }\n\n    @Test\n    public void resolveComposeFileWithRelativeComposeFileAndAbsoluteBaseDir() throws Exception {\n        String relComposeFile = join(SEP, \"relative\", \"path\", \"to\", \"docker-compose.yaml\");  // relative/path/to/docker-compose.yaml\n        final String absMavenProjectDir = createTmpFile(className).getAbsolutePath();\n\n        new Expectations() {{\n            project.getBasedir();\n            result = new File(absMavenProjectDir);\n        }};\n\n        assertEquals(new File(ABS_BASEDIR, relComposeFile),\n                ComposeUtils.resolveComposeFileAbsolutely(ABS_BASEDIR, relComposeFile, project));\n\n        new VerificationsInOrder() {{\n            project.getBasedir();\n        }};\n    }\n\n    @Test\n    public void resolveComposeFileWithRelativeComposeFileAndRelativeBaseDir() throws Exception {\n        String relComposeFile = join(SEP, \"relative\", \"path\", \"to\", \"docker-compose.yaml\");  // relative/path/to/docker-compose.yaml\n        String relBaseDir = \"basedir\" + SEP;\n        final String absMavenProjectDir = createTmpFile(className).getAbsolutePath();\n\n        new Expectations() {{\n            project.getBasedir();\n            result = new File(absMavenProjectDir);\n        }};\n\n        assertEquals(new File(new File(absMavenProjectDir, relBaseDir), relComposeFile),\n                ComposeUtils.resolveComposeFileAbsolutely(relBaseDir, relComposeFile, project));\n\n        new VerificationsInOrder() {{\n            project.getBasedir();\n        }};\n    }\n\n    @Test\n    public void resolveComposesFileWithRelativeComposeFileParentDirectory() throws Exception {\n        String relComposeFile = join(SEP, DOT + DOT, \"relative\", \"path\", \"to\", \"docker-compose.yaml\");  // ../relative/path/to/docker-compose.yaml\n        File tmpDir = createTmpFile(ComposeUtilsTest.class.getName());\n        String absBaseDir = tmpDir.getAbsolutePath();\n\n        assertEquals(new File(tmpDir.getParentFile(), relComposeFile.substring(3)),\n                ComposeUtils.resolveComposeFileAbsolutely(absBaseDir, relComposeFile, null));\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandlerTest.java",
    "content": "package io.fabric8.maven.docker.config.handler.compose;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.handler.ExternalConfigHandlerException;\nimport mockit.Expectations;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.filtering.MavenFilteringException;\nimport org.apache.maven.shared.filtering.MavenReaderFilter;\nimport org.apache.maven.shared.filtering.MavenReaderFilterRequest;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * @author roland\n * @since 28.08.17\n */\npublic class DockerComposeConfigHandlerTest {\n\n    @Injectable\n    ImageConfiguration unresolved;\n\n    @Mocked\n    MavenProject project;\n\n    @Mocked\n    MavenSession session;\n\n    @Mocked\n    MavenReaderFilter readerFilter;\n\n    private DockerComposeConfigHandler handler;\n\n    @Before\n    public void setUp() throws Exception {\n        handler = new DockerComposeConfigHandler();\n        handler.readerFilter = readerFilter;\n    }\n\n\n    @Test\n    public void simple() throws IOException, MavenFilteringException {\n        setupComposeExpectations(\"docker-compose.yml\");\n        List<ImageConfiguration> configs = handler.resolve(unresolved, project, session);\n        assertEquals(1, configs.size());\n        validateRunConfiguration(configs.get(0).getRunConfiguration());\n    }\n\n\n\t@Test\n\tpublic void networkAliases() throws IOException, MavenFilteringException {\n        setupComposeExpectations(\"docker-compose-network-aliases.yml\");\n        List<ImageConfiguration> configs = handler.resolve(unresolved, project, session);\n        \n        // Service 1 has 1 network (network1) with 2 aliases (alias1, alias2)\n        NetworkConfig netSvc = configs.get(0).getRunConfiguration().getNetworkingConfig();\n        assertEquals(\"network1\", netSvc.getName());\n        assertEquals(2, netSvc.getAliases().size());\n        assertEquals(\"alias1\", netSvc.getAliases().get(0));\n        assertEquals(\"alias2\", netSvc.getAliases().get(1));\n  \n        // Service 2 has 1 network (network1) with no aliases\n        netSvc = configs.get(1).getRunConfiguration().getNetworkingConfig();\n        assertEquals(\"network1\", netSvc.getName());\n        assertEquals(0, netSvc.getAliases().size());\n\n        // Service 3 has 1 network (network1) with 1 aliase (alias1)\n        netSvc = configs.get(2).getRunConfiguration().getNetworkingConfig();\n        assertEquals(\"network1\", netSvc.getName());\n        assertEquals(1, netSvc.getAliases().size());\n        assertEquals(\"alias1\", netSvc.getAliases().get(0));\n}\n\t\n    @Test\n    public void positiveVersionTest() throws IOException, MavenFilteringException {\n        for (String composeFile : new String[] { \"version/compose-version-2.yml\", \"version/compose-version-2x.yml\"} ) {\n            setupComposeExpectations(composeFile);\n            assertNotNull(handler.resolve(unresolved, project, session));\n        }\n\n    }\n\t\n    @Test\n    public void negativeVersionTest() throws IOException, MavenFilteringException {\n        for (String composeFile : new String[] { \"version/compose-wrong-version.yml\", \"version/compose-no-version.yml\"} ) {\n            try {\n                setupComposeExpectations(composeFile);\n                handler.resolve(unresolved, project, session);\n                fail();\n            } catch (ExternalConfigHandlerException exp) {\n                assertTrue(exp.getMessage().contains((\"2.x\")));\n            }\n        }\n\n    }\n\n    private void setupComposeExpectations(final String file) throws IOException, MavenFilteringException {\n        new Expectations() {{\n            final File input = getAsFile(\"/compose/\" + file);\n\n            unresolved.getExternalConfig();\n            result = new HashMap<String,String>() {{\n                put(\"composeFile\", input.getAbsolutePath());\n                // provide a base directory that actually exists, so that relative paths referenced by the\n                // docker-compose.yaml file can be resolved\n                // (note: this is different than the directory returned by 'input.getParent()')\n                URL baseResource = this.getClass().getResource(\"/\");\n                String baseDir = baseResource.getFile();\n                assertNotNull(\"Classpath resource '/' does not have a File: '\" + baseResource, baseDir);\n                assertTrue(\"Classpath resource '/' does not resolve to a File: '\" + new File(baseDir) + \"' does not exist.\", new File(baseDir).exists());\n                put(\"basedir\", baseDir);\n            }};\n\n            readerFilter.filter((MavenReaderFilterRequest) any);\n            result = new FileReader(input);\n        }};\n    }\n\n    private File getAsFile(String resource) throws IOException {\n        File tempFile = File.createTempFile(\"compose\",\".yml\");\n        InputStream is = getClass().getResourceAsStream(resource);\n        FileUtils.copyInputStreamToFile(is,tempFile);\n        return tempFile;\n    }\n\n\n     void validateRunConfiguration(RunImageConfiguration runConfig) {\n\n        validateVolumeConfig(runConfig.getVolumeConfiguration());\n\n        assertEquals(a(\"CAP\"), runConfig.getCapAdd());\n        assertEquals(a(\"CAP\"), runConfig.getCapDrop());\n        assertEquals(\"command.sh\", runConfig.getCmd().getShell());\n        assertEquals(a(\"8.8.8.8\"), runConfig.getDns());\n        assertEquals(a(\"example.com\"), runConfig.getDnsSearch());\n        assertEquals(\"domain.com\", runConfig.getDomainname());\n        assertEquals(\"entrypoint.sh\", runConfig.getEntrypoint().getShell());\n        assertEquals(a(\"localhost:127.0.0.1\"), runConfig.getExtraHosts());\n        assertEquals(\"subdomain\", runConfig.getHostname());\n        assertEquals(a(\"redis\",\"link1\"), runConfig.getLinks());\n        assertEquals((Long) 1L, runConfig.getMemory());\n        assertEquals((Long) 1L, runConfig.getMemorySwap());\n        assertEquals(\"0,1\", runConfig.getCpuSet());\n        assertEquals((Long)1000000000L, runConfig.getCpus());\n        assertEquals((Long) 1L, runConfig.getCpuShares());\n        assertEquals(null,runConfig.getEnvPropertyFile());\n\n        assertEquals(null, runConfig.getPortPropertyFile());\n        assertEquals(a(\"8081:8080\"), runConfig.getPorts());\n        assertEquals(true, runConfig.getPrivileged());\n        assertEquals(\"tomcat\", runConfig.getUser());\n        assertEquals(a(\"from\"), runConfig.getVolumeConfiguration().getFrom());\n        assertEquals(\"foo\", runConfig.getWorkingDir());\n\n        validateEnv(runConfig.getEnv());\n\n        // not sure it's worth it to implement 'equals/hashcode' for these\n        RestartPolicy policy = runConfig.getRestartPolicy();\n        assertEquals(\"on-failure\", policy.getName());\n        assertEquals(1, policy.getRetry());\n    }\n\n    /**\n     * Validates the {@link RunVolumeConfiguration} by asserting that:\n     * <ul>\n     *     <li>absolute host paths remain absolute</li>\n     *     <li>access controls are preserved</li>\n     *     <li>relative host paths are resolved to absolute paths correctly</li>\n     * </ul>\n     * @param toValidate the {@code RunVolumeConfiguration} being validated\n     */\n    void validateVolumeConfig(RunVolumeConfiguration toValidate) {\n        final int expectedBindCnt = 4;\n        final List<String> binds = toValidate.getBind();\n        assertEquals(\"Expected \" + expectedBindCnt + \" bind statements\", expectedBindCnt, binds.size());\n\n        assertEquals(a(\"/foo\", \"/tmp:/tmp:rw\", \"namedvolume:/volume:ro\"), binds.subList(0, expectedBindCnt - 1));\n\n        // The docker-compose.yml used for testing contains a volume binding string that uses relative paths in the\n        // host portion.  Insure that the relative portion has been resolved properly.\n        String relativeBindString = binds.get(expectedBindCnt - 1);\n        assertHostBindingExists(relativeBindString);\n    }\n\n    /**\n     * Parses the supplied binding string for the host portion, and insures the host portion actually exists on the\n     * filesystem.  Note this method is designed to accommodate both Windows-style and *nix-style absolute paths.\n     * <p>\n     * The {@code docker-compose.yml} used for testing contains volume binding strings which are <em>relative</em>.\n     * When the {@link RunVolumeConfiguration} is built, relative paths in the host portion of the binding string are\n     * resolved to absolute paths.  This method expects a binding string that has already had its relative paths\n     * <em>resolved</em> to absolute paths.  It parses the host portion of the binding string, and asserts that the path\n     * exists on the system.\n     * </p>\n     *\n     *\n     * @param bindString a volume binding string that contains a host portion that is expected to exist on the local\n     *                   system\n     */\n    private void assertHostBindingExists(String bindString) {\n//        System.err.println(\">>>> \" + bindString);\n\n        // Extract the host-portion of the volume binding string, accounting for windows platform paths and unix style\n        // paths.  For example:\n        // C:\\Users\\foo\\Documents\\workspaces\\docker-maven-plugin\\target\\test-classes\\compose\\version:/tmp/version\n        // and\n        // /Users/foo/workspaces/docker-maven-plugin/target/test-classes/compose/version:/tmp/version\n\n        File file = null;\n        if (bindString.indexOf(\":\") > 1) {\n            // a unix-style path\n            file = new File(bindString.substring(0, bindString.indexOf(\":\")));\n        } else {\n            // a windows-style path with a drive letter\n            file = new File(bindString.substring(0, bindString.indexOf(\":\", 2)));\n        }\n        assertTrue(\"The file '\" + file + \"' parsed from the volume binding string '\" + bindString + \"' does not exist!\", file.exists());\n    }\n\n    protected void validateEnv(Map<String, String> env) {\n        assertEquals(2, env.size());\n        assertEquals(\"name\", env.get(\"NAME\"));\n        assertEquals(\"true\", env.get(\"BOOL\"));\n    }\n\n    protected List<String> a(String ... args) {\n        return Arrays.asList(args);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.CleanupMode;\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.UlimitConfig;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.config.handler.AbstractConfigHandlerTest;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static io.fabric8.maven.docker.config.BuildImageConfiguration.DEFAULT_CLEANUP;\nimport static io.fabric8.maven.docker.config.BuildImageConfiguration.DEFAULT_FILTER;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author roland\n * @since 05/12/14\n */\n\npublic class PropertyConfigHandlerTest extends AbstractConfigHandlerTest {\n\n    private PropertyConfigHandler configHandler;\n    private ImageConfiguration imageConfiguration;\n\n    @Mocked\n    private MavenProject project;\n\n    @Before\n    public void setUp() throws Exception {\n        configHandler = new PropertyConfigHandler();\n        imageConfiguration = buildAnUnresolvedImage();\n    }\n\n    @Test\n    public void testSkipBuild() {\n        assertFalse(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_BUILD, false)).getBuildConfiguration().skip());\n        assertTrue(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_BUILD, true)).getBuildConfiguration().skip());\n\n        assertFalse(resolveExternalImageConfig(new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"busybox\"}).getBuildConfiguration().skip());\n    }\n\n    @Test\n    public void testSkipPush() {\n        assertFalse(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_PUSH, false)).getBuildConfiguration().skipPush());\n        assertTrue(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_PUSH, true)).getBuildConfiguration().skipPush());\n\n        assertFalse(resolveExternalImageConfig(new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"busybox\"}).getBuildConfiguration().skipPush());\n    }\n\n    @Test\n    public void testSkipRun() {\n        assertFalse(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_RUN, false)).getRunConfiguration().skip());\n        assertTrue(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_RUN, true)).getRunConfiguration().skip());\n\n        assertFalse(resolveExternalImageConfig(new String[] {k(ConfigKey.NAME), \"image\"}).getRunConfiguration().skip());\n    }\n\n    @Test\n    public void testType() throws Exception {\n        assertNotNull(configHandler.getType());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testEmpty() throws Exception {\n        resolveImage(imageConfiguration, props());\n    }\n\n    @Test\n    public void testPorts() {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.name\",\"demo\",\n                        \"docker.ports.1\", \"jolokia.port:8080\",\n                        \"docker.ports.2\", \"9090\",\n                        \"docker.ports.3\", \"0.0.0.0:80:80\",\n                        \"docker.from\", \"busybox\"\n                                        ));\n        assertEquals(1,configs.size());\n        RunImageConfiguration runConfig = configs.get(0).getRunConfiguration();\n        List<String> portsAsList = runConfig.getPorts();\n        String[] ports = new ArrayList<>(portsAsList).toArray(new String[portsAsList.size()]);\n        assertArrayEquals(new String[] {\n                \"jolokia.port:8080\",\n                \"9090\",\n                \"0.0.0.0:80:80\"\n        },ports);\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        ports = new ArrayList<>(buildConfig.getPorts()).toArray(new String[buildConfig.getPorts().size()]);\n        assertArrayEquals(new String[]{\"8080\", \"9090\", \"80\"}, ports);\n    }\n\n\n    @Test\n    public void testPortsFromConfigAndProperties() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<String, String>())\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .ports(Arrays.asList(\"1234\"))\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .runConfig(new RunImageConfiguration.Builder()\n                    .ports(Arrays.asList(\"jolokia.port:1234\"))\n                    .build()\n                )\n                .build();\n\n        makeExternalConfigUse(PropertyMode.Override);\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.name\",\"demo\",\n                        \"docker.ports.1\", \"9090\",\n                        \"docker.ports.2\", \"0.0.0.0:80:80\",\n                        \"docker.from\", \"busybox\"\n                ));\n        assertEquals(1,configs.size());\n        RunImageConfiguration runConfig = configs.get(0).getRunConfiguration();\n        List<String> portsAsList = runConfig.getPorts();\n        String[] ports = new ArrayList<>(portsAsList).toArray(new String[portsAsList.size()]);\n        assertArrayEquals(new String[] {\n                \"9090\",\n                \"0.0.0.0:80:80\",\n                \"jolokia.port:1234\"\n        },ports);\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        ports = new ArrayList<>(buildConfig.getPorts()).toArray(new String[buildConfig.getPorts().size()]);\n        assertArrayEquals(new String[]{\"9090\", \"80\", \"1234\"}, ports);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidPropertyMode() {\n        makeExternalConfigUse(PropertyMode.Override);\n        imageConfiguration.getExternalConfig().put(\"mode\", \"invalid\");\n\n        resolveImage(imageConfiguration,props());\n    }\n\n    @Test\n    public void testRunCommands() {\n        List<ImageConfiguration> configs = resolveImage(\n            imageConfiguration,props(\n                \"docker.from\", \"base\",\n                \"docker.name\",\"demo\",\n                \"docker.run.1\", \"foo\",\n                \"docker.run.2\", \"bar\",\n                \"docker.run.3\", \"wibble\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        String[] runCommands = new ArrayList<>(buildConfig.getRunCmds()).toArray(new String[buildConfig.getRunCmds().size()]);\n        assertArrayEquals(new String[]{\"foo\", \"bar\", \"wibble\"}, runCommands);\n    }\n\n    @Test\n    public void testShell() {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.shell\", \"/bin/sh -c\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        String[] shell = new ArrayList<>(buildConfig.getShell().asStrings()).toArray(new String[buildConfig.getShell().asStrings().size()]);\n        assertArrayEquals(new String[]{\"/bin/sh\", \"-c\"}, shell);\n    }\n\n    @Test\n    public void testRunCommandsFromPropertiesAndConfig() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<String, String>())\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .runCmds(Arrays.asList(\"some\",\"ignored\",\"value\"))\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        makeExternalConfigUse(PropertyMode.Override);\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.run.1\", \"propconf\",\n                        \"docker.run.2\", \"withrun\",\n                        \"docker.run.3\", \"used\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        String[] runCommands = new ArrayList<>(buildConfig.getRunCmds()).toArray(new String[buildConfig.getRunCmds().size()]);\n        assertArrayEquals(new String[]{\"propconf\", \"withrun\", \"used\"}, runCommands);\n    }\n\n    @Test\n    public void testShellFromPropertiesAndConfig() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<String, String>())\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .shell(new Arguments(Arrays.asList(\"some\",\"ignored\",\"value\")))\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        makeExternalConfigUse(PropertyMode.Override);\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.shell\", \"propconf withrun used\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        String[] shell = new ArrayList<>(buildConfig.getShell().asStrings()).toArray(new String[buildConfig.getShell().asStrings().size()]);\n        assertArrayEquals(new String[]{\"propconf\", \"withrun\", \"used\"}, shell);\n    }\n\n    @Test\n    public void testRunCommandsFromConfigAndProperties() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Fallback))\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .runCmds(Arrays.asList(\"some\",\"configured\",\"value\"))\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.run.1\", \"this\",\n                        \"docker.run.2\", \"is\",\n                        \"docker.run.3\", \"ignored\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        String[] runCommands = new ArrayList<>(buildConfig.getRunCmds()).toArray(new String[buildConfig.getRunCmds().size()]);\n        assertArrayEquals(new String[]{\"some\", \"configured\", \"value\"}, runCommands);\n    }\n\n    @Test\n    public void testEntrypoint() {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.entrypoint\", \"/entrypoint.sh --from-property\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        assertArrayEquals(new String[]{\"/entrypoint.sh\", \"--from-property\"}, buildConfig.getEntryPoint().asStrings().toArray());\n    }\n\n    @Test\n    public void testEntrypointExecFromConfig() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Fallback))\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .entryPoint(new Arguments(Arrays.asList(\"/entrypoint.sh\", \"--from-property\")))\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\")\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfig = configs.get(0).getBuildConfiguration();\n        assertArrayEquals(new String[]{\"/entrypoint.sh\", \"--from-property\"}, buildConfig.getEntryPoint().asStrings().toArray());\n    }\n\n    @Test\n    public void testDefaultLogEnabledConfiguration() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\")\n        );\n\n        assertEquals(1, configs.size());\n\n        RunImageConfiguration runConfiguration = configs.get(0).getRunConfiguration();\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertFalse(runConfiguration.getLogConfiguration().isActivated());\n\n        // If any log property is set, enabled shall be true by default\n        configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\",\n                        \"docker.log.color\", \"green\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"green\", runConfiguration.getLogConfiguration().getColor());\n\n\n        // If image configuration has non-blank log configuration, it should become enabled\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .runConfig(new RunImageConfiguration.Builder()\n                        .log(new LogConfiguration.Builder().color(\"red\").build())\n                        .build()\n                )\n                .build();\n\n        configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n\n\n        // and if set by property, still enabled but overrides\n        configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\",\n                        \"docker.log.color\", \"yellow\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"yellow\", runConfiguration.getLogConfiguration().getColor());\n\n\n        // Fallback works as well\n        makeExternalConfigUse(PropertyMode.Fallback);\n        configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\",\n                        \"docker.log.color\", \"yellow\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n    }\n\n    @Test\n    public void testExplicitLogEnabledConfiguration() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .runConfig(new RunImageConfiguration.Builder()\n                        .log(new LogConfiguration.Builder().color(\"red\").build())\n                        .build()\n                )\n                .build();\n\n        // Explicitly enabled\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\",\n                        \"docker.log.enabled\", \"true\")\n        );\n\n        RunImageConfiguration runConfiguration = getRunImageConfiguration(configs);\n        assertTrue(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n\n        // Explicitly disabled\n        makeExternalConfigUse(PropertyMode.Override);\n        configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.log.color\", \"yellow\",\n                        \"docker.log.enabled\", \"false\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertFalse(runConfiguration.getLogConfiguration().isEnabled());\n        assertFalse(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"yellow\", runConfiguration.getLogConfiguration().getColor());\n\n\n        // Disabled by config\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Fallback))\n                .runConfig(new RunImageConfiguration.Builder()\n                        .log(new LogConfiguration.Builder().enabled(false).color(\"red\").build())\n                        .build()\n                )\n                .build();\n\n        configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertFalse(runConfiguration.getLogConfiguration().isEnabled());\n        assertFalse(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n\n        // Enabled by property, with override\n        makeExternalConfigUse(PropertyMode.Override);\n        configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.log.enabled\", \"true\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertTrue(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n\n        // Disabled with property too\n        configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\",\"demo\",\n                        \"docker.log.enabled\", \"false\")\n        );\n\n        runConfiguration = getRunImageConfiguration(configs);\n        assertFalse(runConfiguration.getLogConfiguration().isEnabled());\n        assertFalse(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"red\", runConfiguration.getLogConfiguration().getColor());\n    }\n\n    @Test\n    public void testLogFile() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .runConfig(new RunImageConfiguration.Builder()\n                        .log(new LogConfiguration.Builder().file(\"myfile\").build())\n                        .build()\n                )\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\")\n        );\n\n        assertEquals(1, configs.size());\n\n        RunImageConfiguration runConfiguration = configs.get(0).getRunConfiguration();\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"myfile\", runConfiguration.getLogConfiguration().getFileLocation());\n\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .runConfig(new RunImageConfiguration.Builder()\n                        .build()\n                )\n                .build();\n\n        configs = resolveImage(\n                imageConfiguration, props(\n                        \"docker.from\", \"base\",\n                        \"docker.name\", \"demo\",\n                        \"docker.log.file\", \"myfilefromprop\")\n        );\n\n        assertEquals(1, configs.size());\n\n        runConfiguration = configs.get(0).getRunConfiguration();\n        assertNull(runConfiguration.getLogConfiguration().isEnabled());\n        assertTrue(runConfiguration.getLogConfiguration().isActivated());\n        assertEquals(\"myfilefromprop\", runConfiguration.getLogConfiguration().getFileLocation());\n    }\n\n    private RunImageConfiguration getRunImageConfiguration(List<ImageConfiguration> configs) {\n        assertEquals(1, configs.size());\n        return configs.get(0).getRunConfiguration();\n    }\n\n    @Test\n    public void testBuildFromDockerFileMerged() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .name(\"myimage\")\n                .externalConfig(externalConfigMode(PropertyMode.Override))\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .dockerFile(\"/some/path\")\n                        .cacheFrom((Arrays.asList(\"foo/bar:latest\")))\n                        .build()\n                )\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration, props()\n        );\n\n        assertEquals(1, configs.size());\n\n        BuildImageConfiguration buildConfiguration = configs.get(0).getBuildConfiguration();\n        assertNotNull(buildConfiguration);\n        buildConfiguration.initAndValidate(null);\n\n        Path absolutePath = Paths.get(\".\").toAbsolutePath();\n        String expectedPath = absolutePath.getRoot() + \"some\" + File.separator + \"path\";\n        assertEquals(expectedPath, buildConfiguration.getDockerFile().getAbsolutePath());\n    }\n\n    @Test\n    public void testEnvAndLabels() throws Exception {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                    \"docker.from\", \"baase\",\n                        \"docker.name\",\"demo\",\n                        \"docker.env.HOME\", \"/tmp\",\n                        \"docker.env.root.dir\", \"/bla\",\n                        \"docker.labels.version\", \"1.0.0\",\n                        \"docker.labels.blub.bla.foobar\", \"yep\"\n                                        ));\n\n        assertEquals(1,configs.size());\n        ImageConfiguration calcConfig = configs.get(0);\n        for (Map env : new Map[] { calcConfig.getBuildConfiguration().getEnv(),\n                                   calcConfig.getRunConfiguration().getEnv()}) {\n            assertEquals(2,env.size());\n            assertEquals(\"/tmp\",env.get(\"HOME\"));\n            assertEquals(\"/bla\",env.get(\"root.dir\"));\n        }\n        for (Map labels : new Map[] { calcConfig.getBuildConfiguration().getLabels(),\n                                      calcConfig.getRunConfiguration().getLabels()}) {\n            assertEquals(2, labels.size());\n            assertEquals(\"1.0.0\", labels.get(\"version\"));\n            assertEquals(\"yep\", labels.get(\"blub.bla.foobar\"));\n        }\n    }\n\n\n    @Test\n    public void testSpecificEnv() throws Exception {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"baase\",\n                        \"docker.name\",\"demo\",\n                        \"docker.envBuild.HOME\", \"/tmp\",\n                        \"docker.envRun.root.dir\", \"/bla\"\n                ));\n\n        assertEquals(1,configs.size());\n        ImageConfiguration calcConfig = configs.get(0);\n\n        Map<String, String> env;\n\n        env = calcConfig.getBuildConfiguration().getEnv();\n        assertEquals(1,env.size());\n        assertEquals(\"/tmp\",env.get(\"HOME\"));\n\n        env = calcConfig.getRunConfiguration().getEnv();\n        assertEquals(1,env.size());\n        assertEquals(\"/bla\",env.get(\"root.dir\"));\n    }\n\n    @Test\n    public void testMergedEnv() throws Exception {\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.from\", \"baase\",\n                        \"docker.name\",\"demo\",\n                        \"docker.env.HOME\", \"/tmp\",\n                        \"docker.envBuild.HOME\", \"/var/tmp\",\n                        \"docker.envRun.root.dir\", \"/bla\"\n                ));\n\n        assertEquals(1,configs.size());\n        ImageConfiguration calcConfig = configs.get(0);\n\n        Map<String, String> env;\n\n        env = calcConfig.getBuildConfiguration().getEnv();\n        assertEquals(1,env.size());\n        assertEquals(\"/var/tmp\",env.get(\"HOME\"));\n\n        env = calcConfig.getRunConfiguration().getEnv();\n        assertEquals(2,env.size());\n        assertEquals(\"/tmp\",env.get(\"HOME\"));\n        assertEquals(\"/bla\",env.get(\"root.dir\"));\n    }\n\n    @Test\n    public void testAssembly() throws Exception {\n        List<ImageConfiguration> configs = resolveImage(imageConfiguration, props(getTestAssemblyData()));\n        assertEquals(1, configs.size());\n\n        AssemblyConfiguration config = configs.get(0).getBuildConfiguration().getAssemblyConfiguration();\n        assertEquals(\"user\", config.getUser());\n        assertEquals(\"project\", config.getDescriptorRef());\n        assertFalse(config.exportTargetDir());\n        assertTrue(config.isIgnorePermissions());\n    }\n\n    @Test\n    public void testNoCleanup() throws Exception {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.CLEANUP), \"none\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(CleanupMode.NONE, config.getBuildConfiguration().cleanupMode());\n    }\n\n    @Test\n    public void testNoBuildConfig() throws Exception {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertNull(config.getBuildConfiguration());\n    }\n\n    @Test\n    public void testDockerfile() throws Exception {\n        String[] testData = new String[] { k(ConfigKey.NAME), \"image\", k(ConfigKey.DOCKER_FILE_DIR), \"src/main/docker/\", k(ConfigKey.FROM), \"busybox\" };\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        config.initAndValidate(ConfigHelper.NameFormatter.IDENTITY, null);\n        assertTrue(config.getBuildConfiguration().isDockerFileMode());\n        assertEquals(new File(\"src/main/docker/Dockerfile\"), config.getBuildConfiguration().getDockerFile());\n    }\n\n    @Test\n    public void testDockerArchive() {\n        String[] testData = new String[] { k(ConfigKey.NAME), \"image\", k(ConfigKey.DOCKER_ARCHIVE), \"dockerLoad.tar\", k(ConfigKey.FROM), \"busybox\" };\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        config.initAndValidate(ConfigHelper.NameFormatter.IDENTITY, null);\n        assertFalse(config.getBuildConfiguration().isDockerFileMode());\n        assertEquals(new File(\"dockerLoad.tar\"), config.getBuildConfiguration().getDockerArchive());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidDockerFileArchiveConfig() {\n        String[] testData = new String[] { k(ConfigKey.NAME), \"image\", k(ConfigKey.DOCKER_FILE_DIR),  \"src/main/docker/\", k(ConfigKey.DOCKER_ARCHIVE), \"dockerLoad.tar\", k(ConfigKey.FROM), \"base\" };\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        config.initAndValidate(ConfigHelper.NameFormatter.IDENTITY, null);\n    }\n\n    @Test\n    public void testNoCacheDisabled() throws Exception {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.NO_CACHE), \"false\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(false, config.getBuildConfiguration().noCache());\n    }\n\n    @Test\n    public void testNoCacheEnabled() throws Exception {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.NO_CACHE), \"true\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(true, config.getBuildConfiguration().noCache());\n    }\n\n    @Test\n    public void testCacheFrom() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.CACHE_FROM), \"foo/bar:latest\", k(ConfigKey.FROM), \"base\"};\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(Collections.singletonList(\"foo/bar:latest\"), config.getBuildConfiguration().getCacheFrom());\n    }\n\n    @Test\n    public void testCacheFromIsNullInBuildConfig() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<>())\n                .buildConfig(new BuildImageConfiguration.Builder().build())\n                .build();\n\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        \"docker.name\",\"demo\",\n                        \"docker.from\", \"busybox\"\n                ));\n\n        assertNull(configs.get(0).getBuildConfiguration().getCacheFrom().get(0));\n    }\n\n    @Test\n    public void testNoOptimise() throws Exception {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.OPTIMISE), \"false\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(false, config.getBuildConfiguration().optimise());\n    }\n\n    @Test\n    public void testDockerFile() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.DOCKER_FILE), \"file\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertNotNull(config.getBuildConfiguration());\n    }\n\n    @Test\n    public void testDockerFileDir() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.DOCKER_FILE_DIR), \"dir\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertNotNull(config.getBuildConfiguration());\n    }\n\n    @Test\n    public void testContextDir() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.CONTEXT_DIR), \"dir\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertNotNull(config.getBuildConfiguration());\n    }\n\n    @Test\n    public void testFilterDefault() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(DEFAULT_FILTER, config.getBuildConfiguration().getFilter());\n    }\n\n    @Test\n    public void testFilter() {\n        String filter = \"@\";\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"base\", k(ConfigKey.FILTER), filter };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(filter, config.getBuildConfiguration().getFilter());\n    }\n\n    @Test\n    public void testCleanupDefault() {\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"base\" };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(DEFAULT_CLEANUP, config.getBuildConfiguration().cleanupMode().toParameter());\n    }\n\n    @Test\n    public void testCleanup() {\n        CleanupMode mode = CleanupMode.REMOVE;\n        String[] testData = new String[] {k(ConfigKey.NAME), \"image\", k(ConfigKey.FROM), \"base\", k(ConfigKey.CLEANUP), mode.toParameter() };\n\n        ImageConfiguration config = resolveExternalImageConfig(testData);\n        assertEquals(mode, config.getBuildConfiguration().cleanupMode());\n    }\n\n\n    @Test\n    public void testUlimit() {\n        imageConfiguration = new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<String, String>())\n                .runConfig(new RunImageConfiguration.Builder()\n                        .ulimits(Arrays.asList(\n                                new UlimitConfig(\"memlock\", 100, 50),\n                                new UlimitConfig(\"nfile\", 1024, 512)\n                        ))\n                        .build()\n                )\n                .build();\n\n        makeExternalConfigUse(PropertyMode.Override);\n\n        // TODO: Does Replace make sense here or should we Merge?\n        // If merge, it should probably have some more smarts on the ulimit name?\n        List<ImageConfiguration> configs = resolveImage(\n                imageConfiguration,props(\n                        k(ConfigKey.NAME), \"image\",\n                        k(ConfigKey.FROM), \"base\",\n                        k(ConfigKey.ULIMITS)+\".1\", \"memlock=10:10\",\n                        k(ConfigKey.ULIMITS)+\".2\", \"memlock=:-1\",\n                        k(ConfigKey.ULIMITS)+\".3\", \"memlock=1024:\",\n                        k(ConfigKey.ULIMITS)+\".4\", \"memlock=2048\"\n                ));\n\n        assertEquals(1,configs.size());\n        RunImageConfiguration runConfig = configs.get(0).getRunConfiguration();\n        List<UlimitConfig> ulimits = runConfig.getUlimits();\n\n        assertEquals(4, ulimits.size());\n        assertUlimitEquals(ulimit(\"memlock\",10,10),runConfig.getUlimits().get(0));\n        assertUlimitEquals(ulimit(\"memlock\",null,-1),runConfig.getUlimits().get(1));\n        assertUlimitEquals(ulimit(\"memlock\",1024,null),runConfig.getUlimits().get(2));\n        assertUlimitEquals(ulimit(\"memlock\",2048,null),runConfig.getUlimits().get(3));\n    }\n\n    @Test\n    public void testNoAssembly() throws Exception {\n        Properties props = props(k(ConfigKey.NAME), \"image\");\n        //List<ImageConfiguration> configs = configHandler.resolve(imageConfiguration, props);\n        //assertEquals(1, configs.size());\n\n        //AssemblyConfiguration config = configs.get(0).getBuildConfiguration().getAssemblyConfiguration();\n        //assertNull(config);\n    }\n\n    @Test\n    public void testResolve() {\n        ImageConfiguration resolved = resolveExternalImageConfig(getTestData());\n\n        validateBuildConfiguration(resolved.getBuildConfiguration());\n        validateRunConfiguration(resolved.getRunConfiguration());\n        //validateWaitConfiguraion(resolved.getRunConfiguration().getWaitConfiguration());\n    }\n\n    @Override\n    protected String getEnvPropertyFile() {\n        return \"/tmp/envProps.txt\";\n    }\n\n    @Override\n    protected RunImageConfiguration.NamingStrategy getRunNamingStrategy() {\n        return RunImageConfiguration.NamingStrategy.none;\n    }\n\n    @Override\n    protected void validateEnv(Map<String, String> env) {\n        assertTrue(env.containsKey(\"HOME\"));\n        assertEquals(\"/Users/roland\", env.get(\"HOME\"));\n    }\n\n    private ImageConfiguration buildAnUnresolvedImage() {\n        return new ImageConfiguration.Builder()\n                .externalConfig(new HashMap<String, String>())\n                .build();\n    }\n\n    private Map<String, String> externalConfigMode(PropertyMode mode) {\n        Map<String, String> externalConfig = new HashMap<>();\n        if(mode != null) {\n            externalConfig.put(\"type\", \"properties\");\n            externalConfig.put(\"mode\", mode.name());\n        }\n        return externalConfig;\n    }\n\n    private void makeExternalConfigUse(PropertyMode mode) {\n        Map<String, String> externalConfig = imageConfiguration.getExternalConfig();\n        externalConfig.put(\"type\", \"properties\");\n        if(mode != null) {\n            externalConfig.put(\"mode\", mode.name());\n        } else {\n            externalConfig.remove(\"mode\");\n        }\n    }\n\n    private List<ImageConfiguration> resolveImage(ImageConfiguration image, final Properties properties) {\n        //MavenProject project = mock(MavenProject.class);\n        //when(project.getProperties()).thenReturn(properties);\n        new Expectations() {{\n            project.getProperties(); result = properties;\n            project.getBasedir(); minTimes = 0; maxTimes = 1; result = new File(\"./\");\n        }};\n\n        return configHandler.resolve(image, project, null);\n    }\n\n    private ImageConfiguration resolveExternalImageConfig(String[] testData) {\n        Map<String, String> external = new HashMap<>();\n        external.put(\"type\", \"props\");\n\n        ImageConfiguration config = new ImageConfiguration.Builder().name(\"image\").alias(\"alias\").externalConfig(external).build();\n\n        List<ImageConfiguration> resolvedImageConfigs = resolveImage(config, props(testData));\n        assertEquals(1, resolvedImageConfigs.size());\n\n        return resolvedImageConfigs.get(0);\n    }\n\n    private void validateBuildConfiguration(BuildImageConfiguration buildConfig) {\n        assertEquals(CleanupMode.TRY_TO_REMOVE, buildConfig.cleanupMode());\n        assertEquals(\"command.sh\", buildConfig.getCmd().getShell());\n        assertEquals(\"image\", buildConfig.getFrom());\n        assertEquals(\"image-ext\", buildConfig.getFromExt().get(\"name\"));\n        assertEquals(a(\"8080\"), buildConfig.getPorts());\n        assertEquals(\"registry\", buildConfig.getRegistry());\n        assertEquals(a(\"/foo\"), buildConfig.getVolumes());\n        assertEquals(\"fabric8io@redhat.com\",buildConfig.getMaintainer());\n        assertEquals(false, buildConfig.noCache());\n        assertEquals(\"Always\", buildConfig.getImagePullPolicy());\n\n        validateEnv(buildConfig.getEnv());\n        validateLabels(buildConfig.getLabels());\n        validateArgs(buildConfig.getArgs());\n        validateBuildOptions(buildConfig.getBuildOptions());\n        /*\n         * validate only the descriptor is required and defaults are all used, 'testAssembly' validates\n         * all options can be set\n         */\n        AssemblyConfiguration assemblyConfig = buildConfig.getAssemblyConfiguration();\n\n        assertEquals(\"/maven\", assemblyConfig.getTargetDir());\n        assertEquals(\"assembly.xml\", assemblyConfig.getDescriptor());\n        assertNull(assemblyConfig.getUser());\n        assertNull(assemblyConfig.exportTargetDir());\n        assertFalse(assemblyConfig.isIgnorePermissions());\n    }\n\n    private void validateArgs(Map<String, String> args) {\n        assertEquals(\"http://proxy\",args.get(\"PROXY\"));\n    }\n\n    private void validateLabels(Map<String, String> labels) {\n        assertEquals(\"Hello\\\"World\",labels.get(\"com.acme.label\"));\n    }\n\n    private void validateBuildOptions(Map<String,String> buildOptions) {\n        assertEquals(\"2147483648\", buildOptions.get(\"shmsize\"));\n    }\n\n    protected void validateRunConfiguration(RunImageConfiguration runConfig) {\n        assertEquals(a(\"/foo\", \"/tmp:/tmp\"), runConfig.getVolumeConfiguration().getBind());\n        assertEquals(a(\"CAP\"), runConfig.getCapAdd());\n        assertEquals(a(\"CAP\"), runConfig.getCapDrop());\n        assertEquals(a(\"seccomp=unconfined\"), runConfig.getSecurityOpts());\n        assertEquals(\"command.sh\", runConfig.getCmd().getShell());\n        assertEquals(a(\"8.8.8.8\"), runConfig.getDns());\n        assertEquals(\"host\",runConfig.getNetworkingConfig().getStandardMode(null));\n        assertEquals(a(\"example.com\"), runConfig.getDnsSearch());\n        assertEquals(\"domain.com\", runConfig.getDomainname());\n        assertEquals(\"entrypoint.sh\", runConfig.getEntrypoint().getShell());\n        assertEquals(a(\"localhost:127.0.0.1\"), runConfig.getExtraHosts());\n        assertEquals(\"subdomain\", runConfig.getHostname());\n        assertEquals(a(\"redis\"), runConfig.getLinks());\n        assertEquals((Long) 1L, runConfig.getMemory());\n        assertEquals((Long) 1L, runConfig.getMemorySwap());\n        assertEquals((Long) 1000000000L, runConfig.getCpus());\n        assertEquals((Long) 1L, runConfig.getCpuShares());\n        assertEquals(\"0,1\", runConfig.getCpuSet());\n        assertEquals(\"/tmp/envProps.txt\",runConfig.getEnvPropertyFile());\n        assertEquals(\"/tmp/props.txt\", runConfig.getPortPropertyFile());\n        assertEquals(a(\"8081:8080\"), runConfig.getPorts());\n        assertEquals(true, runConfig.getPrivileged());\n        assertEquals(\"tomcat\", runConfig.getUser());\n        assertEquals(a(\"from\"), runConfig.getVolumeConfiguration().getFrom());\n        assertEquals(\"foo\", runConfig.getWorkingDir());\n        assertNotNull( runConfig.getUlimits());\n        assertEquals(4, runConfig.getUlimits().size());\n        assertUlimitEquals(ulimit(\"memlock\",10,10),runConfig.getUlimits().get(0));\n        assertUlimitEquals(ulimit(\"memlock\",null,-1),runConfig.getUlimits().get(1));\n        assertUlimitEquals(ulimit(\"memlock\",1024,null),runConfig.getUlimits().get(2));\n        assertUlimitEquals(ulimit(\"memlock\",2048,null),runConfig.getUlimits().get(3));\n        assertEquals(\"/var/lib/mysql:10m\", runConfig.getTmpfs().get(0));\n        assertEquals(1, runConfig.getTmpfs().size());\n        assertEquals(\"Never\", runConfig.getImagePullPolicy());\n        assertEquals(true, runConfig.getReadOnly());\n        assertEquals(true, runConfig.getAutoRemove());\n\n        validateEnv(runConfig.getEnv());\n\n        // not sure it's worth it to implement 'equals/hashcode' for these\n        RestartPolicy policy = runConfig.getRestartPolicy();\n        assertEquals(\"on-failure\", policy.getName());\n        assertEquals(1, policy.getRetry());\n\n        WaitConfiguration wait = runConfig.getWaitConfiguration();\n        assertEquals(\"http://foo.com\", wait.getUrl());\n        assertEquals(\"pattern\", wait.getLog());\n        assertEquals(\"post_start_command\", wait.getExec().getPostStart());\n        assertEquals(\"pre_stop_command\", wait.getExec().getPreStop());\n        assertTrue(wait.getExec().isBreakOnError());\n        assertEquals(5, wait.getTime().intValue());\n        assertTrue(wait.getHealthy());\n        assertEquals(0, wait.getExit().intValue());\n\n        LogConfiguration config = runConfig.getLogConfiguration();\n        assertEquals(\"green\", config.getColor());\n        assertTrue(config.isEnabled());\n        assertEquals(\"SRV\", config.getPrefix());\n        assertEquals(\"iso8601\", config.getDate());\n        assertEquals(\"json\",config.getDriver().getName());\n        assertEquals(2, config.getDriver().getOpts().size());\n        assertEquals(\"1024\", config.getDriver().getOpts().get(\"max-size\"));\n        assertEquals(\"10\", config.getDriver().getOpts().get(\"max-file\"));\n    }\n\n    private UlimitConfig ulimit(String name, Integer hard, Integer soft) {\n        return new UlimitConfig(name, hard, soft);\n    }\n\n    private Properties props(String ... args) {\n        Properties ret = new Properties();\n        for (int i = 0; i < args.length; i += 2) {\n            ret.setProperty(args[i], args[i + 1]);\n        }\n        return ret;\n    }\n\n    private String[] getTestAssemblyData() {\n        return new String[] {\n            k(ConfigKey.FROM), \"busybox\",\n            k(ConfigKey.ASSEMBLY_BASEDIR), \"/basedir\",\n            k(ConfigKey.ASSEMBLY_DESCRIPTOR_REF), \"project\",\n            k(ConfigKey.ASSEMBLY_EXPORT_BASEDIR), \"false\",\n            k(ConfigKey.ASSEMBLY_IGNORE_PERMISSIONS), \"true\",\n            k(ConfigKey.ASSEMBLY_USER), \"user\",\n            k(ConfigKey.NAME), \"image\",\n            };\n    }\n\n    private String[] getTestData() {\n        return new String[] {\n            k(ConfigKey.ALIAS), \"alias\",\n            k(ConfigKey.ASSEMBLY_DESCRIPTOR), \"assembly.xml\",\n            k(ConfigKey.BIND) + \".1\", \"/foo\",\n            k(ConfigKey.BIND) + \".2\", \"/tmp:/tmp\",\n            k(ConfigKey.CAP_ADD) + \".1\", \"CAP\",\n            k(ConfigKey.CAP_DROP) + \".1\", \"CAP\",\n            k(ConfigKey.SECURITY_OPTS) + \".1\", \"seccomp=unconfined\",\n            k(ConfigKey.CPUS), \"1000000000\",\n            k(ConfigKey.CPUSET), \"0,1\",\n            k(ConfigKey.CPUSHARES), \"1\",\n            k(ConfigKey.CMD), \"command.sh\",\n            k(ConfigKey.DNS) + \".1\", \"8.8.8.8\",\n            k(ConfigKey.NET), \"host\",\n            k(ConfigKey.DNS_SEARCH) + \".1\", \"example.com\",\n            k(ConfigKey.DOMAINNAME), \"domain.com\",\n            k(ConfigKey.ENTRYPOINT), \"entrypoint.sh\",\n            k(ConfigKey.ENV) + \".HOME\", \"/Users/roland\",\n            k(ConfigKey.ARGS) + \".PROXY\", \"http://proxy\",\n            k(ConfigKey.LABELS) + \".com.acme.label\", \"Hello\\\"World\",\n            k(ConfigKey.BUILD_OPTIONS) + \".shmsize\", \"2147483648\",\n            k(ConfigKey.ENV_PROPERTY_FILE), \"/tmp/envProps.txt\",\n            k(ConfigKey.EXTRA_HOSTS) + \".1\", \"localhost:127.0.0.1\",\n            k(ConfigKey.FROM), \"image\",\n            k(ConfigKey.FROM_EXT) + \".name\", \"image-ext\",\n            k(ConfigKey.FROM_EXT) + \".kind\", \"kind\",\n            k(ConfigKey.HOSTNAME), \"subdomain\",\n            k(ConfigKey.LINKS) + \".1\", \"redis\",\n            k(ConfigKey.MAINTAINER), \"fabric8io@redhat.com\",\n            k(ConfigKey.MEMORY), \"1\",\n            k(ConfigKey.MEMORY_SWAP), \"1\",\n            k(ConfigKey.NAME), \"image\",\n            k(ConfigKey.PORT_PROPERTY_FILE), \"/tmp/props.txt\",\n            k(ConfigKey.PORTS) + \".1\", \"8081:8080\",\n            k(ConfigKey.PRIVILEGED), \"true\",\n            k(ConfigKey.REGISTRY), \"registry\",\n            k(ConfigKey.RESTART_POLICY_NAME), \"on-failure\",\n            k(ConfigKey.RESTART_POLICY_RETRY), \"1\",\n            k(ConfigKey.USER), \"tomcat\",\n            k(ConfigKey.ULIMITS)+\".1\", \"memlock=10:10\",\n            k(ConfigKey.ULIMITS)+\".2\", \"memlock=:-1\",\n            k(ConfigKey.ULIMITS)+\".3\", \"memlock=1024:\",\n            k(ConfigKey.ULIMITS)+\".4\", \"memlock=2048\",\n            k(ConfigKey.VOLUMES) + \".1\", \"/foo\",\n            k(ConfigKey.VOLUMES_FROM) + \".1\", \"from\",\n            k(ConfigKey.WAIT_EXEC_PRE_STOP), \"pre_stop_command\",\n            k(ConfigKey.WAIT_EXEC_POST_START), \"post_start_command\",\n            k(ConfigKey.WAIT_EXEC_BREAK_ON_ERROR), \"true\",\n            k(ConfigKey.WAIT_LOG), \"pattern\",\n            k(ConfigKey.WAIT_HEALTHY), \"true\",\n            k(ConfigKey.WAIT_TIME), \"5\",\n            k(ConfigKey.WAIT_EXIT), \"0\",\n            k(ConfigKey.WAIT_URL), \"http://foo.com\",\n            k(ConfigKey.LOG_PREFIX), \"SRV\",\n            k(ConfigKey.LOG_COLOR), \"green\",\n            k(ConfigKey.LOG_ENABLED), \"true\",\n            k(ConfigKey.LOG_DATE), \"iso8601\",\n            k(ConfigKey.LOG_DRIVER_NAME), \"json\",\n            k(ConfigKey.LOG_DRIVER_OPTS) + \".max-size\", \"1024\",\n            k(ConfigKey.LOG_DRIVER_OPTS) + \".max-file\", \"10\",\n            k(ConfigKey.WORKING_DIR), \"foo\",\n            k(ConfigKey.TMPFS) + \".1\", \"/var/lib/mysql:10m\",\n            k(ConfigKey.IMAGE_PULL_POLICY_BUILD), \"Always\",\n            k(ConfigKey.IMAGE_PULL_POLICY_RUN), \"Never\",\n            k(ConfigKey.READ_ONLY), \"true\",\n            k(ConfigKey.AUTO_REMOVE), \"true\",\n        };\n    }\n\n    private String[] getSkipTestData(ConfigKey key, boolean value) {\n        return new String[] {k(ConfigKey.NAME), \"image\", k(key), String.valueOf(value), k(ConfigKey.FROM), \"busybox\" };\n    }\n\n    private String k(ConfigKey from) {\n        return from.asPropertyKey();\n    }\n    private void assertUlimitEquals(UlimitConfig expected, UlimitConfig actual){\n    \tassertEquals(expected.getName(), actual.getName());\n        assertEquals(expected.getSoft(), actual.getSoft());\n        assertEquals(expected.getHard(), actual.getHard());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyModeTest.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class PropertyModeTest {\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidEmpty() {\n        PropertyMode.parse(\"\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidParse() {\n        PropertyMode.parse(\"propertiespom\");\n    }\n    @Test\n    public void testParse() {\n        assertEquals(PropertyMode.Only, PropertyMode.parse(null));\n        assertEquals(PropertyMode.Only, PropertyMode.parse(\"only\"));\n        assertEquals(PropertyMode.Fallback, PropertyMode.parse(\"fallback\"));\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/config/handler/property/ValueProviderTest.java",
    "content": "package io.fabric8.maven.docker.config.handler.property;\n\nimport java.util.*;\n\nimport org.hamcrest.Matchers;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\npublic class ValueProviderTest {\n    private ValueProvider provider;\n    private Properties props;\n\n    @Before\n    public void setUp() throws Exception {\n        props = new Properties();\n    }\n\n    private void configure(PropertyMode mode) {\n        provider = new ValueProvider(\"docker\", props, mode);\n    }\n\n    @Test\n    public void testGetString_Only() {\n        configure(PropertyMode.Only);\n        assertEquals(null, provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(null, provider.getString(ConfigKey.NAME, \"ignored\"));\n\n        props.put(\"docker.name\", \"myname\");\n        assertEquals(\"myname\", provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"myname\", provider.getString(ConfigKey.NAME, \"ignored\"));\n    }\n\n    @Test\n    public void testGetString_Skip() {\n        configure(PropertyMode.Skip);\n        assertEquals(null, provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromconfig\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n\n        props.put(\"docker.name\", \"ignored\");\n        assertEquals(null, provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromconfig\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n    }\n\n    @Test\n    public void testGetString_Fallback() {\n        configure(PropertyMode.Fallback);\n        assertEquals(null, provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromconfig\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n\n        props.put(\"docker.name\", \"fromprop\");\n        assertEquals(\"fromprop\", provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromconfig\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n    }\n\n    @Test\n    public void testGetString_Override() {\n        configure(PropertyMode.Override);\n        assertEquals(null, provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromconfig\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n\n        props.put(\"docker.name\", \"fromprop\");\n        assertEquals(\"fromprop\", provider.getString(ConfigKey.NAME, (String)null));\n        assertEquals(\"fromprop\", provider.getString(ConfigKey.NAME, \"fromconfig\"));\n    }\n\n\n    @Test\n    public void testGetInt() {\n        configure(PropertyMode.Only);\n        assertEquals(null, provider.getInteger(ConfigKey.SHMSIZE, null));\n        assertEquals(null, provider.getInteger(ConfigKey.SHMSIZE, 100));\n\n        props.put(\"docker.shmsize\", \"200\");\n        assertEquals(200, (int)provider.getInteger(ConfigKey.SHMSIZE, null));\n        assertEquals(200, (int)provider.getInteger(ConfigKey.SHMSIZE, 100));\n    }\n\n\n    @Test\n    public void testGetList() {\n        configure(PropertyMode.Only);\n        assertEquals(null, provider.getList(ConfigKey.PORTS, null));\n        assertEquals(null, provider.getList(ConfigKey.PORTS, Collections.singletonList(\"8080\")));\n\n        props.put(\"docker.ports.1\", \"200\");\n\n        assertThat(provider.getList(ConfigKey.PORTS, null), Matchers.contains(\"200\"));\n        assertThat(provider.getList(ConfigKey.PORTS, Collections.singletonList(\"8080\")), Matchers.contains(\"200\"));\n\n        props.put(\"docker.ports.1\", \"200\");\n        props.put(\"docker.ports.2\", \"8080\");\n        assertThat(provider.getList(ConfigKey.PORTS, null), Matchers.contains(\"200\", \"8080\"));\n        assertThat(provider.getList(ConfigKey.PORTS, Collections.singletonList(\"123\")), Matchers.contains(\"200\", \"8080\"));\n\n        configure(PropertyMode.Fallback);\n\n        assertThat(provider.getList(ConfigKey.PORTS, null), Matchers.contains(\"200\", \"8080\"));\n        assertThat(provider.getList(ConfigKey.PORTS, Collections.singletonList(\"123\")), Matchers.contains(\"123\", \"200\", \"8080\"));\n\n        configure(PropertyMode.Override);\n        assertThat(provider.getList(ConfigKey.PORTS, null), Matchers.contains(\"200\", \"8080\"));\n        assertThat(provider.getList(ConfigKey.PORTS, Collections.singletonList(\"123\")), Matchers.contains(\"200\", \"8080\", \"123\"));\n\n        // Test with another property that does not have CombinePolicy Merge\n        props.put(\"docker.entrypoint.1\", \"ep1\");\n        props.put(\"docker.entrypoint.2\", \"ep2\");\n        assertThat(provider.getList(ConfigKey.ENTRYPOINT, null), Matchers.contains(\"ep1\", \"ep2\"));\n        assertThat(provider.getList(ConfigKey.ENTRYPOINT, Collections.singletonList(\"asd\")), Matchers.contains(\"ep1\", \"ep2\"));\n\n        configure(PropertyMode.Fallback);\n        assertThat(provider.getList(ConfigKey.ENTRYPOINT, null), Matchers.contains(\"ep1\", \"ep2\"));\n        assertThat(provider.getList(ConfigKey.ENTRYPOINT, Collections.singletonList(\"asd\")), Matchers.contains(\"asd\"));\n\n        // Override combine policy\n        props.put(\"docker.entrypoint._combine\", \"merge\");\n\n        assertThat(provider.getList(ConfigKey.ENTRYPOINT, Collections.singletonList(\"asd\")), Matchers.contains(\"asd\", \"ep1\", \"ep2\"));\n    }\n\n\n\n    @Test\n    public void testGetMap() {\n        configure(PropertyMode.Only);\n        assertEquals(null, provider.getMap(ConfigKey.ENV_RUN, null));\n        assertEquals(null, provider.getMap(ConfigKey.ENV_RUN, getTestMap(\"key\", \"value\")));\n\n        props.put(\"docker.envRun.myprop1\", \"pvalue1\");\n        props.put(\"docker.envRun.myprop2\", \"pvalue2\");\n\n        Map m = provider.getMap(ConfigKey.ENV_RUN, null);\n        assertEquals(2, m.size());\n        assertEquals(\"pvalue1\", m.get(\"myprop1\"));\n        assertEquals(\"pvalue2\", m.get(\"myprop2\"));\n\n        m = provider.getMap(ConfigKey.ENV_RUN, getTestMap(\"mycfg\", \"cvalue\"));\n        assertEquals(2, m.size());\n        assertEquals(\"pvalue1\", m.get(\"myprop1\"));\n        assertEquals(\"pvalue2\", m.get(\"myprop2\"));\n\n\n        configure(PropertyMode.Override);\n\n        m = provider.getMap(ConfigKey.ENV_RUN, null);\n        assertEquals(2, m.size());\n        assertEquals(\"pvalue1\", m.get(\"myprop1\"));\n        assertEquals(\"pvalue2\", m.get(\"myprop2\"));\n\n        m = provider.getMap(ConfigKey.ENV_RUN, getTestMap(\"ckey\", \"cvalue\", \"myprop1\", \"ignored\"));\n        assertEquals(3, m.size());\n        assertEquals(\"pvalue1\", m.get(\"myprop1\"));\n        assertEquals(\"pvalue2\", m.get(\"myprop2\"));\n        assertEquals(\"cvalue\", m.get(\"ckey\"));\n\n\n        configure(PropertyMode.Fallback);\n        m = provider.getMap(ConfigKey.ENV_RUN, getTestMap(\"ckey\", \"cvalue\", \"myprop1\", \"overrides\"));\n        assertEquals(3, m.size());\n        assertEquals(\"overrides\", m.get(\"myprop1\"));\n        assertEquals(\"pvalue2\", m.get(\"myprop2\"));\n        assertEquals(\"cvalue\", m.get(\"ckey\"));\n\n        // Test with another property that does not have CombinePolicy Merge\n        props.put(\"docker.buildOptions.boprop1\", \"popt1\");\n        props.put(\"docker.buildOptions.boprop2\", \"popt2\");\n        configure(PropertyMode.Override);\n        m = provider.getMap(ConfigKey.BUILD_OPTIONS, null);\n        assertEquals(2, m.size());\n        assertEquals(\"popt1\", m.get(\"boprop1\"));\n        assertEquals(\"popt2\", m.get(\"boprop2\"));\n\n        m = provider.getMap(ConfigKey.BUILD_OPTIONS, getTestMap(\"ckey\", \"ignored\", \"myprop1\", \"ignored\"));\n        assertEquals(2, m.size());\n        assertEquals(\"popt1\", m.get(\"boprop1\"));\n        assertEquals(\"popt2\", m.get(\"boprop2\"));\n\n        configure(PropertyMode.Fallback);\n        m = provider.getMap(ConfigKey.BUILD_OPTIONS, getTestMap(\"ckey\", \"notignored1\", \"myprop1\", \"notignored2\"));\n        assertEquals(2, m.size());\n        assertEquals(\"notignored1\", m.get(\"ckey\"));\n        assertEquals(\"notignored2\", m.get(\"myprop1\"));\n\n        // Override combine policy\n        props.put(\"docker.buildOptions._combine\", \"merge\");\n\n        m = provider.getMap(ConfigKey.BUILD_OPTIONS, getTestMap(\"ckey\", \"notignored1\", \"boprop2\", \"notignored2\"));\n        assertEquals(3, m.size());\n        assertEquals(\"popt1\", m.get(\"boprop1\"));\n        assertEquals(\"notignored2\", m.get(\"boprop2\"));\n        assertEquals(\"notignored1\", m.get(\"ckey\"));\n    }\n\n\n\n    private Map getTestMap(String ... vals) {\n        Map ret = new HashMap();\n        for (int i = 0; i < vals.length; i+=2) {\n            ret.put(vals[i],vals[i+1]);\n        }\n        return ret;\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/log/DefaultLogCallbackTest.java",
    "content": "package io.fabric8.maven.docker.log;\n\nimport static org.hamcrest.Matchers.contains;\nimport static org.hamcrest.Matchers.empty;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.Matchers.startsWith;\nimport static org.junit.Assert.assertThat;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.*;\nimport java.time.ZonedDateTime;\nimport java.util.*;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\nimport com.google.common.io.Files;\nimport org.apache.maven.shared.utils.io.FileUtils;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.access.log.LogCallback.DoneException;\nimport io.fabric8.maven.docker.util.TimestampFactory;\n\npublic class DefaultLogCallbackTest {\n\n    private File file;\n\n    private LogOutputSpec spec;\n\n    private LogCallback callback;\n\n    private ZonedDateTime ts;\n\n    private static final int NR_LOOPS = 100;\n\n    @Before\n    public void before() throws IOException {\n        file = File.createTempFile(\"logcallback\", \".log\");\n        file.deleteOnExit();\n        spec = new LogOutputSpec.Builder().prefix(\"callback-test> \")\n                                          .file(file.toString()).build();\n        callback = new DefaultLogCallback(spec);\n        callback.open();\n        ts = TimestampFactory.createTimestamp(\"2016-12-21T15:09:00.999666333Z\");\n    }\n\n    @Test\n    public void shouldLogSequentially() throws IOException, DoneException {\n        callback.log(1, ts, \"line 1\");\n        callback.log(1, ts, \"line 2\");\n        callback.close();\n\n        List<String> lines = Arrays.asList(FileUtils.fileReadArray(file));\n        assertThat(lines, contains(\"callback-test> line 1\", \"callback-test> line 2\"));\n    }\n\n    @Test\n    public void shouldLogError() throws IOException, DoneException {\n        callback.error(\"error 1\");\n        callback.log(1, ts, \"line 2\");\n        callback.error(\"error 3\");\n        callback.close();\n\n        List<String> lines = Arrays.asList(FileUtils.fileReadArray(file));\n        assertThat(lines, contains(\"error 1\", \"callback-test> line 2\", \"error 3\"));\n    }\n\n    @Test\n    public void shouldLogToStdout() throws IOException, DoneException {\n        // we don't need the default stream for this test\n        callback.close();\n\n        file = File.createTempFile(\"logcallback-stdout\", \".log\");\n        file.deleteOnExit();\n        FileOutputStream os = new FileOutputStream(file);\n        PrintStream ps = new PrintStream(os);\n        PrintStream stdout = System.out;\n        try {\n            System.setOut(ps);\n            spec = new LogOutputSpec.Builder().prefix(\"stdout> \")\n                    .build();\n            callback = new DefaultLogCallback(spec);\n            callback.open();\n            DefaultLogCallback callback2 = new DefaultLogCallback(spec);\n            callback2.open();\n\n            callback.log(1, ts, \"line 1\");\n            callback2.log(1, ts, \"line 2\");\n            callback.log(1, ts, \"line 3\");\n            callback.close();\n            callback2.log(1, ts, \"line 4\");\n            callback2.close();\n\n            List<String> lines = Arrays.asList(FileUtils.fileReadArray(file));\n            assertThat(lines, contains(\"stdout> line 1\", \"stdout> line 2\", \"stdout> line 3\", \"stdout> line 4\"));\n        } finally {\n            System.setOut(stdout);\n        }\n    }\n\n    @Test\n    public void shouldKeepStreamOpen() throws IOException, DoneException {\n        DefaultLogCallback callback2 = new DefaultLogCallback(spec);\n        callback2.open();\n        callback.log(1, ts, \"line 1\");\n        callback2.log(1, ts, \"line 2\");\n        callback.log(1, ts, \"line 3\");\n        callback.close();\n        callback2.log(1, ts, \"line 4\");\n        callback2.close();\n\n        List<String> lines = Arrays.asList(FileUtils.fileReadArray(file));\n        assertThat(lines,\n                contains(\"callback-test> line 1\", \"callback-test> line 2\", \"callback-test> line 3\", \"callback-test> line 4\"));\n    }\n\n    @Test\n    public void shouldLogInParallel() throws IOException, DoneException, InterruptedException {\n        DefaultLogCallback callback2 = new DefaultLogCallback(spec);\n        callback2.open();\n\n        ExecutorService executorService = Executors.newFixedThreadPool(2);\n        LoggerTask task1 = new LoggerTask(callback, 1);\n        LoggerTask task2 = new LoggerTask(callback2, 1 + NR_LOOPS);\n        executorService.submit(task1);\n        executorService.submit(task2);\n        executorService.awaitTermination(1, TimeUnit.SECONDS);\n\n        List<String> lines = Arrays.asList(FileUtils.fileReadArray(file));\n        //System.out.println(lines);\n        assertThat(lines.size(), is(NR_LOOPS * 2));\n\n        // fill set with expected line numbers\n        Set<Integer> indexes = new HashSet<>();\n        for (int i = 1; i <= 2 * NR_LOOPS; i++) {\n            indexes.add(i);\n        }\n\n        // remove found line numbers from set\n        for (String line : lines) {\n            String prefix = \"callback-test> line \";\n            assertThat(line, startsWith(prefix));\n            String suffix = line.substring(prefix.length());\n            indexes.remove(Integer.parseInt(suffix));\n        }\n\n        // expect empty set\n        assertThat(indexes, is(empty()));\n    }\n\n    @Test\n    public void shouldCreateParentDirs() throws IOException {\n        File dir = Files.createTempDir();\n        dir.deleteOnExit();\n        file = new File(dir, \"non/existing/dirs/file.log\");\n        spec = new LogOutputSpec.Builder().prefix(\"callback-test> \")\n                .file(file.toString()).build();\n        callback = new DefaultLogCallback(spec);\n        callback.open();\n        assertTrue(file.exists());\n    }\n\n    private class LoggerTask implements Runnable {\n\n        private LogCallback cb;\n\n        private int start;\n\n        LoggerTask(LogCallback cb, int start) {\n            this.cb = cb;\n            this.start = start;\n        }\n\n        @Override\n        public void run() {\n            for (int i = 0; i < NR_LOOPS; i++) {\n                try {\n                    callback.log(1, ts, \"line \" + (start + i));\n                } catch (DoneException e) {\n                    // ignore\n                }\n            }\n            cb.close();\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/log/LogOutputSpecDateTest.java",
    "content": "package io.fabric8.maven.docker.log;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.format.FormatStyle;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.concurrent.TimeUnit;\n\nimport static java.time.format.DateTimeFormatter.ofLocalizedDateTime;\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author denisa\n * @since 03.08.20\n */\n@RunWith(Parameterized.class)\npublic class LogOutputSpecDateTest {\n    // Mon Jan 2 15:04:05 PST 2006\n    private static final ZonedDateTime DATE_TIME = ZonedDateTime.of(2006, 1, 2, 15, 4, 5, (int) MILLISECONDS.toNanos(7), ZoneOffset.ofHours(-8));\n\n    @Parameterized.Parameters(name = \"{index}: format \\\"{0}\\\" --> \\\"{1}\\\"\")\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][]{\n                {null, \"\"},\n                {\"none\", \"\"},\n                {\"false\", \"\"},\n                {\"default\", \"15:04:05.007 \"},\n                {\"iso8601\", \"2006-01-02T15:04:05.007-08:00 \"},\n                {\"short\", DATE_TIME.format(ofLocalizedDateTime(FormatStyle.SHORT)) + \" \"},\n                {\"medium\", DATE_TIME.format(ofLocalizedDateTime(FormatStyle.MEDIUM)) + \" \"},\n                {\"long\", DATE_TIME.format(ofLocalizedDateTime(FormatStyle.LONG)) + \" \"},\n                {\"YYYY MM\", \"2006 01 \"},\n        });\n    }\n\n    @Parameterized.Parameter(0)\n    public String timeFormatter;\n\n    @Parameterized.Parameter(1)\n    public String expectedPrompt;\n\n    @Test\n    public void prompt() {\n        LogOutputSpec spec = createSpec(timeFormatter);\n        assertEquals(expectedPrompt, spec.getPrompt(false, DATE_TIME));\n    }\n\n    private LogOutputSpec createSpec(String timeFormatter) {\n        return new LogOutputSpec.Builder().timeFormatter(timeFormatter).prefix(\"\").build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/log/LogOutputSpecFactoryTest.java",
    "content": "package io.fabric8.maven.docker.log;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 04.11.17\n */\n@RunWith(Parameterized.class)\npublic class LogOutputSpecFactoryTest {\n\n    private static String ALIAS = \"fcn\";\n    private static String NAME = \"rhuss/fcn:1.0\";\n    private static String CONTAINER_ID = \"1234567890\";\n\n    @Parameterized.Parameters(name = \"{index}: format \\\"{0}\\\" --> \\\"{1}\\\"\")\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][] {\n            { \"%z\", \"\" },\n            { null, ALIAS + \"> \"},\n            { \"%c\", CONTAINER_ID.substring(0,6) },\n            { \"%C: \", CONTAINER_ID + \": \" },\n            { \"%n -- \", NAME + \" -- \" },\n            { \"%z%c%n%C %a\", CONTAINER_ID.substring(0,6) + NAME + CONTAINER_ID + \" \" + ALIAS }\n           });\n    }\n\n    @Parameterized.Parameter(0)\n    public String prefixFormat;\n\n    @Parameterized.Parameter(1)\n    public String expectedPrefix;\n\n    @Test\n    public void prefix() {\n        LogOutputSpec spec = createSpec(prefixFormat);\n        assertEquals(expectedPrefix, spec.getPrompt(false, null));\n    }\n\n    private LogOutputSpec createSpec(String prefix) {\n        LogOutputSpecFactory factory = new LogOutputSpecFactory(false, false, null);\n        LogConfiguration logConfig = new LogConfiguration.Builder().prefix(prefix).build();\n        RunImageConfiguration runConfig = new RunImageConfiguration.Builder().log(logConfig).build();\n        ImageConfiguration imageConfiguration = new ImageConfiguration.Builder().alias(ALIAS).name(NAME).runConfig(runConfig).build();\n        return factory.createSpec(CONTAINER_ID,imageConfiguration);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/log/LogOutputSpecTest.java",
    "content": "package io.fabric8.maven.docker.log;\n\nimport org.fusesource.jansi.Ansi;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author denisa\n * @since 03.08.20\n */\npublic class LogOutputSpecTest {\n    // Mon Jan 2 15:04:05 PST 2006\n    private static final ZonedDateTime DATE_TIME = ZonedDateTime.of(2006, 1, 2, 15, 4, 5, (int) MILLISECONDS.toNanos(7), ZoneOffset.ofHours(-8));\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void prompt() {\n        final LogOutputSpec spec = new LogOutputSpec.Builder()\n                .timeFormatter(\"\").prefix(\"fcn> \")\n                .build();\n        assertEquals(\"15:04:05.007 fcn> \", spec.getPrompt(false, DATE_TIME));\n    }\n\n    @Test\n    public void promptWithBrightColor() {\n        final LogOutputSpec spec = new LogOutputSpec.Builder()\n                .color(\"RED\", true)\n                .timeFormatter(\"\").prefix(\"fcn> \")\n                .build();\n\n        boolean enabled = Ansi.isEnabled();\n        Ansi.setEnabled(true);\n        try {\n            assertEquals(\"\\u001B[90m15:04:05.007\\u001B[m \\u001B[91mfcn> \\u001B[m\", spec.getPrompt(true, DATE_TIME));\n        } finally {\n            Ansi.setEnabled(enabled);\n        }\n    }\n\n    @Test\n    public void promptWithColor() {\n        final LogOutputSpec spec = new LogOutputSpec.Builder()\n                .color(\"RED\")\n                .timeFormatter(\"\").prefix(\"fcn> \")\n                .build();\n\n        boolean enabled = Ansi.isEnabled();\n        Ansi.setEnabled(true);\n        try {\n            assertEquals(\"\\u001B[90m15:04:05.007\\u001B[m \\u001B[31mfcn> \\u001B[m\", spec.getPrompt(true, DATE_TIME));\n        } finally {\n            Ansi.setEnabled(enabled);\n        }\n    }\n\n    @Test\n    public void unrecognizedColor() {\n        expectedException.expect(IllegalArgumentException.class);\n        expectedException.expectMessage(\"Invalid color\");\n        new LogOutputSpec.Builder().color(\"glitter\").build();\n    }\n\n    @Test\n    public void unrecognizedTimeFormat() {\n        expectedException.expect(IllegalArgumentException.class);\n        expectedException.expectMessage(\"Cannot parse log date specification\");\n        new LogOutputSpec.Builder().timeFormatter(\"This is not a format\").build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/model/ContainerDetailsTest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\npublic class ContainerDetailsTest {\n\n    private Container container;\n\n    private JsonObject json;\n\n    @Before\n    public void setup() {\n        json = new JsonObject();\n    }\n\n    @Test\n    public void testCustomNetworkIpAddresses() {\n        givenNetworkSettings(\"custom1\",\"1.2.3.4\",\"custom2\",\"5.6.7.8\");\n\n        whenCreateContainer();\n\n        thenMappingSize(2);\n        thenMappingMatches(\"custom1\",\"1.2.3.4\",\"custom2\",\"5.6.7.8\");\n    }\n\n    @Test\n    public void testEmptyNetworkSettings() {\n        givenNetworkSettings();\n\n\n        whenCreateContainer();\n\n        thenMappingIsNull();\n    }\n\n    private void thenMappingIsNull() {\n        assertNull(container.getCustomNetworkIpAddresses());\n    }\n\n    private void thenMappingMatches(String ... args) {\n        Map<String,String> addresses = container.getCustomNetworkIpAddresses();\n        for (int i = 0; i < args.length; i+=2) {\n            assertEquals(args[i+1],addresses.get(args[i]));\n        }\n    }\n\n    private void thenMappingSize(int size) {\n        assertEquals(container.getCustomNetworkIpAddresses().size(), size);\n    }\n\n    private void givenNetworkSettings(String ... args) {\n        JsonObject networkSettings = new JsonObject();\n        JsonObject networks = new JsonObject();\n        for (int i = 0; i < args.length; i+=2) {\n            JsonObject network = new JsonObject();\n            network.addProperty(\"IPAddress\",args[i+1]);\n            networks.add(args[i], network);\n        }\n        networkSettings.add(\"Networks\", networks);\n        json.add(\"NetworkSettings\", networkSettings);\n    }\n\n    @Test\n    public void testContainerWithMappedPorts() {\n        givenAContainerWithMappedPorts();\n\n        whenCreateContainer();\n\n        thenPortBindingSizeIs(2);\n\n        thenMapContainsSpecAndBinding(\"80/tcp\", 32771, \"0.0.0.0\");\n        thenMapContainsSpecAndBinding(\"52/udp\", 32772, \"1.2.3.4\");\n    }\n\n    @Test\n    public void testContainerWithPorts() {\n        givenAContainerWithPorts();\n        whenCreateContainer();\n\n        thenPortBindingSizeIs(2);\n\n        thenMapContainsPortSpecOnly(\"80/tcp\");\n        thenMapContainsPortSpecOnly(\"52/udp\");\n    }\n\n    @Test\n    public void testContainerWithoutPorts() {\n        givenAContainerWithoutPorts();\n        whenCreateContainer();\n        thenPortBindingSizeIs(0);\n    }\n\n    @Test\n    public void testContainerWithLabels() {\n        givenAContainerWithLabels();\n        whenCreateContainer();\n        thenLabelsSizeIs(2);\n        thenLabelsContains(\"key1\", \"value1\");\n        thenLabelsContains(\"key2\", \"value2\");\n    }\n\n    private void thenLabelsContains(String key, String value) {\n        assertTrue(container.getLabels().containsKey(key));\n        assertEquals(value, container.getLabels().get(key));\n    }\n\n    private void givenAContainerWithLabels() {\n        JsonObject labels = new JsonObject();\n        labels.addProperty(\"key1\", \"value1\");\n        labels.addProperty(\"key2\", \"value2\");\n\n        JsonObject config = new JsonObject();\n        config.add(ContainerDetails.LABELS, labels);\n\n        json.add(\"Config\",config);\n    }\n\n    @Test\n    public void testCreateContainer() throws Exception {\n        givenContainerData();\n        whenCreateContainer();\n        thenValidateContainer();\n    }\n\n    private JsonArray createHostIpAndPort(int port, String ip) {\n        JsonObject object = new JsonObject();\n\n        object.addProperty(ContainerDetails.HOST_IP, ip);\n        object.addProperty(ContainerDetails.HOST_PORT, String.valueOf(port));\n\n        JsonArray array = new JsonArray();\n        array.add(object);\n\n        return array;\n    }\n\n    private JsonObject createPortsObject() {\n        JsonObject ports = new JsonObject();\n        JsonObject networkSettings = new JsonObject();\n\n        networkSettings.add(ContainerDetails.PORTS, ports);\n        json.add(ContainerDetails.NETWORK_SETTINGS, networkSettings);\n\n        return ports;\n    }\n\n    private void givenAContainerWithPorts() {\n        JsonObject ports = createPortsObject();\n\n        ports.add(\"80/tcp\", null);\n        ports.add(\"52/udp\", null);\n    }\n\n    private void givenAContainerWithMappedPorts() {\n        JsonObject ports = createPortsObject();\n\n        ports.add(\"80/tcp\", createHostIpAndPort(32771, \"0.0.0.0\"));\n        ports.add(\"52/udp\", createHostIpAndPort(32772, \"1.2.3.4\"));\n    }\n\n    private void givenAContainerWithoutPorts() {\n        json.add(ContainerDetails.NETWORK_SETTINGS, new JsonObject());\n    }\n\n    private void givenContainerData() {\n        json.addProperty(ContainerDetails.CREATED, \"2015-01-06T15:47:31.485331387Z\");\n        json.addProperty(ContainerDetails.ID, \"1234AF1234AF\");\n        json.addProperty(ContainerDetails.NAME, \"/milkman-kindness\");\n        // new JsonObject(\"{ 'Image': '9876CE'}\")\n        JsonObject jsonObject1 = new JsonObject();\n        jsonObject1.addProperty(\"Image\", \"9876CE\");\n        json.add(ContainerDetails.CONFIG, jsonObject1);\n\n        JsonObject jsonObject2 = new JsonObject();\n        jsonObject2.addProperty(\"Running\", \"true\");\n        json.add(ContainerDetails.STATE, jsonObject2);\n\n        json.add(ContainerDetails.NETWORK_SETTINGS, new JsonObject());\n    }\n\n    private void thenMapContainsPortSpecOnly(String key) {\n        assertTrue(container.getPortBindings().containsKey(key));\n        assertNull(container.getPortBindings().get(key));\n    }\n\n    private void thenMapContainsSpecAndBinding(String key, int port, String ip) {\n        assertTrue(container.getPortBindings().containsKey(key));\n        assertNotNull(container.getPortBindings().get(key));\n\n        assertEquals(ip, container.getPortBindings().get(key).getHostIp());\n        assertEquals(port, container.getPortBindings().get(key).getHostPort().intValue());\n    }\n\n    private void thenLabelsSizeIs(int size) {\n        assertEquals(size, container.getLabels().size());\n    }\n\n    private void thenPortBindingSizeIs(int size) {\n        assertEquals(size, container.getPortBindings().size());\n    }\n\n    private void thenValidateContainer() {\n        assertEquals(1420559251485L, container.getCreated());\n        assertEquals(\"1234AF1234AF\", container.getId());\n        assertEquals(\"milkman-kindness\", container.getName());\n        assertEquals(\"9876CE\", container.getImage());\n        assertTrue(container.isRunning());\n        assertTrue(container.getPortBindings().isEmpty());\n    }\n\n    private void whenCreateContainer() {\n        container = new ContainerDetails(json);\n    }\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/model/ContainerListElementTest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\npublic class ContainerListElementTest {\n\n    private Container container;\n\n    private JsonObject json;\n\n    @Before\n    public void setup() {\n        json = new JsonObject();\n    }\n\n    @Test\n    public void testContaierWithMappedPorts() {\n        givenAContainerWithMappedPorts();\n        whenCreateContainer();\n        thenPortBindingSizeIs(2);\n        thenMapContainsSpecAndBinding(\"80/tcp\", 32771, \"0.0.0.0\");\n        thenMapContainsSpecAndBinding(\"52/udp\", 32772, \"1.2.3.4\");\n    }\n\n    @Test\n    public void testContaierWithPorts() {\n        givenAContainerWithPorts();\n        whenCreateContainer();\n        thenPortBindingSizeIs(2);\n        thenMapContainsPortSpecOnly(\"80/tcp\");\n        thenMapContainsPortSpecOnly(\"52/udp\");\n    }\n\n    @Test\n    public void testContainerWithLabels() {\n        givenAContainerWithLabels();\n        whenCreateContainer();\n        thenLabelsSizeIs(2);\n        thenLabelsContains(\"key1\", \"value1\");\n        thenLabelsContains(\"key2\", \"value2\");\n    }\n\n    @Test\n    public void testContainerWithoutLabels() {\n        givenContainerData();\n        whenCreateContainer();\n        thenLabelsSizeIs(0);\n    }\n\n    @Test\n    public void testContainerWithoutPorts() {\n        givenAContainerWithoutPorts();\n        whenCreateContainer();\n        thenPortBindingSizeIs(0);\n    }\n\n    @Test\n    public void testCreateContainer() throws Exception {\n        givenContainerData();\n        whenCreateContainer();\n        thenValidateContainer();\n    }\n\n    @Test(expected = UnsupportedOperationException.class)\n    public void testNoNameInListElement() {\n        new ContainersListElement(new JsonObject()).getName();\n    }\n\n    private void addToArray(JsonArray array, int index, String key, String value) {\n        array.get(index).getAsJsonObject().addProperty(key, value);\n    }\n\n    private void addToArray(JsonArray array, int index, String key, Integer value) {\n        array.get(index).getAsJsonObject().addProperty(key, value);\n    }\n\n    private JsonObject createPortData(int port, String type) {\n        JsonObject ports = new JsonObject();\n        ports.addProperty(\"PrivatePort\", port);\n        ports.addProperty(ContainersListElement.TYPE, type);\n\n        return ports;\n    }\n\n    private void givenAContainerWithPorts() {\n        JsonArray array = new JsonArray();\n        array.add(createPortData(80, \"tcp\"));\n        array.add(createPortData(52, \"udp\"));\n\n        json.add(ContainersListElement.PORTS, array);\n    }\n\n\n    private void givenAContainerWithLabels() {\n        JsonObject labels = new JsonObject();\n        labels.addProperty(\"key1\", \"value1\");\n        labels.addProperty(\"key2\", \"value2\");\n\n        json.add(ContainerDetails.LABELS, labels);\n    }\n\n    private void givenAContainerWithMappedPorts() {\n        givenAContainerWithPorts();\n\n        JsonArray array = json.getAsJsonArray(ContainersListElement.PORTS);\n\n        addToArray(array, 0, ContainersListElement.IP, \"0.0.0.0\");\n        addToArray(array, 0, ContainersListElement.PUBLIC_PORT, 32771);\n\n        addToArray(array, 1, ContainersListElement.IP, \"1.2.3.4\");\n        addToArray(array, 1, ContainersListElement.PUBLIC_PORT, 32772);\n    }\n\n    private void givenAContainerWithoutPorts() {\n        json.add(\"Ports\", new JsonArray());\n    }\n\n    private void givenContainerData() {\n        json.addProperty(ContainersListElement.CREATED,1420559251485L);\n        json.addProperty(ContainersListElement.ID, \"1234AF1234AF\");\n        json.addProperty(ContainersListElement.IMAGE, \"9876CE\");\n        json.addProperty(ContainersListElement.STATUS, \"Up 16 seconds\");\n        json.add(ContainersListElement.PORTS, new JsonArray());\n    }\n\n    private void thenLabelsContains(String key, String value) {\n        assertTrue(container.getLabels().containsKey(key));\n        assertEquals(value, container.getLabels().get(key));\n    }\n\n    private void thenLabelsSizeIs(int size) {\n        assertEquals(size, container.getLabels().size());\n    }\n\n    private void thenMapContainsPortSpecOnly(String key) {\n        assertTrue(container.getPortBindings().containsKey(key));\n        assertNull(container.getPortBindings().get(key));\n    }\n\n    private void thenMapContainsSpecAndBinding(String key, int port, String ip) {\n        assertTrue(container.getPortBindings().containsKey(key));\n        assertNotNull(container.getPortBindings().get(key));\n\n        assertEquals(ip, container.getPortBindings().get(key).getHostIp());\n        assertEquals(port, container.getPortBindings().get(key).getHostPort().intValue());\n    }\n\n    private void thenPortBindingSizeIs(int size) {\n        assertEquals(size, container.getPortBindings().size());\n    }\n\n    private void thenValidateContainer() {\n        assertEquals(1420559251485L, container.getCreated());\n        assertEquals(\"1234AF1234AF\", container.getId());\n        assertEquals(\"9876CE\", container.getImage());\n        assertTrue(container.isRunning());\n        assertTrue(container.getPortBindings().isEmpty());\n    }\n\n    private void whenCreateContainer() {\n        container = new ContainersListElement(json);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/model/ImageArchiveManifestAdapterTest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonNull;\nimport com.google.gson.JsonObject;\n\npublic class ImageArchiveManifestAdapterTest {\n    @Test\n    public void createFromEmptyJsonArray() {\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(new JsonArray());\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertTrue(\"No entries in manifest\", manifest.getEntries().isEmpty());\n    }\n\n    @Test\n    public void createFromJsonArrayNonObject() {\n        JsonArray jsonArray = new JsonArray();\n        jsonArray.add(false);\n        jsonArray.add(new JsonArray());\n        jsonArray.add(10);\n\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(jsonArray);\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertTrue(\"No entries in manifest\", manifest.getEntries().isEmpty());\n    }\n\n    @Test\n    public void createFromEmptyJsonObject() {\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(new JsonObject());\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertTrue(\"No entries in manifest\", manifest.getEntries().isEmpty());\n    }\n\n    @Test\n    public void createFromJsonNull() {\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(JsonNull.INSTANCE);\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertTrue(\"No entries in manifest\", manifest.getEntries().isEmpty());\n    }\n\n    @Test\n    public void createFromArrayOfObject() {\n        JsonArray objects = new JsonArray();\n        objects.add(new JsonObject());\n\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(objects);\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertFalse(\"Some entries in manifest\", manifest.getEntries().isEmpty());\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            Assert.assertNotNull(entry);\n        }\n    }\n\n    @Test\n    public void createFromArrayOfObjects() {\n        JsonArray objects = new JsonArray();\n        objects.add(new JsonObject());\n        objects.add(new JsonObject());\n        objects.add(new JsonObject());\n\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(objects);\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertFalse(\"Some entries in manifest\", manifest.getEntries().isEmpty());\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            Assert.assertNotNull(entry);\n        }\n    }\n\n    @Test\n    public void createFromArrayOfObjectsAndElements() {\n        JsonArray objects = new JsonArray();\n        objects.add(new JsonObject());\n        objects.add(new JsonArray());\n        objects.add(new JsonObject());\n        objects.add(\"ABC\");\n        objects.add(123);\n        objects.add(JsonNull.INSTANCE);\n\n        ImageArchiveManifest manifest = new ImageArchiveManifestAdapter(objects);\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertFalse(\"Some entries in manifest\", manifest.getEntries().isEmpty());\n\n        for(ImageArchiveManifestEntry entry : manifest.getEntries()) {\n            Assert.assertNotNull(entry);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/model/ImageArchiveManifestEntryAdapterTest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\npublic class ImageArchiveManifestEntryAdapterTest {\n    @Test\n    public void createFromEmptyJsonObject() {\n        ImageArchiveManifestEntryAdapter entry = new ImageArchiveManifestEntryAdapter(new JsonObject());\n\n        Assert.assertNotNull(entry);\n        Assert.assertNull(entry.getConfig());\n        Assert.assertNull(entry.getId());\n        Assert.assertNotNull(entry.getRepoTags());\n        Assert.assertTrue(entry.getRepoTags().isEmpty());\n        Assert.assertNotNull(entry.getLayers());\n        Assert.assertTrue(entry.getLayers().isEmpty());\n    }\n\n    @Test\n    public void createFromValidJsonObject() {\n        JsonObject entryJson = new JsonObject();\n        entryJson.addProperty(ImageArchiveManifestEntryAdapter.CONFIG, \"image-id-sha256.json\");\n\n        JsonArray repoTagsJson = new JsonArray();\n        repoTagsJson.add(\"test/image:latest\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.REPO_TAGS, repoTagsJson);\n\n        JsonArray layersJson = new JsonArray();\n        layersJson.add(\"layer-id-sha256/layer.tar\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.LAYERS, layersJson);\n\n        ImageArchiveManifestEntryAdapter entry = new ImageArchiveManifestEntryAdapter(entryJson);\n\n        Assert.assertNotNull(entry);\n        Assert.assertEquals(\"image-id-sha256.json\", entry.getConfig());\n        Assert.assertEquals(\"image-id-sha256\", entry.getId());\n        Assert.assertNotNull(entry.getRepoTags());\n        Assert.assertEquals(Collections.singletonList(\"test/image:latest\"), entry.getRepoTags());\n        Assert.assertNotNull(entry.getLayers());\n        Assert.assertEquals(Collections.singletonList(\"layer-id-sha256/layer.tar\"), entry.getLayers());\n    }\n\n    @Test\n    public void createFromValidJsonObjectWithAdditionalFields() {\n        JsonObject entryJson = new JsonObject();\n        entryJson.addProperty(\"Random\", \"new feature\");\n\n        entryJson.addProperty(ImageArchiveManifestEntryAdapter.CONFIG, \"image-id-sha256.json\");\n\n        JsonArray repoTagsJson = new JsonArray();\n        repoTagsJson.add(\"test/image:latest\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.REPO_TAGS, repoTagsJson);\n\n        JsonArray layersJson = new JsonArray();\n        layersJson.add(\"layer-id-sha256/layer.tar\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.LAYERS, layersJson);\n\n        ImageArchiveManifestEntryAdapter entry = new ImageArchiveManifestEntryAdapter(entryJson);\n\n        Assert.assertNotNull(entry);\n        Assert.assertEquals(\"image-id-sha256.json\", entry.getConfig());\n        Assert.assertEquals(\"image-id-sha256\", entry.getId());\n        Assert.assertNotNull(entry.getRepoTags());\n        Assert.assertEquals(Collections.singletonList(\"test/image:latest\"), entry.getRepoTags());\n        Assert.assertNotNull(entry.getLayers());\n        Assert.assertEquals(Collections.singletonList(\"layer-id-sha256/layer.tar\"), entry.getLayers());\n    }\n\n    @Test\n    public void createFromPartlyValidJsonObject() {\n        JsonObject entryJson = new JsonObject();\n\n        entryJson.addProperty(ImageArchiveManifestEntryAdapter.CONFIG, \"image-id-sha256.json\");\n\n        JsonArray repoTagsJson = new JsonArray();\n        repoTagsJson.add(\"test/image:latest\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.REPO_TAGS, repoTagsJson);\n\n        JsonObject layersJson = new JsonObject();\n        layersJson.addProperty(\"layer1\", \"layer-id-sha256/layer.tar\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.LAYERS, layersJson);\n\n        ImageArchiveManifestEntryAdapter entry = new ImageArchiveManifestEntryAdapter(entryJson);\n\n        Assert.assertNotNull(entry);\n        Assert.assertEquals(\"image-id-sha256.json\", entry.getConfig());\n        Assert.assertEquals(\"image-id-sha256\", entry.getId());\n        Assert.assertNotNull(entry.getRepoTags());\n        Assert.assertEquals(Collections.singletonList(\"test/image:latest\"), entry.getRepoTags());\n        Assert.assertNotNull(entry.getLayers());\n        Assert.assertTrue(entry.getLayers().isEmpty());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/model/ImageDetailsTest.java",
    "content": "package io.fabric8.maven.docker.model;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport com.google.gson.JsonNull;\nimport org.junit.Before;\nimport org.junit.Test;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\n\npublic class ImageDetailsTest {\n\n    private Image image;\n\n    private JsonObject json;\n\n    @Before\n    public void setup() {\n        json = new JsonObject();\n    }\n\n    @Test\n    public void testImageWithLabels() {\n        givenAnImageWithLabels();\n        whenCreateImage();\n        thenLabelsSizeIs(2);\n        thenLabelsContains(\"key1\", \"value1\");\n        thenLabelsContains(\"key2\", \"value2\");\n    }\n\n    private void thenLabelsContains(String key, String value) {\n        assertTrue(image.getLabels().containsKey(key));\n        assertEquals(value, image.getLabels().get(key));\n    }\n\n    private void givenAnImageWithLabels() {\n        JsonObject labels = new JsonObject();\n\n        labels.addProperty(\"key1\", \"value1\");\n        labels.addProperty(\"key2\", \"value2\");\n\n        json.add(ImageDetails.LABELS, labels);\n    }\n\n    @Test\n    public void testImageWithRepoTags() {\n        givenImageData();\n        whenCreateImage();\n        thenRepoTagsSizeIs(2);\n    }\n\n    @Test\n    public void testImageWithNullRepoTags() {\n        givenAnImageWithNullRepoTags();\n        whenCreateImage();\n        thenRepoTagsSizeIs(0);\n    }\n\n    @Test\n    public void testImageWitEmptyRepoTags() {\n        givenAnImageWithEmptyRepoTags();\n        whenCreateImage();\n        thenRepoTagsSizeIs(0);\n    }\n\n    @Test\n    public void testImageWitNoRepoTags() {\n        whenCreateImage();\n        thenRepoTagsSizeIs(0);\n    }\n\n    private void givenAnImageWithNullRepoTags() {\n        json.add(ImageDetails.REPO_TAGS, JsonNull.INSTANCE);\n    }\n\n    private void givenAnImageWithEmptyRepoTags() {\n        json.add(ImageDetails.REPO_TAGS, new JsonArray());\n    }\n\n    @Test\n    public void testCreateImage() throws Exception {\n        givenImageData();\n        whenCreateImage();\n        thenValidateImage();\n    }\n\n    private void givenImageData() {\n        json.addProperty(ImageDetails.ID, \"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc\");\n        json.addProperty(ImageDetails.PARENT_ID, \"27cf784147099545\");\n        json.addProperty(ImageDetails.CREATED, 1365714795L);\n\n        json.addProperty(ImageDetails.SIZE, 24653L);\n        json.addProperty(ImageDetails.VIRTUAL_SIZE, 180116135L);\n\n        JsonArray repoTags = new JsonArray();\n        repoTags.add(\"ubuntu:12.10\");\n        repoTags.add(\"ubuntu:quantal\");\n\n        json.add(ImageDetails.REPO_TAGS, repoTags);\n    }\n\n    private void thenLabelsSizeIs(int size) {\n        assertEquals(size, image.getLabels().size());\n    }\n\n    private void thenRepoTagsSizeIs(int size) {\n        assertEquals(size, image.getRepoTags().size());\n    }\n\n    private void thenValidateImage() {\n        assertEquals(\"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc\", image.getId());\n        assertEquals(\"27cf784147099545\", image.getParentId());\n        assertEquals(1365714795L, image.getCreated());\n\n        assertEquals(24653L, image.getSize());\n        assertEquals(180116135L, image.getVirtualSize());\n    }\n\n    private void whenCreateImage() {\n        image = new ImageDetails(json);\n    }\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/BuildServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.File;\nimport java.util.Collections;\n\nimport java.util.Properties;\n\n\nimport io.fabric8.maven.docker.access.BuildOptions;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.util.DockerFileUtilTest;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport mockit.Expectations;\nimport mockit.FullVerifications;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertNotNull;\n\n\npublic class BuildServiceTest {\n\n    private static final String NEW_IMAGE_ID = \"efg789efg789\";\n    private static final String OLD_IMAGE_ID = \"abc123abc123\";\n\n    @Tested\n    private BuildService buildService;\n\n    @Injectable\n    private DockerAccess docker;\n\n    @Mocked\n    private DockerAssemblyManager dockerAssemblyManager;\n\n    private ImageConfiguration imageConfig;\n\n    @Injectable\n    private Logger log;\n\n    private String oldImageId;\n\n    @Mocked\n    private MojoParameters params;\n\n    @Mocked\n    Logger logger;\n\n    @Mocked\n    MojoParameters mojoParameters;\n\n    @Mocked\n    MavenProject mavenProject;\n\n    @Injectable\n    private QueryService queryService;\n\n    @Injectable\n    private ArchiveService archiveService;\n\n    @Injectable\n    private RegistryService registryService;\n\n    @Before\n    public void setup() throws Exception {\n        new Expectations() {{\n            archiveService.createArchive(anyString, (BuildImageConfiguration) any, (MojoParameters) any, log);\n            result = new File(\"docker-build.tar\");\n        }};\n    }\n\n    @Test\n    public void testBuildImageWithCleanup() throws Exception {\n        givenAnImageConfiguration(true);\n        givenImageIds(OLD_IMAGE_ID, NEW_IMAGE_ID);\n        whenBuildImage(true,false);\n        thenImageIsBuilt();\n        thenOldImageIsRemoved();\n    }\n\n    @Test\n    public void testBuildImageWithNoCleanup() throws Exception {\n        givenAnImageConfiguration(false);\n        givenImageIds(OLD_IMAGE_ID, NEW_IMAGE_ID);\n        whenBuildImage(false,false);\n        thenImageIsBuilt();\n        thenOldImageIsNotRemoved();\n    }\n\n    @Test\n    public void testCleanupCachedImage() throws Exception {\n        givenAnImageConfiguration(true);\n        givenImageIds(OLD_IMAGE_ID, OLD_IMAGE_ID);\n        whenBuildImage(false, false);\n        thenImageIsBuilt();\n        thenOldImageIsNotRemoved();\n    }\n\n    @Test\n    public void testCleanupNoExistingImage() throws Exception {\n        givenAnImageConfiguration(true);\n        givenImageIds(null, NEW_IMAGE_ID);\n        whenBuildImage(false, false);\n        thenImageIsBuilt();\n        thenOldImageIsNotRemoved();\n    }\n\n    @Test\n    public void testMultiStageBuild() throws Exception {\n        BuildImageConfiguration buildConfig = new BuildImageConfiguration.Builder()\n                .cleanup(\"false\")\n                .dockerFile(DockerFileUtilTest.class.getResource(\"Dockerfile_multi_stage\").getPath())\n                .filter(\"false\")\n                .build();\n\n        buildConfig.initAndValidate(logger);\n\n        imageConfig = new ImageConfiguration.Builder()\n                .name(\"build-image\")\n                .alias(\"build-alias\")\n                .buildConfig(buildConfig)\n                .build();\n\n        final ImagePullManager pullManager = new ImagePullManager(null,null, null);\n        final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()\n                .mojoParameters(mojoParameters)\n                .build();\n\n        new Expectations(mojoParameters) {{\n            mojoParameters.getProject(); result = mavenProject;\n            mavenProject.getProperties(); result = new Properties();\n        }};\n\n        File buildArchive = buildService.buildArchive(imageConfig, buildContext, \"\");\n        buildService.buildImage(imageConfig, pullManager, buildContext, buildArchive);\n\n        //verify that tries to pull both images\n        new Verifications() {{\n            queryService.hasImage(\"fabric8/s2i-java\");\n            registryService.pullImageWithPolicy(\"fabric8/s2i-java\",  pullManager, buildContext.getRegistryConfig(), false);\n            queryService.hasImage(\"fabric8/s1i-java\");\n            registryService.pullImageWithPolicy(\"fabric8/s1i-java\",  pullManager, buildContext.getRegistryConfig(), false);\n        }};\n    }\n\n    @Test\n    public void testDockerBuildArchiveOnly() throws Exception {\n        givenAnImageConfiguration(true);\n        final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()\n                .mojoParameters(mojoParameters)\n                .build();\n        File dockerArchive = buildService.buildArchive(imageConfig, buildContext, mavenProject.getBasedir().getAbsolutePath());\n        assertNotNull(dockerArchive);\n    }\n\n    @Test (expected = MojoExecutionException.class)\n    public void testDockerBuildArchiveOnlyWithInvalidPath() throws MojoExecutionException{\n        givenAnImageConfiguration(true);\n        final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()\n                .mojoParameters(mojoParameters)\n                .build();\n        File dockerArchive = buildService.buildArchive(imageConfig, buildContext, \"/i/donot/exist\");\n        assertNotNull(dockerArchive);\n    }\n\n    @Test\n    public void testTagImage() throws DockerAccessException, MojoExecutionException {\n        // Given\n        givenAnImageConfiguration(false);\n        final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()\n                .mojoParameters(mojoParameters)\n                .build();\n\n        // When\n        whenBuildImage(false, true);\n        buildService.tagImage(imageConfig.getName(), \"1.1.0\", \"quay.io/someuser\");\n\n        // Then\n        thenImageIsBuilt();\n        new Verifications() {{\n            docker.tag(imageConfig.getName(), \"quay.io/someuser/build-image:1.1.0\", true); times = 1;\n        }};\n    }\n\n    private void givenAnImageConfiguration(Boolean cleanup) {\n        BuildImageConfiguration buildConfig = new BuildImageConfiguration.Builder()\n                .cleanup(cleanup.toString())\n                .build();\n\n        imageConfig = new ImageConfiguration.Builder()\n                .name(\"build-image\")\n                .alias(\"build-alias\")\n                .buildConfig(buildConfig)\n                .build();\n    }\n\n    private void givenImageIds(final String oldImageId, final String newImageId) throws DockerAccessException {\n        this.oldImageId = oldImageId;\n        new Expectations() {{\n            queryService.getImageId(imageConfig.getName()); result = new String[] { oldImageId, newImageId };\n        }};\n    }\n\n    private void thenImageIsBuilt() throws DockerAccessException {\n        final File dockerBuildTar = new File(\"docker-build.tar\");\n        new Verifications() {{\n            docker.buildImage(imageConfig.getName(),\n                              dockerBuildTar,\n                              (BuildOptions) any);\n        }};\n    }\n\n    private void thenOldImageIsNotRemoved() throws DockerAccessException {\n        new FullVerifications(docker) {{\n\n        }};\n    }\n\n    private void thenOldImageIsRemoved() throws DockerAccessException {\n        new Verifications() {{\n            docker.removeImage(oldImageId, true);\n        }};\n    }\n\n    private void whenBuildImage(boolean cleanup, boolean nocache) throws DockerAccessException, MojoExecutionException {\n        new Expectations() {{\n            docker.buildImage(withEqual(imageConfig.getName()), (File) any, (BuildOptions) any);\n        }};\n        if (cleanup) {\n            new Expectations() {{\n                docker.removeImage(withEqual(oldImageId), withEqual(true)); result = true;\n            }};\n        }\n        final BuildService.BuildContext buildContext = new BuildService.BuildContext.Builder()\n                .mojoParameters(mojoParameters)\n                .build();\n        File dockerArchive = buildService.buildArchive(imageConfig, buildContext, \"\");\n\n        buildService.buildImage(imageConfig, params, nocache, false, Collections.<String, String>emptyMap(), dockerArchive);\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/ContainerTrackerTest.java",
    "content": "package io.fabric8.maven.docker.service;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.util.*;\n\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 14/02/16\n */\npublic class ContainerTrackerTest {\n\n    private ContainerTracker tracker;\n\n    @Before\n    public void setUp() throws Exception {\n        tracker = new ContainerTracker();\n    }\n\n    @Test\n    public void lookup() throws Exception {\n        tracker.registerContainer(\"1234\",getImageConfiguration(\"name\",\"alias\"),getPomLabel(\"test1\"));\n\n        assertEquals(\"1234\",tracker.lookupContainer(\"name\"));\n        assertEquals(\"1234\",tracker.lookupContainer(\"alias\"));\n        assertNull(tracker.lookupContainer(\"blub\"));\n    }\n\n    @Test\n    public void removeContainer() throws Exception {\n        String data[][] = new String[][] {\n            {\"1\", \"name1\", \"alias1\", \"100\", \"200\", \"stop1\", \"label1\", \"false\"},\n            {\"2\", \"name2\", \"alias2\", null, null, null, \"label2\", \"true\"}\n        };\n\n        List<ContainerTracker.ContainerShutdownDescriptor> descs = registerAtTracker(data);\n\n        ContainerTracker.ContainerShutdownDescriptor desc = tracker.removeContainer(\"1\");\n        verifyDescriptor(data[0],desc);\n        assertNull(tracker.lookupContainer(\"1\"));\n        assertNull(tracker.removeContainer(\"1\"));\n\n        assertTrue(tracker.removeShutdownDescriptors(getPomLabel(\"label1\")).isEmpty());\n        assertFalse(tracker.removeShutdownDescriptors(getPomLabel(\"label2\")).isEmpty());\n        assertTrue(tracker.removeShutdownDescriptors(getPomLabel(\"label2\")).isEmpty());\n    }\n\n    @Test\n    public void removeDescriptors() throws Exception {\n\n        String data[][] = new String[][] {\n            { \"1\", \"name1\", \"alias1\", \"100\", \"200\", \"stop1\", \"label1\", \"true\" },\n            { \"2\", \"name2\", \"alias2\", null, null, null, \"label1\", \"false\" },\n            { \"3\", \"name3\", null, null, null, null, \"label2\", \"true\" }\n        };\n\n        List<ContainerTracker.ContainerShutdownDescriptor> descs = registerAtTracker(data);\n\n        Collection<ContainerTracker.ContainerShutdownDescriptor> removed = tracker.removeShutdownDescriptors(getPomLabel(\"label1\"));\n        assertEquals(2,removed.size());\n        Iterator<ContainerTracker.ContainerShutdownDescriptor> it = removed.iterator();\n        // Reverse order\n        verifyDescriptor(data[1],it.next());\n        verifyDescriptor(data[0],it.next());\n\n        assertNull(tracker.lookupContainer(\"name1\"));\n        assertNull(tracker.lookupContainer(\"alias1\"));\n        assertNull(tracker.lookupContainer(\"name2\"));\n        assertNull(tracker.lookupContainer(\"alias2\"));\n        assertNotNull(tracker.lookupContainer(\"name3\"));\n    }\n\n    @Test\n    public void removeAll() throws Exception {\n        String data[][] = new String[][] {\n            { \"1\", \"name1\", \"alias1\", \"100\", \"200\", \"stop1\", \"label1\", \"true\" },\n            { \"2\", \"name2\", \"alias2\", null, null, null, \"label1\", \"false\" },\n            { \"3\", \"name3\", null, null, null, null, \"label2\", \"false\" }\n        };\n\n        List<ContainerTracker.ContainerShutdownDescriptor> descs = registerAtTracker(data);\n        Collection<ContainerTracker.ContainerShutdownDescriptor> removed = tracker.removeShutdownDescriptors(null);\n\n        assertEquals(3,removed.size());\n        Iterator<ContainerTracker.ContainerShutdownDescriptor> it = removed.iterator();\n        // Reverse order\n        verifyDescriptor(data[2],it.next());\n        verifyDescriptor(data[1],it.next());\n        verifyDescriptor(data[0],it.next());\n\n        assertEquals(0,tracker.removeShutdownDescriptors(null).size());\n    }\n\n    private void verifyDescriptor(String[] d, ContainerTracker.ContainerShutdownDescriptor desc) {\n        assertNotNull(desc);\n        assertEquals(desc.getContainerId(),d[0]);\n        assertEquals(desc.getImage(),d[1]);\n        if (d[2] != null) {\n            assertTrue(desc.getDescription().contains(d[2]));\n        }\n        assertEquals(desc.getShutdownGracePeriod(),parseInt(d[3]));\n        assertEquals(desc.getKillGracePeriod(),parseInt(d[4]));\n        assertEquals(desc.getPreStop(),d[5]);\n        assertEquals(desc.isBreakOnError(), Boolean.parseBoolean(d[7]));\n        assertNotNull(desc.getImageConfiguration());\n    }\n\n    private List<ContainerTracker.ContainerShutdownDescriptor> registerAtTracker(String[][] data) {\n        List<ContainerTracker.ContainerShutdownDescriptor> descriptors = new ArrayList<>();\n        for (String[] d : data) {\n            ImageConfiguration imageConfig =\n                getImageConfiguration(d[1], d[2], parseInt(d[3]), parseInt(d[4]), d[5], Boolean.parseBoolean(d[7]));\n\n            tracker.registerContainer(d[0],\n                                      imageConfig,\n                                      getPomLabel(d[6]));\n            descriptors.add(new ContainerTracker.ContainerShutdownDescriptor(imageConfig, d[0]));\n        }\n        return descriptors;\n    }\n\n    private int parseInt(String s) {\n        return s != null ? Integer.parseInt(s) : 0;\n    }\n\n    private GavLabel getPomLabel(String artifactId) {\n        return new GavLabel(\"io.fabric8\", artifactId, \"1.0.0\");\n    }\n\n    private ImageConfiguration getImageConfiguration(String name, String alias) {\n        return getImageConfiguration(name, alias, 0,0,null,false);\n    }\n\n    private ImageConfiguration getImageConfiguration(String name, String alias, int shutdown,int kill, String preStop, boolean breakOnError) {\n        WaitConfiguration waitConfig = null;\n        if (shutdown != 0 && kill != 0) {\n            WaitConfiguration.Builder builder = new WaitConfiguration.Builder()\n            .shutdown(shutdown)\n            .kill(kill);\n            if (preStop != null) {\n                builder.preStop(preStop);\n                builder.breakOnError(breakOnError);\n            }\n            waitConfig = builder.build();\n        }\n        return new ImageConfiguration.Builder()\n            .name(name)\n            .alias(alias)\n            .runConfig(new RunImageConfiguration.Builder()\n                           .wait(waitConfig)\n                           .build())\n            .build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/JibBuildServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport com.google.cloud.tools.jib.api.Credential;\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.assembly.DockerAssemblyManager;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.util.AuthConfigFactory;\nimport io.fabric8.maven.docker.util.JibServiceUtil;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport mockit.Verifications;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.settings.Settings;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\npublic class JibBuildServiceTest {\n\n    @Mocked\n    private Logger logger;\n\n    @Mocked\n    private ServiceHub serviceHub;\n\n    @Mocked\n    private Settings settings;\n\n    @Mocked\n    private MojoParameters params;\n\n    @Mocked\n    private MavenProject project;\n\n    @Mocked\n    private AuthConfigFactory authConfigFactory;\n\n    @Mocked\n    private DockerAssemblyManager dockerAssemblyManager;\n\n    @Test\n    @java.lang.SuppressWarnings(\"squid:S00112\")\n    public void testGetRegistryCredentialsForPush() throws MojoExecutionException {\n        // Given\n        ImageConfiguration imageConfiguration = getImageConfiguration();\n        Map authConfig = Collections.emptyMap();\n        RegistryService.RegistryConfig registryConfig = new RegistryService.RegistryConfig.Builder()\n                .authConfig(authConfig)\n                .authConfigFactory(authConfigFactory)\n                .settings(settings)\n                .build();\n        mockAuthConfigFactory(true, registryConfig);\n        // When\n        Credential credential = JibBuildService.getRegistryCredentials(\n                registryConfig, true, imageConfiguration, logger);\n        // Then\n        assertNotNull(credential);\n        assertEquals(\"testuserpush\", credential.getUsername());\n        assertEquals(\"testpass\", credential.getPassword());\n    }\n\n    @Test\n    @java.lang.SuppressWarnings(\"squid:S00112\")\n    public void testGetRegistryCredentialsForPull() throws MojoExecutionException {\n        // Given\n        ImageConfiguration imageConfiguration = getImageConfiguration();\n        Map authConfig = Collections.emptyMap();\n        RegistryService.RegistryConfig registryConfig = new RegistryService.RegistryConfig.Builder()\n                .authConfig(authConfig)\n                .authConfigFactory(authConfigFactory)\n                .settings(settings)\n                .build();\n        mockAuthConfigFactory(false, registryConfig);\n        // When\n        Credential credential = JibBuildService.getRegistryCredentials(\n                registryConfig, false, imageConfiguration, logger);\n        // Then\n        assertNotNull(credential);\n        assertEquals(\"testuserpull\", credential.getUsername());\n        assertEquals(\"testpass\", credential.getPassword());\n    }\n\n    @Test\n    public void testGetBuildTarArchive() throws IOException {\n        // Given\n        File projectBaseDir = Files.createTempDirectory(\"test\").toFile();\n        ImageConfiguration imageConfiguration = getImageConfiguration();\n        setupServiceHubExpectations(projectBaseDir);\n\n        // When\n        File tarArchive = JibBuildService.getBuildTarArchive(imageConfiguration, params);\n\n        // Then\n        assertNotNull(tarArchive);\n        assertEquals(\"/target/test/testimage/0.0.1/tmp/docker-build.tar\", tarArchive.getAbsolutePath().substring(projectBaseDir.getAbsolutePath().length()));\n    }\n\n\n    @Test\n    public void testGetAssemblyTarArchive() throws IOException, MojoExecutionException {\n        // Given\n        File projectBaseDir = Files.createTempDirectory(\"test\").toFile();\n        ImageConfiguration imageConfiguration = getImageConfiguration();\n        setupDockerAssemblyExpectations(projectBaseDir);\n\n        // When\n        File tarArchive = JibBuildService.getAssemblyTarArchive(imageConfiguration, serviceHub, params, logger);\n\n        // Then\n        assertNotNull(tarArchive);\n        assertEquals(\"/target/test/testimage/0.0.1/tmp/docker-build.tar\", tarArchive.getAbsolutePath().substring(projectBaseDir.getAbsolutePath().length()));\n    }\n\n    @Test\n    public void testPrependRegistry() {\n        // Given\n        ImageConfiguration imageConfiguration = getImageConfiguration();\n        // When\n        JibBuildService.prependRegistry(imageConfiguration, \"quay.io\");\n        // Then\n        assertNotNull(imageConfiguration);\n        assertEquals(\"quay.io/test/testimage:0.0.1\", imageConfiguration.getName());\n    }\n\n    @Test\n    public void testPushWithNoConfigurations(@Mocked JibServiceUtil jibServiceUtil) throws Exception {\n        // When\n        new JibBuildService(serviceHub, params, logger).push(Collections.emptyList(), 1, null, false);\n        // Then\n        // @formatter:off\n        new Verifications() {{\n            JibServiceUtil.jibPush((ImageConfiguration)any, (Credential)any, (File)any, logger); times = 0;\n        }};\n        // @formatter:on\n    }\n\n    @Test\n    public void testPushWithConfiguration(@Mocked JibServiceUtil jibServiceUtil) throws Exception {\n        // Given\n        File projectBaseDir = Files.createTempDirectory(\"test\").toFile();\n        setupServiceHubExpectations(projectBaseDir);\n        final ImageConfiguration imageConfiguration = getImageConfiguration();\n        final RegistryService.RegistryConfig registryConfig = new RegistryService.RegistryConfig.Builder()\n                .authConfigFactory(authConfigFactory)\n                .build();\n        mockAuthConfigFactory(true, registryConfig);\n        // When\n        new JibBuildService(serviceHub, params, logger).push(Collections.singletonList(imageConfiguration), 1, registryConfig, false);\n        // Then\n        // @formatter:off\n        new Verifications() {{\n            JibServiceUtil.jibPush(\n                    imageConfiguration,\n                    Credential.from(\"testuserpush\", \"testpass\"),\n                    (File)any,\n                    logger);\n            times = 1;\n        }};\n        // @formatter:on\n    }\n\n    private ImageConfiguration getImageConfiguration() {\n        return new ImageConfiguration.Builder()\n                .name(\"test/testimage:0.0.1\")\n                .buildConfig(new BuildImageConfiguration.Builder().from(\"busybox\").build())\n                .build();\n    }\n\n    @java.lang.SuppressWarnings(\"squid:S00112\")\n    private void setupServiceHubExpectations(File projectBaseDir) {\n\n        new Expectations() {{\n            project.getBasedir();\n            result = projectBaseDir;\n\n            params.getOutputDirectory();\n            result = \"target\";\n\n            params.getProject();\n            result = project;\n\n        }};\n    }\n\n    private void setupDockerAssemblyExpectations(File projectBaseDir) throws MojoExecutionException {\n        new Expectations() {{\n            dockerAssemblyManager.createDockerTarArchive(anyString, params, (BuildImageConfiguration)any, logger, null);\n            result = new File(projectBaseDir, \"target/test/testimage/0.0.1/tmp/docker-build.tar\");\n\n            serviceHub.getDockerAssemblyManager();\n            result = dockerAssemblyManager;\n        }};\n    }\n\n\n    private void mockAuthConfigFactory(boolean isPush, RegistryService.RegistryConfig registryConfig) throws MojoExecutionException {\n        new Expectations() {{\n            authConfigFactory.createAuthConfig(anyBoolean, registryConfig.isSkipExtendedAuth(), registryConfig.getAuthConfig(), registryConfig.getSettings(), null, anyString);\n            result = new AuthConfig(\"testuser\" + (isPush ? \"push\" : \"pull\"), \"testpass\", \"foo@example.com\", null, null);\n        }};\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/LoadImageTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport java.io.File;\nimport java.util.Collections;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ConfigHelper;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.MojoParameters;\nimport mockit.Expectations;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Test;\n\n\npublic class LoadImageTest {\n    @Tested\n    private BuildService buildService;\n\n    @Injectable\n    private DockerAccess docker;\n\n    private ImageConfiguration imageConfig;\n\n    @Injectable\n    private Logger log;\n\n    @Mocked\n    private MavenProject project;\n\n    @Mocked\n    private MojoParameters params;\n\n    @Injectable\n    private QueryService queryService;\n\n    @Injectable\n    private ArchiveService archiveService;\n\n    @Injectable\n    private RegistryService registryService;\n\n    private String dockerArchive;\n\n    @Test\n    public void testLoadImage() throws DockerAccessException, MojoExecutionException {\n        givenMojoParameters();\n        givenAnImageConfiguration();\n        givenDockerArchive(\"test.tar\");\n        whenBuildImage();\n        thenImageIsBuilt();\n    }\n\n    private void givenMojoParameters() {\n        new Expectations() {{\n            params.getProject();\n            project.getBasedir(); result = \"/maven-project\";\n            params.getSourceDirectory(); result = \"src/main/docker\";\n        }};\n    }\n\n    private void givenDockerArchive(String s) {\n        dockerArchive = s;\n    }\n\n    private void givenAnImageConfiguration() {\n        BuildImageConfiguration buildConfig = new BuildImageConfiguration.Builder()\n            .dockerArchive(\"test.tar\")\n            .build();\n\n        imageConfig = new ImageConfiguration.Builder()\n            .name(\"build-image\")\n            .alias(\"build-alias\")\n            .buildConfig(buildConfig)\n            .build();\n        imageConfig.initAndValidate(ConfigHelper.NameFormatter.IDENTITY,log);\n    }\n\n    private void whenBuildImage() throws DockerAccessException, MojoExecutionException {\n        buildService.buildImage(imageConfig, params, false, false, Collections.<String, String>emptyMap(), new File(\"/maven-project/src/main/docker/test.tar\"));\n    }\n\n    private void thenImageIsBuilt() throws DockerAccessException {\n        final File targetFile = new File(\"/maven-project/src/main/docker/test.tar\");\n        new Verifications() {{\n            docker.loadImage(\"build-image\", withEqual(targetFile));\n        }};\n    }\n\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/MojoExecutionServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.io.IOException;\nimport java.io.StringReader;\n\nimport mockit.Expectations;\nimport mockit.Injectable;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport mockit.Verifications;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.model.Plugin;\nimport org.apache.maven.plugin.BuildPluginManager;\nimport org.apache.maven.plugin.MojoExecution;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.plugin.descriptor.MojoDescriptor;\nimport org.apache.maven.plugin.descriptor.PluginDescriptor;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.configuration.PlexusConfiguration;\nimport org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;\nimport org.codehaus.plexus.util.xml.Xpp3DomBuilder;\nimport org.codehaus.plexus.util.xml.pull.XmlPullParserException;\nimport org.junit.Test;\n\n/**\n * @author roland\n * @since 01/07/15\n */\n\npublic class MojoExecutionServiceTest {\n\n    @Tested\n    @Mocked\n    MojoExecutionService executionService;\n\n    @Injectable\n    protected MavenProject project;\n\n    @Injectable\n    MavenSession session;\n\n    @Injectable\n    BuildPluginManager pluginManager;\n\n    @Mocked\n    PluginDescriptor pluginDescriptor;\n\n    private final String PLUGIN_NAME = \"io.fabric8:fabric8-maven-plugin\";\n    private final String GOAL_NAME = \"delete-pods\";\n\n\n    @Test\n    public void straight() throws Exception {\n        standardSetup();\n        executionService.callPluginGoal(PLUGIN_NAME + \":\" + GOAL_NAME);\n\n        new Verifications() {{}};\n    }\n\n    private void standardSetup() throws Exception {\n        new Expectations() {{\n            project.getPlugin(PLUGIN_NAME);\n            result = new Plugin();\n            pluginDescriptor.getMojo(GOAL_NAME);\n            result = createPluginDescriptor();\n\n            pluginManager.executeMojo(session, (MojoExecution) any);\n            executionService.getPluginDescriptor((MavenProject) any, (Plugin) any);\n        }};\n    }\n\n    @Test\n    public void straightWithExecutionId() throws Exception {\n        standardSetup();\n        executionService.callPluginGoal(PLUGIN_NAME + \":\" + GOAL_NAME + \"#1\");\n    }\n\n    @Test(expected = MojoExecutionException.class)\n    public void noDescriptor() throws Exception {\n        new Expectations() {{\n            project.getPlugin(PLUGIN_NAME);\n            result = new Plugin();\n            pluginDescriptor.getMojo(GOAL_NAME);\n            result = null;\n            executionService.getPluginDescriptor((MavenProject) any, (Plugin) any);\n        }};\n        executionService.callPluginGoal(PLUGIN_NAME + \":\" + GOAL_NAME);\n\n        new Verifications() {{}};\n    }\n\n    @Test(expected = MojoFailureException.class)\n    public void noPlugin() throws MojoFailureException, MojoExecutionException {\n        new Expectations() {{\n            project.getPlugin(anyString);\n            result = null;\n        }};\n\n        executionService.callPluginGoal(\"bla:blub:bla\");\n    }\n\n    @Test(expected = MojoFailureException.class)\n    public void wrongFormat() throws MojoFailureException, MojoExecutionException {\n        executionService.callPluginGoal(\"blubber\");\n    }\n\n    // ============================================================================================\n\n    private MojoDescriptor createPluginDescriptor() throws XmlPullParserException, IOException {\n        MojoDescriptor descriptor = new MojoDescriptor();\n        PlexusConfiguration config = new XmlPlexusConfiguration(Xpp3DomBuilder.build(new StringReader(\"<config name='test'><test>1</test></config>\")));\n        descriptor.setMojoConfiguration(config);\n        return descriptor;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/RegistryServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.config.ImagePullPolicy;\nimport io.fabric8.maven.docker.util.AuthConfigFactory;\nimport io.fabric8.maven.docker.util.AutoPullMode;\nimport io.fabric8.maven.docker.util.ImageName;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Mocked;\nimport mockit.Verifications;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author roland\n * @since 23.11.17\n */\npublic class RegistryServiceTest {\n\n    private Exception actualException;\n\n    // pull\n    private String imageName;\n    private ImagePullPolicy imagePullPolicy;\n    private TestCacheStore cacheStore;\n    private AutoPullMode autoPullMode;\n    private RegistryService registryService;\n    private boolean hasImage;\n    private String registry;\n    private Map authConfig;\n\n    // push\n    private ImageConfiguration imageConfiguration;\n\n    @Mocked\n    private DockerAccess docker;\n\n    @Mocked\n    private Logger logger;\n\n    @Mocked\n    private AuthConfigFactory authConfigFactory;\n\n    @Before\n    public void setup() {\n        reset();\n    }\n\n    private void reset() {\n        registryService = new RegistryService(docker, logger);\n        cacheStore = new TestCacheStore();\n        authConfig = new HashMap();\n\n        imageName = null;\n        imagePullPolicy = null;\n        autoPullMode = null;\n        hasImage = false;\n        registry = null;\n    }\n\n    @Test\n    public void pullImagePullPolicyAlways() throws Exception {\n        for (boolean hasImage : new boolean[]{ false, true }) {\n            reset();\n            givenAnImage();\n            givenImagePullPolicy(ImagePullPolicy.Always);\n            givenHasImage(hasImage);\n\n            checkPulledButNotTagged();\n        }\n    }\n\n    @Test\n    public void pullImageAutopullAlways() throws Exception {\n        for (boolean hasImage : new boolean[]{ false, true }) {\n            reset();\n            givenAnImage();\n            givenAutoPullMode(AutoPullMode.ALWAYS);\n            givenHasImage(hasImage);\n\n            checkPulledButNotTagged();\n        }\n    }\n\n    private void checkPulledButNotTagged() throws DockerAccessException {\n\n        whenAutoPullImage();\n\n        thenImageHasBeenPulled();\n        thenImageHasNotBeenTagged();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void pullImageAlwaysWhenPreviouslyPulled() throws Exception {\n        givenAnImage();\n        givenHasImage(false);\n        givenPreviousPulled(true);\n        givenImagePullPolicy(ImagePullPolicy.Always);\n\n\n        checkNotPulledAndNotTagged();\n    }\n\n    private void checkNotPulledAndNotTagged() throws DockerAccessException {\n        whenAutoPullImage();\n\n        thenImageHasNotBeenPulled();\n        thenImageHasNotBeenTagged();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void alreadyPulled() throws DockerAccessException {\n        givenAnImage();\n        givenPreviousPulled(true);\n\n        whenAutoPullImage();\n\n        thenImageHasNotBeenPulled();\n        thenImageHasNotBeenTagged();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void policyNeverWithImageAvailable() throws DockerAccessException {\n        givenAnImage();\n        givenHasImage(true);\n        givenPreviousPulled(false);\n        givenImagePullPolicy(ImagePullPolicy.Never);\n\n        whenAutoPullImage();\n\n        thenImageHasNotBeenPulled();\n        thenImageHasNotBeenTagged();\n\n    }\n\n    @Test\n    public void policyNeverWithImageNotAvailable() throws DockerAccessException {\n        givenAnImage();\n        givenHasImage(false);\n        givenPreviousPulled(false);\n        givenImagePullPolicy(ImagePullPolicy.Never);\n\n        whenAutoPullImage();\n\n        thenImageHasNotBeenPulled();\n        thenImageHasNotBeenTagged();\n        thenExceptionThrown();\n    }\n\n    private void thenExceptionThrown() {\n        assertNotNull(actualException);\n        assertTrue(actualException.getMessage().contains(imageName));\n    }\n\n    @Test\n    public void pullWithCustomRegistry() throws DockerAccessException {\n        givenAnImage(\"myregistry.com/user/test:1.0.1\");\n        givenHasImage(false);\n        givenPreviousPulled(false);\n        givenRegistry(\"anotherRegistry.com\");\n        givenImagePullPolicy(ImagePullPolicy.IfNotPresent);\n\n        whenAutoPullImage();\n\n        thenImageHasBeenPulledWithRegistry(\"myregistry.com\");\n        thenImageHasNotBeenTagged();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void tagForCustomRegistry() throws DockerAccessException {\n        givenAnImage(\"user/test:1.0.1\");\n        givenHasImage(false);\n        givenPreviousPulled(false);\n        givenRegistry(\"anotherRegistry.com\");\n        givenImagePullPolicy(ImagePullPolicy.IfNotPresent);\n\n        whenAutoPullImage();\n\n        thenImageHasBeenPulledWithRegistry(\"anotherRegistry.com\");\n        thenImageHasBeenTagged();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void pushImage() throws DockerAccessException {\n        givenAnImageConfiguration(\"user/test:1.0.1\");\n\n        whenPushImage();\n\n        thenImageHasBeenPushed();\n        thenNoExceptionThrown();\n    }\n\n    @Test\n    public void pushImageSkipped() throws DockerAccessException {\n        givenAnImageConfiguration(\"user/test:1.0.1\");\n        givenPushSkipped(true);\n\n        whenPushImage();\n\n        thenImageHasNotBeenPushed();\n        thenNoExceptionThrown();\n    }\n\n    // ====================================================================================================\n\n    private void thenNoExceptionThrown() {\n        assertNull(actualException);\n    }\n    private void thenImageHasNotBeenPulled() throws DockerAccessException {\n        new Verifications() {{\n            docker.pullImage(anyString, (AuthConfig) withNotNull(), anyString); times = 0;\n        }};\n    }\n\n    private void thenImageHasNotBeenPushed() throws DockerAccessException {\n        new Verifications() {{\n            docker.pushImage(anyString, (AuthConfig) withNotNull(), anyString, anyInt); times = 0;\n        }};\n    }\n\n    private void thenImageHasBeenPushed() throws DockerAccessException {\n        new Verifications() {{\n            docker.pushImage(anyString, (AuthConfig) withNotNull(), anyString, anyInt);\n        }};\n    }\n\n    private void thenImageHasBeenTagged() throws DockerAccessException {\n        new Verifications() {{\n            docker.tag(new ImageName(imageName).getFullName(registry), imageName, false);\n        }};\n    }\n\n\n    private void thenImageHasNotBeenTagged() throws DockerAccessException {\n        new Verifications() {{\n            docker.tag(anyString, anyString, anyBoolean); times = 0;\n        }};\n    }\n\n    private void thenImageHasBeenPulled() throws DockerAccessException {\n        thenImageHasBeenPulledWithRegistry(null);\n    }\n\n    private void thenImageHasBeenPulledWithRegistry(final String registry) throws DockerAccessException {\n        new Verifications() {{\n            docker.pullImage(imageName, (AuthConfig) withNotNull(), registry);\n        }};\n        assertTrue(cacheStore.get(imageName) != null);\n    }\n\n    private void whenAutoPullImage() {\n\n        try {\n            String iPolicyS = imagePullPolicy != null ? imagePullPolicy.toString() : null;\n            String autoPullModeS = autoPullMode != null ? autoPullMode.toString() : null;\n            ImagePullManager pullManager = new ImagePullManager(cacheStore,iPolicyS, autoPullModeS);\n            RegistryService.RegistryConfig.Builder registryConfigBuilder =\n                new RegistryService.RegistryConfig.Builder()\n                .authConfigFactory(authConfigFactory)\n                .authConfig(authConfig);\n            if (registry != null) {\n                registryConfigBuilder.registry(registry);\n            }\n            registryService.pullImageWithPolicy(imageName, pullManager, registryConfigBuilder.build(), hasImage);\n\n        } catch (Exception e) {\n            //e.printStackTrace();\n            this.actualException = e;\n        }\n    }\n\n    private void whenPushImage() {\n        try {\n            RegistryService.RegistryConfig.Builder registryConfigBuilder =\n                    new RegistryService.RegistryConfig.Builder()\n                            .authConfigFactory(authConfigFactory)\n                            .authConfig(authConfig);\n            registryService.pushImages(Collections.singleton(imageConfiguration), 1, registryConfigBuilder.build(), false);\n        } catch (Exception e) {\n            this.actualException = e;\n        }\n    }\n\n    private void givenImagePullPolicy(ImagePullPolicy policy) {\n        this.imagePullPolicy = policy;\n    }\n\n    private void givenAutoPullMode(AutoPullMode autoPullMode) {\n        this.autoPullMode = autoPullMode;\n    }\n\n    private void givenHasImage(boolean hasImage) {\n        this.hasImage = hasImage;\n    }\n\n    private void givenAuthConfig(Map authConfig) {\n        this.authConfig = authConfig;\n    }\n\n    private void givenPreviousPulled(boolean pulled) {\n        this.cacheStore.put(\"dummyKey\", pulled ? \"{ '\" + imageName + \"': true}\" : null);\n    }\n\n    private void givenAnImage() {\n        givenAnImage(\"test:latest\");\n    }\n\n    private void givenRegistry(String registry) {\n        this.registry = registry;\n    }\n\n    private void givenAnImage(String imageName) {\n        this.imageName = imageName;\n    }\n\n    private void givenAnImageConfiguration(String imageName) {\n        final BuildImageConfiguration buildImageConfiguration = new BuildImageConfiguration.Builder().build();\n        imageConfiguration = new ImageConfiguration.Builder().name(imageName).buildConfig(buildImageConfiguration).build();\n    }\n\n    private void givenPushSkipped(boolean skipPush) {\n        final BuildImageConfiguration buildImageConfiguration = new BuildImageConfiguration.Builder().skipPush(skipPush).build();\n        imageConfiguration = new ImageConfiguration.Builder(imageConfiguration).buildConfig(buildImageConfiguration).build();\n    }\n\n    private class TestCacheStore implements ImagePullManager.CacheStore {\n\n        String cache;\n\n        @Override\n        public String get(String key) {\n            return cache;\n        }\n\n        @Override\n        public void put(String key, String value) {\n            cache = value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/RunServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\nimport com.google.gson.JsonObject;\n\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.config.StopMode;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.ContainerHostConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.NetworkConfig;\nimport io.fabric8.maven.docker.config.RestartPolicy;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport io.fabric8.maven.docker.config.UlimitConfig;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.log.LogOutputSpec;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.util.JsonFactory;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * This test need to be refactored. In fact, testing Mojos must be setup correctly at all. Blame on me that there are so\n * few tests ...\n */\npublic class RunServiceTest {\n\n    private ContainerCreateConfig containerConfig;\n\n    @Mocked\n    private MavenProject project;\n\n    @Mocked\n    private MavenSession session;\n\n    @Mocked\n    private DockerAccess docker;\n\n    @Mocked\n    private Logger log;\n\n    @Mocked\n    private QueryService queryService;\n\n    private Properties properties;\n\n    private RunImageConfiguration runConfig;\n\n    private RunService runService;\n\n    private ContainerHostConfig startConfig;\n\n    private ContainerTracker tracker;\n\n    @Before\n    public void setup() {\n        tracker = new ContainerTracker();\n        properties = new Properties();\n        LogOutputSpecFactory logOutputSpecFactory = new LogOutputSpecFactory(true, true, null);\n\n        runService = new RunService(docker, queryService, tracker, logOutputSpecFactory, log);\n    }\n\n    @Test\n    public void testCreateContainerAllConfig() throws Exception {\n        /*-\n         * this is really two tests in one\n         *  - verify the start dockerRunner calls all the methods to build the container configs\n         *  - the container configs produce the correct json when all options are specified\n         *\n         * it didn't seem worth the effort to build a separate test to verify the json and then mock/verify all the calls here\n         */\n\n        new Expectations() {{\n            queryService.getContainerName(\"redisContainer1\"); result = \"db1\";\n            queryService.getContainerName(\"redisContainer2\"); result = \"db2\";\n            queryService.getContainerName(\"parentContainer\"); result = \"parentContainer\";\n            queryService.getContainerName(\"otherContainer\"); result = \"otherContainer\";\n        }};\n\n        givenARunConfiguration();\n        givenAnImageConfiguration(\"redis3\", \"db1\", \"redisContainer1\");\n        givenAnImageConfiguration(\"redis3\", \"db2\", \"redisContainer2\");\n\n        givenAnImageConfiguration(\"parent\", \"parentName\", \"parentContainer\");\n        givenAnImageConfiguration(\"other_name\", \"other:ro\", \"otherContainer\");\n\n\n        whenCreateContainerConfig(\"base\");\n\n        thenContainerConfigIsValid();\n        thenStartConfigIsValid();\n    }\n\n    // ===========================================================\n\n    private String container = \"testContainer\";\n    private int SHUTDOWN_WAIT = 500;\n    private int KILL_AFTER = 1000;\n    private VolumeConfiguration volumeConfiguration = new VolumeConfiguration.Builder()\n            .name(\"sqlserver-backup-dev\")\n            .driver(\"rexray\")\n            .opts(Collections.singletonMap(\"size\", \"50\"))\n            .build();\n\n    @Test\n    public void shutdownWithoutKeepingContainers() throws Exception {\n        new Expectations() {{\n            docker.stopContainer(container, 0);\n            log.debug(anyString, (Object[]) any); minTimes = 1;\n            docker.removeContainer(container, false);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withSubstring(\"removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfig(SHUTDOWN_WAIT, 0), false, false);\n        assertTrue(\"Waited for at least \" + SHUTDOWN_WAIT + \" ms\",\n                System.currentTimeMillis() - start >= SHUTDOWN_WAIT);\n    }\n    @Test\n    @Ignore\n    public void killafterAndShutdownWithoutKeepingContainers() throws Exception {\n        long start = System.currentTimeMillis();\n        setupForKillWait();\n\n        runService.stopContainer(container, createImageConfig(SHUTDOWN_WAIT, KILL_AFTER), false, false);\n        assertTrue(\"Waited for at least \" + (SHUTDOWN_WAIT + KILL_AFTER) + \" ms\",\n                System.currentTimeMillis() - start >= SHUTDOWN_WAIT + KILL_AFTER);\n    }\n\n    @Test\n    @Ignore\n    public void killafterWithoutKeepingContainers() throws Exception {\n        long start = System.currentTimeMillis();\n        setupForKillWait();\n\n        runService.stopContainer(container, createImageConfig(0, KILL_AFTER), false, false);\n        assertTrue(\"Waited for at least \" + (KILL_AFTER) + \" ms\",\n                   System.currentTimeMillis() - start >= KILL_AFTER);\n    }\n\n    private void setupForKillWait() throws DockerAccessException {\n        // use this to simulate something happened - timers need to be started before this method gets invoked\n        // This used to work:\n        // docker = new MockUp<DockerAccess>() {\n        //    @Mock\n        //    public void stopContainer(String contaierId, int wait) {\n        //        WaitUtil.sleep(KILL_AFTER);\n        //    }\n        ///}.getMockInstance();\n\n        new Expectations() {{\n\n            docker.stopContainer(container, (KILL_AFTER + 500) / 1000);\n            log.debug(anyString, (Object[]) any); minTimes = 1;\n            docker.removeContainer(container, false);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withSubstring(\"removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n    }\n\n    @Test\n    public void shutdownWithoutKeepingContainersAndRemovingVolumes() throws Exception {\n        new Expectations() {{\n\n            docker.stopContainer(container, 0);\n            log.debug(anyString, (Object[]) any); minTimes = 1;\n            docker.removeContainer(container, true);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withSubstring(\"removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfig(SHUTDOWN_WAIT, 0), false, true);\n        assertTrue(\"Waited for at least \" + SHUTDOWN_WAIT + \" ms\",\n                   System.currentTimeMillis() - start >= SHUTDOWN_WAIT);\n    }\n\n    @Test\n    public void shutdownWithKeepingContainer() throws Exception {\n        new Expectations() {{\n            docker.stopContainer(container, 0);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withNotEqual(\" and removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfig(SHUTDOWN_WAIT, 0), true, false);\n        assertTrue(\"No wait\",\n                   System.currentTimeMillis() - start < SHUTDOWN_WAIT);\n\n    }\n\n    @Test\n    public void shutdownWithPreStopExecConfig() throws Exception {\n\n        new Expectations() {{\n            docker.createExecContainer(container, (Arguments) withNotNull());result = \"execContainerId\";\n            docker.startExecContainer(\"execContainerId\", (LogOutputSpec) any);\n            docker.stopContainer(container, 0);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withNotEqual(\" and removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfigWithExecConfig(SHUTDOWN_WAIT), true, false);\n        assertTrue(\"No wait\",\n                   System.currentTimeMillis() - start < SHUTDOWN_WAIT);\n    }\n\n    @Test\n    public void testWithoutWait() throws Exception {\n        new Expectations() {{\n            docker.stopContainer(container, 0);\n            log.debug(anyString); times = 0;\n            docker.removeContainer(container, false);\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withSubstring(\"removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }};\n\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfig(0, 0), false, false);\n        assertTrue(\"No wait\", System.currentTimeMillis() - start < SHUTDOWN_WAIT);\n    }\n\n    @Test(expected = DockerAccessException.class)\n    public void testWithException() throws Exception {\n\n        new Expectations() {{\n            docker.stopContainer(container, 0); result = new DockerAccessException(\"Test\");\n        }};\n\n        runService.stopContainer(container, createImageConfig(SHUTDOWN_WAIT, 0), false, false);\n    }\n\n    @Test\n    public void testVolumesDuringStart() throws DockerAccessException {\n        ServiceHub hub = new ServiceHubFactory().createServiceHub(project, session, docker, log, new LogOutputSpecFactory(true, true, null));\n        List<String> volumeBinds = Collections.singletonList(\"sqlserver-backup-dev:/var/opt/mssql/data\");\n        List<VolumeConfiguration> volumeConfigurations = Collections.singletonList(volumeConfiguration);\n\n        List<String> createdVolumes = runService.createVolumesAsPerVolumeBinds(hub, volumeBinds, volumeConfigurations);\n\n        assertEquals(createdVolumes.get(0), volumeConfigurations.get(0).getName());\n        assertTrue(createdVolumes.contains(volumeConfigurations.get(0).getName()));\n    }\n\n    @Test\n    public void testStopModeWithKill() throws DockerAccessException, ExecException {\n        new Expectations() {{\n            docker.killContainer(container);\n            log.debug(anyString); times = 0;\n            docker.removeContainer(container, false);\n            log.info(withSubstring(\"Killed\"),\n                    anyString,\n                    withSubstring(\" removed\"),\n                    withSubstring(container.substring(0,12)));\n        }};\n\n        long start = System.currentTimeMillis();\n        runService.stopContainer(container, createImageConfigWithStopMode(StopMode.kill), false, false);\n        assertTrue(\"No wait\", System.currentTimeMillis() - start < SHUTDOWN_WAIT);\n    }\n\n    private ImageConfiguration createImageConfig(int wait, int kill) {\n        return new ImageConfiguration.Builder()\n                .name(\"test_name\")\n                .alias(\"testAlias\")\n                .runConfig(new RunImageConfiguration.Builder()\n                                   .wait(new WaitConfiguration.Builder()\n                                                 .shutdown(wait)\n                                                 .kill(kill)\n                                                 .build())\n                                   .build())\n                .build();\n    }\n\n    private ImageConfiguration createImageConfigWithStopMode(StopMode stopMode) {\n        return new ImageConfiguration.Builder()\n                .name(\"test_name\")\n                .alias(\"testAlias\")\n                .runConfig(new RunImageConfiguration.Builder()\n                        .stopMode(StopMode.kill)\n                        .build())\n                .build();\n    }\n\n    private ImageConfiguration createImageConfigWithExecConfig(int wait) {\n        return new ImageConfiguration.Builder()\n                .name(\"test_name\")\n                .alias(\"testAlias\")\n                .runConfig(new RunImageConfiguration.Builder()\n                                   .wait(new WaitConfiguration.Builder()\n                                                 .shutdown(wait)\n                                                 .preStop(\"pre-stop-command\")\n                                                 .postStart(\"post-start-command\")\n                                                 .build())\n                                   .build())\n                .build();\n    }\n\n    private void addToTracker(String varName, String key, String value) throws NoSuchFieldException, IllegalAccessException {\n        Field field = tracker.getClass().getDeclaredField(varName);\n        field.setAccessible(true);\n        Map<String, String> map = (Map<String, String>) field.get(tracker);\n        map.put(key, value);\n    }\n\n    // Better than poking into the private vars would be to use createAndStart() with the mock to build up the map.\n    private void givenAnImageConfiguration(String name, String alias, String containerId) throws Exception {\n        addToTracker(\"imageToContainerMap\", name, containerId);\n        addToTracker(\"aliasToContainerMap\", alias, containerId);\n    }\n\n    private void givenARunConfiguration() {\n        runConfig =\n                new RunImageConfiguration.Builder()\n                        .hostname(\"hostname\")\n                        .domainname(\"domain.com\")\n                        .user(\"user\")\n                        .shmSize(1024L)\n                        .memory(1L)\n                        .memorySwap(1L)\n                        .cpus(1000000000L)\n                        .cpuSet(\"0,1\")\n                        .cpuShares(1L)\n                        .env(env())\n                        .cmd(\"date\")\n                        .entrypoint(new Arguments(\"entrypoint\"))\n                        .extraHosts(extraHosts())\n                        .ulimits(ulimits())\n                        .workingDir(\"/foo\")\n                        .ports(ports())\n                        .links(links())\n                        .volumes(volumeConfiguration())\n                        .dns(dns())\n                        .dnsSearch(dnsSearch())\n                        .privileged(true)\n                        .capAdd(capAdd())\n                        .capDrop(capDrop())\n                        .securityOpts(securityOpts())\n                        .restartPolicy(restartPolicy())\n                        .net(\"custom_network\")\n                        .network(networkConfiguration())\n                        .readOnly(false)\n                        .autoRemove(false)\n                        .build();\n    }\n\n    private NetworkConfig networkConfiguration() {\n        NetworkConfig config = new NetworkConfig(\"custom_network\");\n        config.addAlias(\"net-alias\");\n        return config;\n    }\n    private void thenContainerConfigIsValid() throws IOException {\n        JsonObject expectedConfig = JsonFactory.newJsonObject(loadFile(\"docker/containerCreateConfigAll.json\"));\n        assertEquals(expectedConfig.toString(), containerConfig.toJson());\n    }\n\n    private void thenStartConfigIsValid() throws IOException {\n        JsonObject expectedHostConfig = JsonFactory.newJsonObject(loadFile(\"docker/containerHostConfigAll.json\"));\n        assertEquals(expectedHostConfig.toString(), startConfig.toJson());\n    }\n\n    private void whenCreateContainerConfig(String imageName) throws DockerAccessException {\n        PortMapping portMapping = runService.createPortMapping(runConfig, properties);\n\n        containerConfig = runService.createContainerConfig(imageName, runConfig, portMapping, null, properties, getBaseDirectory());\n        startConfig = runService.createContainerHostConfig(runConfig, portMapping, getBaseDirectory());\n    }\n\n    private List<String> bind() {\n        return Collections.singletonList(\"/host_tmp:/container_tmp\");\n    }\n\n    private List<String> capAdd() {\n        return Collections.singletonList(\"NET_ADMIN\");\n    }\n\n    private List<String> capDrop() {\n        return Collections.singletonList(\"MKNOD\");\n    }\n\n    private List<String> securityOpts() {\n        return Collections.singletonList(\"seccomp=unconfined\");\n    }\n\n    private List<String> dns() {\n        return Collections.singletonList(\"8.8.8.8\");\n    }\n\n    private List<String> dnsSearch() {\n        return Collections.singletonList(\"domain.com\");\n    }\n\n    private Map<String, String> env() {\n        Map<String, String> env = new HashMap<>();\n        env.put(\"foo\", \"bar\");\n\n        return env;\n    }\n    private List<UlimitConfig> ulimits(){\n        return Collections.singletonList(new UlimitConfig(\"memlock=1024:2048\"));\n    }\n\n    private List<String> extraHosts() {\n        return Collections.singletonList(\"localhost:127.0.0.1\");\n    }\n\n    private List<String> links() {\n        return Collections.unmodifiableList(Arrays.asList(\"db1\", \"db2\"));\n    }\n\n    private String loadFile(String fileName) throws IOException {\n        return IOUtils.toString(getClass().getClassLoader().getResource(fileName));\n    }\n\n    private File getBaseDirectory() {\n        return new File(getClass().getResource(\"/\").getPath());\n    }\n\n    private List<String> ports() {\n        return Collections.singletonList(\"0.0.0.0:11022:22\");\n    }\n\n    private RestartPolicy restartPolicy() {\n        return new RestartPolicy.Builder().name(\"on-failure\").retry(1).build();\n    }\n\n    private RunVolumeConfiguration volumeConfiguration() {\n        return new RunVolumeConfiguration.Builder()\n                .bind(bind())\n                .from(volumesFrom())\n                .build();\n    }\n\n    private List<String> volumesFrom() {\n        return Arrays.asList(\"parent\", \"other:ro\");\n    }\n\n    final class LogInfoMatchingExpectations extends Expectations {\n        LogInfoMatchingExpectations(String container, boolean withRemove) {\n            log.info(withSubstring(\"Stop\"),\n                     anyString,\n                     withRemove ? withSubstring(\"removed\") : withNotEqual(\" and removed\"),\n                     withSubstring(container.substring(0,12)),\n                     anyLong);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/VolumeServiceTest.java",
    "content": "package io.fabric8.maven.docker.service;\n\n\nimport com.google.gson.JsonObject;\n\nimport org.junit.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.VolumeCreateConfig;\nimport io.fabric8.maven.docker.config.VolumeConfiguration;\nimport io.fabric8.maven.docker.util.JsonFactory;\nimport mockit.Delegate;\nimport mockit.Expectations;\nimport mockit.Mocked;\n\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThat;\n\n/**\n *  Basic Unit Tests for {@link VolumeService}\n *\n *  @author Tom Burton\n *  @version Dec 16, 2016\n */\npublic class VolumeServiceTest {\n   private VolumeCreateConfig volumeConfig;\n\n   @Mocked\n   private DockerAccess docker;\n\n   /*\n    * methods to test\n    *\n    * volumeService.createVolumeConfig(volumeName, driver, driverOpts, labels);\n    * volumeService.removeVolume(volumeName);\n    */\n\n   private Map<String, String > withMap(String what) {\n      Map<String, String> map = new HashMap<String, String>();\n      map.put(what + \"Key1\", \"value1\");\n      map.put(what + \"Key2\", \"value2\");\n\n      return map;\n   }\n\n   @Test\n   public void testCreateVolumeConfig() throws Exception {\n      final VolumeConfiguration config =\n          new VolumeConfiguration.Builder()\n              .name(\"testVolume\")\n              .driver(\"test\")\n              .opts(withMap(\"opts\"))\n              .labels(withMap(\"labels\"))\n              .build();\n\n      new Expectations() {{\n         // Use a 'delegate' to verify the argument given directly. No need\n         // for an 'intermediate' return method in the service just to check this.\n         docker.createVolume(with(new Delegate<VolumeCreateConfig>() {\n            void check(VolumeCreateConfig vcc) {\n               assertThat(vcc.getName(), is(\"testVolume\"));\n               JsonObject vccJson = JsonFactory.newJsonObject(vcc.toJson());\n               assertEquals(\"test\", vccJson.get(\"Driver\").getAsString());\n            }\n         })); result = \"testVolume\";\n      }};\n\n      String volume = new VolumeService(docker).createVolume(config);\n      assertEquals(volume, \"testVolume\");\n   }\n\n   @Test\n   public void testCreateVolume() throws Exception {\n      VolumeConfiguration vc = new VolumeConfiguration.Builder()\n                                     .name(\"testVolume\")\n                                     .driver(\"test\").opts(withMap(\"opts\"))\n                                     .labels(withMap(\"labels\"))\n                                     .build();\n\n      new Expectations() {{\n         docker.createVolume((VolumeCreateConfig)any); result = \"testVolume\";\n      }};\n\n      assertThat(vc.getName(), is(\"testVolume\"));\n      String name = new VolumeService(docker).createVolume(vc);\n\n      assertThat(name, is(\"testVolume\"));\n   }\n\n   @Test\n   public void testRemoveVolume() throws Exception {\n      VolumeConfiguration vc = new VolumeConfiguration.Builder()\n            .name(\"testVolume\")\n            .driver(\"test\").opts(withMap(\"opts\"))\n            .labels(withMap(\"labels\"))\n            .build();\n\n      new Expectations() {{\n         docker.createVolume((VolumeCreateConfig) any); result = \"testVolume\";\n         docker.removeVolume(\"testVolume\");\n      }};\n\n      VolumeService volumeService = new VolumeService(docker);\n      String name = volumeService.createVolume(vc);\n      volumeService.removeVolume(name);\n   }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/service/helper/StartContainerExecutorTest.java",
    "content": "package io.fabric8.maven.docker.service.helper;\n\nimport io.fabric8.maven.docker.access.ContainerCreateConfig;\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.ExecException;\nimport io.fabric8.maven.docker.access.PortMapping;\nimport io.fabric8.maven.docker.log.LogOutputSpecFactory;\nimport io.fabric8.maven.docker.model.ContainerDetails;\nimport io.fabric8.maven.docker.service.ContainerTracker;\nimport io.fabric8.maven.docker.service.QueryService;\nimport io.fabric8.maven.docker.service.RunService;\nimport io.fabric8.maven.docker.service.ServiceHub;\nimport io.fabric8.maven.docker.util.GavLabel;\nimport io.fabric8.maven.docker.util.JsonFactory;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Test;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.LogConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.Properties;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\npublic class StartContainerExecutorTest {\n\n  @Test\n  public void getExposedPropertyKeyPart_withoutRunConfig() {\n\n    // Given\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final String actual = executor.getExposedPropertyKeyPart();\n\n    // Then\n    assertEquals(\"alias\", actual);\n\n  }\n\n\n  @Test\n  public void getExposedPropertyKeyPart_withRunConfig() {\n\n    // Given\n    final RunImageConfiguration runConfig = new RunImageConfiguration.Builder()\n        .exposedPropertyKey(\"key\")\n        .build();\n\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .runConfig(runConfig)\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final String actual = executor.getExposedPropertyKeyPart();\n\n    // Then\n    assertEquals(\"key\", actual);\n\n  }\n\n  @Test\n  public void showLogs_withoutRunConfig() {\n\n    // Given\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertFalse(actual);\n\n  }\n\n  @Test\n  public void showLogs_withoutLogConfigButFollowTrue() {\n\n    // Given\n    final RunImageConfiguration runConfig = new RunImageConfiguration.Builder()\n        .exposedPropertyKey(\"key\")\n        .build();\n\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .runConfig(runConfig)\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .follow(true)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertTrue(actual);\n\n  }\n\n  @Test\n  public void showLogs_withLogConfigDisabled() {\n\n    // Given\n    final LogConfiguration logConfig = new LogConfiguration.Builder()\n        .enabled(false)\n        .build();\n\n    final RunImageConfiguration runConfig = new RunImageConfiguration.Builder()\n        .exposedPropertyKey(\"key\")\n        .log(logConfig)\n        .build();\n\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .runConfig(runConfig)\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertFalse(actual);\n\n  }\n\n  @Test\n  public void showLogs_withLogConfigEnabled() {\n\n    // Given\n    final LogConfiguration logConfig = new LogConfiguration.Builder()\n        .enabled(true)\n        .build();\n\n    final RunImageConfiguration runConfig = new RunImageConfiguration.Builder()\n        .exposedPropertyKey(\"key\")\n        .log(logConfig)\n        .build();\n\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .runConfig(runConfig)\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertTrue(actual);\n\n  }\n\n  @Test\n  public void showLogs_withShowLogsTrue() {\n\n    // Given\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .showLogs(\"true\")\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertTrue(actual);\n  }\n\n  @Test\n  public void showLogs_withShowLogsMatchRandomImage() {\n\n    // Given\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .showLogs(\"some_random_string\")\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertFalse(actual);\n  }\n\n  @Test\n  public void showLogs_withShowLogsMatchImage() {\n\n    // Given\n    final ImageConfiguration imageConfig = new ImageConfiguration.Builder()\n        .name(\"name\")\n        .alias(\"alias\")\n        .build();\n\n    final StartContainerExecutor executor = new StartContainerExecutor.Builder()\n        .showLogs(\"name, alias\")\n        .imageConfig(imageConfig)\n        .build();\n\n    // When\n    final boolean actual = executor.showLogs();\n\n    // Then\n    assertTrue(actual);\n  }\n\n  @Test\n  public void testStartContainers(@Mocked ServiceHub hub, @Mocked DockerAccess dockerAccess, @Mocked ContainerTracker containerTracker, @Mocked Logger log) throws IOException, ExecException {\n    // Given\n    new Expectations() {{\n      dockerAccess.createContainer((ContainerCreateConfig) any, anyString);\n      result = \"container-name\";dockerAccess.getContainer(anyString);\n      result = new ContainerDetails(JsonFactory.newJsonObject(\"{\\\"NetworkSettings\\\":{\\\"IPAddress\\\":\\\"192.168.1.2\\\"}}\"));\n\n      QueryService queryService = new QueryService(dockerAccess);\n      hub.getQueryService();\n      result = queryService;\n\n      hub.getRunService();\n      result = new RunService(dockerAccess, queryService, containerTracker, new LogOutputSpecFactory(true, true, null), log);\n    }};\n    Properties projectProps = new Properties();\n    StartContainerExecutor startContainerExecutor = new StartContainerExecutor.Builder()\n            .serviceHub(hub)\n            .projectProperties(projectProps)\n            .portMapping(new PortMapping(Collections.emptyList(), projectProps))\n            .gavLabel(new GavLabel(\"io.fabric8:test:0.1.0\"))\n            .basedir(new File(\"/tmp/foo\"))\n            .containerNamePattern(\"test-\")\n            .buildTimestamp(new Date())\n            .exposeContainerProps(\"docker.container\")\n            .imageConfig(new ImageConfiguration.Builder()\n                    .name(\"name\")\n                    .alias(\"alias\")\n                    .runConfig(new RunImageConfiguration.Builder()\n                            .build())\n                    .build())\n            .build();\n\n    // When\n    String containerId = startContainerExecutor.startContainer();\n    // Then\n    assertEquals(\"container-name\", containerId);\n    assertEquals(\"container-name\", projectProps.getProperty(\"docker.container.alias.id\"));\n    assertEquals(\"192.168.1.2\", projectProps.getProperty(\"docker.container.alias.ip\"));\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/AnsiLoggerTest.java",
    "content": "package io.fabric8.maven.docker.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport static org.junit.Assert.assertEquals;\n\nimport org.apache.maven.monitor.logging.DefaultLog;\nimport org.codehaus.plexus.logging.console.ConsoleLogger;\nimport org.fusesource.jansi.Ansi;\nimport org.fusesource.jansi.AnsiConsole;\nimport org.junit.AfterClass;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @author roland\n * @since 07/10/16\n */\npublic class AnsiLoggerTest {\n    @BeforeClass\n    public static void installAnsi() {\n        AnsiConsole.systemInstall();\n    }\n\n    @Before\n    public void forceAnsiPassthrough() {\n        // Because the AnsiConsole keeps a per-VM counter of calls to systemInstall, it is\n        // difficult to force it to pass through escapes to stdout during test.\n        // Additionally, running the test in a suite (e.g. with mvn test) means other\n        // code may have already initialized or manipulated the AnsiConsole.\n        // Hence we just reset the stdout/stderr references to those captured by AnsiConsole\n        // during its static initialization and restore them after tests.\n        System.setOut(AnsiConsole.system_out);\n        System.setErr(AnsiConsole.system_err);\n    }\n\n    @AfterClass\n    public static void restoreAnsiPassthrough() {\n        AnsiConsole.systemUninstall();\n        System.setOut(AnsiConsole.out);\n        System.setErr(AnsiConsole.err);\n    }\n\n    @Test\n    public void emphasizeDebug() {\n        TestLog testLog = new TestLog() {\n            @Override\n            public boolean isDebugEnabled() {\n                return true;\n            }\n        };\n\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        logger.debug(\"Debug messages do not interpret [[*]]%s[[*]]\", \"emphasis\");\n        assertEquals(\"T>Debug messages do not interpret [[*]]emphasis[[*]]\",\n                testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeInfoWithDebugEnabled() {\n        TestLog testLog = new TestLog() {\n            @Override\n            public boolean isDebugEnabled() {\n                return true;\n            }\n        };\n\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        logger.info(\"Info messages do not apply [[*]]%s[[*]] when debug is enabled\", \"color codes\");\n        assertEquals(\"T>Info messages do not apply color codes when debug is enabled\",\n                testLog.getMessage());\n    }\n\n    @Test\n    public void verboseEnabled() {\n        String[] data = {\n            \"build\", \"Test\",\n            \"api\", null,\n            \"bla\", \"log: Unknown verbosity group bla. Ignoring...\",\n            \"all\", \"Test\",\n            \"\", \"Test\",\n            \"true\", \"Test\",\n            \"false\", null\n        };\n        for (int i = 0; i < data.length; i += 2) {\n            TestLog testLog = new TestLog();\n            AnsiLogger logger = new AnsiLogger(testLog, false, data[i], false, \"\");\n            logger.verbose(Logger.LogVerboseCategory.BUILD, \"Test\");\n            assertEquals(data[i+1], testLog.getMessage());\n        }\n    }\n    @Test\n\n    public void emphasizeInfo() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, \"build\", false, \"T>\");\n        Ansi ansi = Ansi.ansi();\n        logger.info(\"Yet another [[*]]Test[[*]] %s\", \"emphasis\");\n        assertEquals(ansi.fg(AnsiLogger.COLOR_INFO)\n                         .a(\"T>\")\n                         .a(\"Yet another \")\n                         .fgBright(AnsiLogger.COLOR_EMPHASIS)\n                         .a(\"Test\")\n                         .fg(AnsiLogger.COLOR_INFO)\n                         .a(\" emphasis\")\n                         .reset().toString(),\n                     testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeInfoSpecificColor() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        Ansi ansi = new Ansi();\n        logger.info(\"Specific [[C]]color[[C]] %s\",\"is possible\");\n        assertEquals(ansi.fg(AnsiLogger.COLOR_INFO)\n                        .a(\"T>\")\n                        .a(\"Specific \")\n                        .fg(Ansi.Color.CYAN)\n                        .a(\"color\")\n                        .fg(AnsiLogger.COLOR_INFO)\n                        .a(\" is possible\")\n                        .reset().toString(),\n                testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeInfoIgnoringEmpties() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        Ansi ansi = new Ansi();\n        // Note that the closing part of the emphasis does not need to match the opening.\n        // E.g. [[b]]Blue[[*]] works just like [[b]]Blue[[b]]\n        logger.info(\"[[b]][[*]]Skip[[*]][[*]]ping [[m]]empty strings[[/]] %s[[*]][[c]][[c]][[*]]\",\"is possible\");\n        assertEquals(ansi.fg(AnsiLogger.COLOR_INFO)\n                        .a(\"T>\")\n                        .a(\"Skipping \")\n                        .fgBright(Ansi.Color.MAGENTA)\n                        .a(\"empty strings\")\n                        .fg(AnsiLogger.COLOR_INFO)\n                        .a(\" is possible\")\n                        .reset().toString(),\n                testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeInfoSpecificBrightColor() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        Ansi ansi = new Ansi();\n        logger.info(\"Lowercase enables [[c]]bright version[[c]] of %d colors\",Ansi.Color.values().length - 1);\n        assertEquals(ansi.fg(AnsiLogger.COLOR_INFO)\n                        .a(\"T>\")\n                        .a(\"Lowercase enables \")\n                        .fgBright(Ansi.Color.CYAN)\n                        .a(\"bright version\")\n                        .fg(AnsiLogger.COLOR_INFO)\n                        .a(\" of 8 colors\")\n                        .reset().toString(),\n                testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeInfoWithoutColor() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, false, null, false, \"T>\");\n        logger.info(\"Disabling color causes logger to [[*]]interpret and remove[[*]] %s\",\"emphasis\");\n        assertEquals(\"T>Disabling color causes logger to interpret and remove emphasis\",\n                     testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeWarning() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        Ansi ansi = new Ansi();\n        logger.warn(\"%s messages support [[*]]emphasis[[*]] too\",\"Warning\");\n        assertEquals(ansi.fg(AnsiLogger.COLOR_WARNING)\n                         .a(\"T>\")\n                         .a(\"Warning messages support \")\n                         .fgBright(AnsiLogger.COLOR_EMPHASIS)\n                         .a(\"emphasis\")\n                         .fg(AnsiLogger.COLOR_WARNING)\n                         .a(\" too\")\n                         .reset().toString(),\n                     testLog.getMessage());\n    }\n\n    @Test\n    public void emphasizeError() {\n        TestLog testLog = new TestLog();\n        AnsiLogger logger = new AnsiLogger(testLog, true, null, false, \"T>\");\n        Ansi ansi = new Ansi();\n        logger.error(\"Error [[*]]messages[[*]] could emphasise [[*]]%s[[*]]\",\"many things\");\n        assertEquals(ansi.fg(AnsiLogger.COLOR_ERROR)\n                         .a(\"T>\")\n                         .a(\"Error \")\n                         .fgBright(AnsiLogger.COLOR_EMPHASIS)\n                         .a(\"messages\")\n                         .fg(AnsiLogger.COLOR_ERROR)\n                         .a(\" could emphasise \")\n                         .fgBright(AnsiLogger.COLOR_EMPHASIS)\n                         .a(\"many things\")\n                         .reset()\n                         .toString(),\n                     testLog.getMessage());\n    }\n\n\n    private class TestLog extends DefaultLog {\n        private String message;\n\n        public TestLog() {\n            super(new ConsoleLogger());\n        }\n\n        @Override\n        public void debug(CharSequence content) {\n            this.message = content.toString();\n            super.debug(content);\n        }\n\n        @Override\n        public void info(CharSequence content) {\n            this.message = content.toString();\n            super.info(content);\n        }\n\n        @Override\n        public void warn(CharSequence content) {\n            this.message = content.toString();\n            super.warn(content);\n        }\n\n        @Override\n        public void error(CharSequence content) {\n            this.message = content.toString();\n            super.error(content);\n        }\n\n        void reset() {\n            message = null;\n        }\n\n        public String getMessage() {\n            return message;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.net.InetAddress;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonNull;\nimport com.google.gson.JsonObject;\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory;\nimport mockit.Expectations;\nimport mockit.Mock;\nimport mockit.MockUp;\nimport mockit.Mocked;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.bootstrap.HttpServer;\nimport org.apache.http.impl.bootstrap.ServerBootstrap;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.settings.Server;\nimport org.apache.maven.settings.Settings;\nimport org.codehaus.plexus.PlexusContainer;\nimport org.codehaus.plexus.component.repository.exception.ComponentLookupException;\nimport org.codehaus.plexus.util.Base64;\nimport org.codehaus.plexus.util.IOUtil;\nimport org.codehaus.plexus.util.xml.Xpp3Dom;\nimport org.hamcrest.Matchers;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\nimport org.junit.rules.ExpectedException;\nimport org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;\n\nimport static java.util.Collections.singletonMap;\nimport static java.util.UUID.randomUUID;\nimport static org.apache.http.HttpStatus.SC_NOT_FOUND;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.hamcrest.Matchers.hasProperty;\nimport static org.hamcrest.Matchers.instanceOf;\nimport static org.hamcrest.Matchers.startsWith;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\n/**\n * @author roland\n * @since 29.07.14\n */\n\npublic class AuthConfigFactoryTest {\n\n    public static final String ECR_NAME = \"123456789012.dkr.ecr.bla.amazonaws.com\";\n\n    @Rule\n    public final EnvironmentVariables environmentVariables = new EnvironmentVariables();\n\n    @Mocked\n    Settings settings;\n\n    @Mocked\n    private Logger log;\n\n    private AuthConfigFactory factory;\n\n    private boolean isPush = true;\n\n    private GsonBuilder gsonBuilder;\n\n    private HttpServer httpServer;\n\n\n    public static final class MockSecDispatcher implements SecDispatcher {\n        @Mock\n        public String decrypt(String password) {\n            return password;\n        }\n    }\n\n    @Mocked\n    PlexusContainer container;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void containerSetup() throws ComponentLookupException {\n        final SecDispatcher secDispatcher = new MockSecDispatcher();\n        new Expectations() {{\n            container.lookup(SecDispatcher.ROLE, \"maven\"); minTimes = 0; result = secDispatcher;\n\n        }};\n        factory = new AuthConfigFactory(container);\n        factory.setLog(log);\n\n        gsonBuilder = new GsonBuilder();\n    }\n\n    @After\n    public void shutdownHttpServer() {\n        if (httpServer != null) {\n            httpServer.stop();\n            httpServer = null;\n        }\n    }\n\n    @Test\n    public void testEmpty() throws Exception {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                assertNull(factory.createAuthConfig(isPush,false,null,settings,null,\"blubberbla:1611\"));\n            }\n        });\n    }\n\n    @Test\n    public void testSystemProperty() throws Exception {\n        System.setProperty(\"docker.push.username\",\"roland\");\n        System.setProperty(\"docker.push.password\", \"secret\");\n        System.setProperty(\"docker.push.email\", \"roland@jolokia.org\");\n        try {\n            AuthConfig config = factory.createAuthConfig(true, false, null, settings, null, null);\n            verifyAuthConfig(config,\"roland\",\"secret\",\"roland@jolokia.org\");\n        } finally {\n            System.clearProperty(\"docker.push.username\");\n            System.clearProperty(\"docker.push.password\");\n            System.clearProperty(\"docker.push.email\");\n        }\n    }\n\n    private void testSystemProperty(String prefix) throws Exception {\n        System.setProperty(prefix + \".username\", \"roland\");\n        System.setProperty(prefix + \".password\", \"secret\");\n        System.setProperty(prefix + \".email\", \"roland@jolokia.org\");\n        try {\n            AuthConfig config = factory.createAuthConfig(true, false, null, settings, null, null);\n            verifyAuthConfig(config, \"roland\", \"secret\", \"roland@jolokia.org\");\n        } finally {\n            System.clearProperty(prefix + \".username\");\n            System.clearProperty(prefix + \".password\");\n            System.clearProperty(prefix + \".email\");\n        }\n    }\n\n    @Test\n    public void testDockerSystemProperty() throws Exception {\n        testSystemProperty(\"docker\");\n    }\n\n    @Test\n    public void testRegistrySystemProperty() throws Exception {\n        testSystemProperty(\"registry\");\n    }\n\n    @Test\n    public void testDockerSystemPropertyHasPrecedence() throws Exception {\n        System.setProperty(\"docker.username\", \"roland\");\n        System.setProperty(\"docker.password\", \"secret\");\n        System.setProperty(\"docker.email\", \"roland@jolokia.org\");\n        System.setProperty(\"registry.username\", \"_roland\");\n        System.setProperty(\"registry.password\", \"_secret1\");\n        System.setProperty(\"registry.email\", \"_1roland@jolokia.org\");\n        try {\n            AuthConfig config = factory.createAuthConfig(true, false, null, settings, null, null);\n            verifyAuthConfig(config, \"roland\", \"secret\", \"roland@jolokia.org\");\n        } finally {\n            System.clearProperty(\"docker.username\");\n            System.clearProperty(\"docker.password\");\n            System.clearProperty(\"docker.email\");\n            System.clearProperty(\"registry.username\");\n            System.clearProperty(\"registry.password\");\n            System.clearProperty(\"registry.email\");\n        }\n    }\n\n    @Test\n    public void testDockerAuthLogin() throws Exception {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                checkDockerAuthLogin(homeDir,AuthConfigFactory.DOCKER_LOGIN_DEFAULT_REGISTRY,null);\n                checkDockerAuthLogin(homeDir,\"localhost:5000\",\"localhost:5000\");\n                checkDockerAuthLogin(homeDir,\"https://localhost:5000\",\"localhost:5000\");\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginNoConfig() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File dir) throws IOException, MojoExecutionException {\n                AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"roland\", null);\n                assertNull(config);\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginFallsBackToAuthWhenCredentialHelperDoesNotMatchDomain() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),null,singletonMap(\"registry1\", \"credHelper1-does-not-exist\"));\n                AuthConfig config = factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"localhost:5000\");\n                verifyAuthConfig(config,\"roland\",\"secret\",\"roland@jolokia.org\");\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginNoAuthConfigFoundWhenCredentialHelperDoesNotMatchDomainOrAuth() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),null,singletonMap(\"registry1\", \"credHelper1-does-not-exist\"));\n                AuthConfig config = factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"does-not-exist-either:5000\");\n                assertNull(config);\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginNoErrorWhenEmailInAuthConfigContainsNullValue() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),\"roland\", \"pass\", null, \"registry:5000\", true);\n                AuthConfig config = factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"registry:5000\");\n                assertNotNull(config);\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginSelectCredentialHelper() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),\"credsStore-does-not-exist\",singletonMap(\"registry1\", \"credHelper1-does-not-exist\"));\n                expectedException.expect(MojoExecutionException.class);\n                expectedException.expectCause(Matchers.allOf(\n                        instanceOf(IOException.class),\n                        hasProperty(\"message\",startsWith(\"Failed to start 'docker-credential-credHelper1-does-not-exist get'\"))\n                ));\n                factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"registry1\");\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginSelectCredentialsStore() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),\"credsStore-does-not-exist\",singletonMap(\"registry1\", \"credHelper1-does-not-exist\"));\n                expectedException.expect(MojoExecutionException.class);\n                expectedException.expectCause(Matchers.allOf(\n                        instanceOf(IOException.class),\n                        hasProperty(\"message\",startsWith(\"Failed to start 'docker-credential-credsStore-does-not-exist get'\"))\n                ));\n                factory.createAuthConfig(isPush,false,null,settings,\"roland\",null);\n            }\n        });\n    }\n\n    @Test\n    public void testDockerLoginDefaultToCredentialsStore() throws MojoExecutionException, IOException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                writeDockerConfigJson(createDockerConfig(homeDir),\"credsStore-does-not-exist\",singletonMap(\"registry1\", \"credHelper1-does-not-exist\"));\n                expectedException.expect(MojoExecutionException.class);\n                expectedException.expectCause(Matchers.allOf(\n                        instanceOf(IOException.class),\n                        hasProperty(\"message\",startsWith(\"Failed to start 'docker-credential-credsStore-does-not-exist get'\"))\n                ));\n                factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"registry2\");\n            }\n        });\n    }\n\n    private void executeWithTempHomeDir(HomeDirExecutor executor) throws IOException, MojoExecutionException {\n        String userHome = System.getProperty(\"user.home\");\n        environmentVariables.clear(\"KUBECONFIG\");\n        try {\n            File tempDir = Files.createTempDirectory(\"d-m-p\").toFile();\n            System.setProperty(\"user.home\", tempDir.getAbsolutePath());\n            executor.exec(tempDir);\n        } finally {\n            System.setProperty(\"user.home\",userHome);\n        }\n\n    }\n\n    interface HomeDirExecutor {\n        void exec(File dir) throws IOException, MojoExecutionException;\n    }\n\n    private void checkDockerAuthLogin(File homeDir,String configRegistry,String lookupRegistry)\n            throws IOException, MojoExecutionException {\n        writeDockerConfigJson(createDockerConfig(homeDir), \"roland\", \"secret\", \"roland@jolokia.org\", configRegistry, false);\n        AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"roland\", lookupRegistry);\n        verifyAuthConfig(config,\"roland\",\"secret\",\"roland@jolokia.org\");\n    }\n\n    private File createDockerConfig(File homeDir)\n            throws IOException {\n        File dockerDir = new File(homeDir,\".docker\");\n        dockerDir.mkdirs();\n        return dockerDir;\n    }\n\n    private void writeDockerConfigJson(File dockerDir, String user, String password,\n                                       String email, String registry, boolean shouldSerializeNulls) throws IOException {\n        File configFile = new File(dockerDir,\"config.json\");\n\n        JsonObject config = new JsonObject();\n        addAuths(config,user,password,email,registry);\n\n        try (Writer writer = new FileWriter(configFile)){\n            if (shouldSerializeNulls) {\n                gsonBuilder.serializeNulls();\n            }\n            gsonBuilder.create().toJson(config, writer);\n        }\n    }\n\n    private void writeDockerConfigJson(File dockerDir,String credsStore,Map<String,String> credHelpers) throws IOException {\n        File configFile = new File(dockerDir,\"config.json\");\n\n        JsonObject config = new JsonObject();\n        if (!credHelpers.isEmpty()){\n            config.add(\"credHelpers\", JsonFactory.newJsonObject(credHelpers));\n        }\n\n        if (credsStore!=null) {\n            config.addProperty(\"credsStore\",credsStore);\n        }\n\n        addAuths(config,\"roland\",\"secret\",\"roland@jolokia.org\", \"localhost:5000\");\n\n        try (Writer writer = new FileWriter(configFile)){\n            gsonBuilder.create().toJson(config, writer);\n        }\n    }\n\n    private void addAuths(JsonObject config,String user,String password,String email,String registry) {\n        JsonObject auths = new JsonObject();\n        JsonObject value = new JsonObject();\n        value.addProperty(\"auth\", new String(Base64.encodeBase64((user + \":\" + password).getBytes())));\n        if (email == null) {\n            value.add(\"email\", JsonNull.INSTANCE);\n        } else {\n            value.addProperty(\"email\", email);\n        }\n\n        auths.add(registry, value);\n        config.add(\"auths\",auths);\n    }\n\n    @Test\n    public void testOpenShiftConfigFromPluginConfig() throws Exception {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                createOpenShiftConfig(homeDir,\"openshift_simple_config.yaml\");\n                Map<String,String> authConfigMap = new HashMap<>();\n                authConfigMap.put(\"useOpenShiftAuth\",\"true\");\n                AuthConfig config = factory.createAuthConfig(isPush, false, authConfigMap, settings, \"roland\", null);\n                verifyAuthConfig(config,\"admin\",\"token123\",null);\n            }\n        });\n    }\n\n    @Test\n    public void testOpenShiftConfigFromSystemProps() throws Exception {\n        try {\n            System.setProperty(\"docker.useOpenShiftAuth\",\"true\");\n            executeWithTempHomeDir(new HomeDirExecutor() {\n                @Override\n                public void exec(File homeDir) throws IOException, MojoExecutionException {\n                    createOpenShiftConfig(homeDir, \"openshift_simple_config.yaml\");\n                    AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"roland\", null);\n                    verifyAuthConfig(config, \"admin\", \"token123\", null);\n                }\n            });\n        } finally {\n            System.getProperties().remove(\"docker.useOpenShiftAuth\");\n        }\n    }\n\n    @Test\n    public void testOpenShiftConfigFromSystemPropsNegative() throws Exception {\n        try {\n            System.setProperty(\"docker.useOpenShiftAuth\",\"false\");\n            executeWithTempHomeDir(new HomeDirExecutor() {\n                @Override\n                public void exec(File homeDir) throws IOException, MojoExecutionException {\n                    createOpenShiftConfig(homeDir, \"openshift_simple_config.yaml\");\n                    Map<String,String> authConfigMap = new HashMap<>();\n                    authConfigMap.put(\"useOpenShiftAuth\",\"true\");\n                    AuthConfig config = factory.createAuthConfig(isPush, false, authConfigMap, settings, \"roland\", null);\n                    assertNull(config);\n                }\n            });\n        } finally {\n            System.getProperties().remove(\"docker.useOpenShiftAuth\");\n        }\n    }\n\n    @Test\n    public void testOpenShiftConfigNotLoggedIn() throws Exception {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                createOpenShiftConfig(homeDir,\"openshift_nologin_config.yaml\");\n                Map<String,String> authConfigMap = new HashMap<>();\n                authConfigMap.put(\"useOpenShiftAuth\",\"true\");\n                expectedException.expect(MojoExecutionException.class);\n                expectedException.expectMessage(containsString(\".kube/config\"));\n                factory.createAuthConfig(isPush,false,authConfigMap,settings,\"roland\",null);\n            }\n        });\n\n    }\n\n    private void createOpenShiftConfig(File homeDir,String testConfig) throws IOException {\n        File kubeDir = new File(homeDir,\".kube\");\n        kubeDir.mkdirs();\n        File config = new File(kubeDir,\"config\");\n        IOUtil.copy(getClass().getResourceAsStream(testConfig),new FileWriter(config));\n    }\n\n    @Test\n    public void testSystemPropertyNoPassword() throws Exception {\n        expectedException.expect(MojoExecutionException.class);\n        expectedException.expectMessage(\"No docker.password provided for username secret\");\n        checkException(\"docker.username\");\n    }\n\n    private void checkException(String key) throws MojoExecutionException {\n        System.setProperty(key, \"secret\");\n        try {\n            factory.createAuthConfig(isPush, false, null, settings, null, null);\n        } finally {\n            System.clearProperty(key);\n        }\n    }\n\n    @Test\n    public void testFromPluginConfiguration() throws MojoExecutionException {\n        Map pluginConfig = new HashMap();\n        pluginConfig.put(\"username\", \"roland\");\n        pluginConfig.put(\"password\", \"secret\");\n        pluginConfig.put(\"email\", \"roland@jolokia.org\");\n\n        AuthConfig config = factory.createAuthConfig(isPush, false, pluginConfig, settings, null, null);\n        verifyAuthConfig(config, \"roland\", \"secret\", \"roland@jolokia.org\");\n    }\n\n    @Test\n    public void testFromPluginConfigurationPull() throws MojoExecutionException {\n        Map pullConfig = new HashMap();\n        pullConfig.put(\"username\", \"roland\");\n        pullConfig.put(\"password\", \"secret\");\n        pullConfig.put(\"email\", \"roland@jolokia.org\");\n        Map pluginConfig = new HashMap();\n        pluginConfig.put(\"pull\",pullConfig);\n        AuthConfig config = factory.createAuthConfig(false, false, pluginConfig, settings, null, null);\n        verifyAuthConfig(config, \"roland\", \"secret\", \"roland@jolokia.org\");\n    }\n\n\n    @Test\n    public void testFromPluginConfigurationFailed() throws MojoExecutionException {\n        Map pluginConfig = new HashMap();\n        pluginConfig.put(\"username\",\"admin\");\n        expectedException.expect(MojoExecutionException.class);\n        expectedException.expectMessage(\"No 'password' given while using <authConfig> in configuration for mode DEFAULT\");\n        factory.createAuthConfig(isPush,false,pluginConfig,settings,null,null);\n    }\n\n    @Test\n    public void testFromSettingsSimple() throws MojoExecutionException {\n        setupServers();\n        AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"roland\", \"test.org\");\n        assertNotNull(config);\n        verifyAuthConfig(config, \"roland\", \"secret\", \"roland@jolokia.org\");\n    }\n\n    @Test\n    public void testFromSettingsDefault() throws MojoExecutionException {\n        setupServers();\n        AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"fabric8io\", \"test.org\");\n        assertNotNull(config);\n        verifyAuthConfig(config, \"fabric8io\", \"secret2\", \"fabric8io@redhat.com\");\n    }\n\n    @Test\n    public void testFromSettingsDefault2() throws MojoExecutionException {\n        setupServers();\n        AuthConfig config = factory.createAuthConfig(isPush, false, null, settings, \"tanja\", null);\n        assertNotNull(config);\n        verifyAuthConfig(config,\"tanja\",\"doublesecret\",\"tanja@jolokia.org\");\n    }\n\n    @Test\n    public void testWrongUserName() throws IOException, MojoExecutionException {\n        executeWithTempHomeDir(new HomeDirExecutor() {\n            @Override\n            public void exec(File homeDir) throws IOException, MojoExecutionException {\n                setupServers();\n                assertNull(factory.createAuthConfig(isPush,false,null,settings,\"roland\",\"another.repo.org\"));\n            }\n        });\n    }\n\n    @Test\n    public void getAuthConfigViaAwsSdk() throws MojoExecutionException {\n        String accessKeyId = randomUUID().toString();\n        String secretAccessKey = randomUUID().toString();\n        new MockedAwsSdkAuthConfigFactory(accessKeyId, secretAccessKey);\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, null);\n    }\n\n    @Test\n    public void ecsTaskRole() throws IOException, MojoExecutionException {\n        givenAwsSdkIsDisabled();\n        String containerCredentialsUri = \"/v2/credentials/\" + randomUUID().toString();\n        String accessKeyId = randomUUID().toString();\n        String secretAccessKey = randomUUID().toString();\n        String sessionToken = randomUUID().toString();\n        givenEcsMetadataService(containerCredentialsUri, accessKeyId, secretAccessKey, sessionToken);\n        setupEcsMetadataConfiguration(httpServer, containerCredentialsUri);\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, sessionToken);\n    }\n\n    @Test\n    public void fargateTaskRole() throws IOException, MojoExecutionException {\n        givenAwsSdkIsDisabled();\n        String containerCredentialsUri = \"v2/credentials/\" + randomUUID().toString();\n        String accessKeyId = randomUUID().toString();\n        String secretAccessKey = randomUUID().toString();\n        String sessionToken = randomUUID().toString();\n        givenEcsMetadataService(\"/\" + containerCredentialsUri, accessKeyId, secretAccessKey, sessionToken);\n        setupEcsMetadataConfiguration(httpServer, containerCredentialsUri);\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, sessionToken);\n    }\n\n    @Test\n    public void awsTemporaryCredentialsArePickedUpFromEnvironment() throws MojoExecutionException {\n        givenAwsSdkIsDisabled();\n        String accessKeyId = randomUUID().toString();\n        String secretAccessKey = randomUUID().toString();\n        String sessionToken = randomUUID().toString();\n        environmentVariables.set(\"AWS_ACCESS_KEY_ID\", accessKeyId);\n        environmentVariables.set(\"AWS_SECRET_ACCESS_KEY\", secretAccessKey);\n        environmentVariables.set(\"AWS_SESSION_TOKEN\", sessionToken);\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, sessionToken);\n    }\n\n    @Test\n    public void awsStaticCredentialsArePickedUpFromEnvironment() throws MojoExecutionException {\n        givenAwsSdkIsDisabled();\n        String accessKeyId = randomUUID().toString();\n        String secretAccessKey = randomUUID().toString();\n        environmentVariables.set(\"AWS_ACCESS_KEY_ID\", accessKeyId);\n        environmentVariables.set(\"AWS_SECRET_ACCESS_KEY\", secretAccessKey);\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, null);\n    }\n\n    @Test\n    public void incompleteAwsCredentialsAreIgnored() throws MojoExecutionException {\n        givenAwsSdkIsDisabled();\n        environmentVariables.set(\"AWS_ACCESS_KEY_ID\", randomUUID().toString());\n\n        AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, \"user\", ECR_NAME);\n\n        assertNull(authConfig);\n    }\n\n    private void setupServers() {\n        new Expectations() {{\n            List<Server> servers = new ArrayList<>();\n\n            servers.add(create(ECR_NAME, \"roland\", \"secret\", \"roland@jolokia.org\"));\n            servers.add(create(\"test.org\", \"fabric8io\", \"secret2\", \"fabric8io@redhat.com\"));\n            servers.add(create(\"test.org/roland\", \"roland\", \"secret\", \"roland@jolokia.org\"));\n            servers.add(create(\"docker.io\", \"tanja\", \"doublesecret\", \"tanja@jolokia.org\"));\n            servers.add(create(\"another.repo.org/joe\", \"joe\", \"3secret\", \"joe@foobar.com\"));\n            settings.getServers();\n            result = servers;\n        }\n\n            private Server create(String id, String user, String password, String email) {\n                Server server = new Server();\n                server.setId(id);\n                server.setUsername(user);\n                server.setPassword(password);\n                Xpp3Dom dom = new Xpp3Dom(\"configuration\");\n                Xpp3Dom emailD = new Xpp3Dom(\"email\");\n                emailD.setValue(email);\n                dom.addChild(emailD);\n                server.setConfiguration(dom);\n                return server;\n            }\n        };\n    }\n\n    private void givenEcsMetadataService(String containerCredentialsUri, String accessKeyId, String secretAccessKey, String sessionToken) throws IOException {\n        httpServer =\n            ServerBootstrap.bootstrap()\n                           .setLocalAddress(InetAddress.getLoopbackAddress())\n                           .registerHandler(\"*\", (request, response, context) -> {\n                               System.out.println(\"REQUEST: \" + request.getRequestLine());\n                               if (containerCredentialsUri.matches(request.getRequestLine().getUri())) {\n                                   response.setEntity(new StringEntity(gsonBuilder.create().toJson(ImmutableMap.of(\n                                       \"AccessKeyId\", accessKeyId,\n                                       \"SecretAccessKey\", secretAccessKey,\n                                       \"Token\", sessionToken\n                                                                                                                  ))));\n                               } else {\n                                   response.setStatusCode(SC_NOT_FOUND);\n                               }\n                           })\n                           .create();\n        httpServer.start();\n    }\n\n    private void setupEcsMetadataConfiguration(HttpServer httpServer, String containerCredentialsUri) {\n        environmentVariables.set(\"ECS_METADATA_ENDPOINT\", \"http://\" +\n                httpServer.getInetAddress().getHostAddress()+\":\" + httpServer.getLocalPort());\n        environmentVariables.set(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\", containerCredentialsUri);\n    }\n\n    private void verifyAuthConfig(AuthConfig config, String username, String password, String email, String auth) {\n        assertNotNull(config);\n        JsonObject params = gsonBuilder.create().fromJson(new String(Base64.decodeBase64(config.toHeaderValue().getBytes())), JsonObject.class);\n        assertEquals(username, params.get(AuthConfig.AUTH_USERNAME).getAsString());\n        assertEquals(password, params.get(AuthConfig.AUTH_PASSWORD).getAsString());\n        if (email != null) {\n            assertEquals(email, params.get(AuthConfig.AUTH_EMAIL).getAsString());\n        }\n        if (auth != null) {\n            assertEquals(auth, params.get(AuthConfig.AUTH_AUTH).getAsString());\n        }\n    }\n\n    private void verifyAuthConfig(AuthConfig config, String username, String password, String email) {\n        verifyAuthConfig(config, username, password, email, null);\n    }\n\n    private static void givenAwsSdkIsDisabled() {\n        new DisableAwsSdkAuthConfigFactory();\n    }\n\n    private static class MockedAwsSdkAuthConfigFactory extends MockUp<AwsSdkAuthConfigFactory> {\n        private final String accessKeyId;\n        private final String secretAccessKey;\n\n        public MockedAwsSdkAuthConfigFactory(String accessKeyId, String secretAccessKey) {\n            this.accessKeyId = accessKeyId;\n            this.secretAccessKey = secretAccessKey;\n        }\n\n        @Mock\n        public void $init(Logger log) {\n        }\n\n        @Mock\n        public AuthConfig createAuthConfig() {\n            return new AuthConfig(accessKeyId, secretAccessKey, null,null);\n        }\n\n    }\n\n    private static class DisableAwsSdkAuthConfigFactory extends MockUp<AwsSdkAuthConfigFactory> {\n\n        @Mock\n        public void $init(Logger log) {\n        }\n\n        @Mock\n        public AuthConfig createAuthConfig() {\n            return null;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/AuthConfigTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.gson.JsonObject;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.junit.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\n/**\n * @author roland\n * @since 30.07.14\n */\npublic class AuthConfigTest {\n\n\n    @Test\n    public void simpleConstructor() {\n        Map<String,String> map = new HashMap<String,String>();\n        map.put(AuthConfig.AUTH_USERNAME,\"roland\");\n        map.put(AuthConfig.AUTH_PASSWORD,\"#>secrets??\");\n        map.put(AuthConfig.AUTH_EMAIL,\"roland@jolokia.org\");\n        AuthConfig config = new AuthConfig(map);\n        check(config);\n    }\n\n    @Test\n    public void mapConstructor() {\n        AuthConfig config = new AuthConfig(\"roland\",\"#>secrets??\",\"roland@jolokia.org\",null);\n        check(config);\n    }\n\n    @Test\n    public void dockerLoginConstructor() {\n        AuthConfig config = new AuthConfig(Base64.encodeBase64String(\"roland:#>secrets??\".getBytes()),\"roland@jolokia.org\");\n        check(config);\n    }\n\n    private void check(AuthConfig config) {\n        // Since Base64.decodeBase64 handles URL-safe encoding, must explicitly check\n        // the correct characters are used\n        assertEquals(\n                \"eyJ1c2VybmFtZSI6InJvbGFuZCIsInBhc3N3b3JkIjoiIz5zZWNyZXRzPz8iLCJlbWFpbCI6InJvbGFuZEBqb2xva2lhLm9yZyJ9\",\n                config.toHeaderValue()\n        );\n\n        String header = new String(Base64.decodeBase64(config.toHeaderValue()));\n\n        JsonObject data = JsonFactory.newJsonObject(header);\n        assertEquals(\"roland\",data.get(AuthConfig.AUTH_USERNAME).getAsString());\n        assertEquals(\"#>secrets??\",data.get(AuthConfig.AUTH_PASSWORD).getAsString());\n        assertEquals(\"roland@jolokia.org\",data.get(AuthConfig.AUTH_EMAIL).getAsString());\n        assertFalse(data.has(AuthConfig.AUTH_AUTH));\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/AutoPullModeTest.java",
    "content": "package io.fabric8.maven.docker.util;/*\n * \n * Copyright 2014 Roland Huss\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\nimport org.junit.Test;\n\nimport static io.fabric8.maven.docker.util.AutoPullMode.*;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author roland\n * @since 01/03/15\n */\npublic class AutoPullModeTest {\n\n    @Test\n    public void simple() {\n        assertEquals(ON, fromString(\"on\"));\n        assertEquals(ON, fromString(\"true\"));\n        assertEquals(OFF, fromString(\"Off\"));\n        assertEquals(OFF, fromString(\"falsE\"));\n        assertEquals(ALWAYS, fromString(\"alWays\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void unknown() {\n        fromString(\"unknown\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\n\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport io.fabric8.maven.docker.config.RunImageConfiguration;\nimport io.fabric8.maven.docker.model.Container;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author marcus\n * @since 1.0.0\n */\n\npublic class ContainerNamingUtilTest {\n\n    @Test\n    public void testDefault() {\n        Assert.assertEquals(\"jolokia_demo-1\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", null),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Test\n    public void testAlias() {\n        Assert.assertEquals(\"nameAlias\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"%a\"),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Test\n    public void testTimestamp() {\n        Assert.assertEquals(\"123456\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"%t\"),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Test\n    public void testImageName() {\n        Assert.assertEquals(\"jolokia_demo\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"%n\"),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Test\n    public void testIndex() {\n        Assert.assertEquals(\"1\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"%i\"),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Test\n    public void testAll() {\n        Assert.assertEquals(\"prefix-1-nameAlias-jolokia_demo-123456-postfix\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"prefix-%i-%a-%n-%t-postfix\"),\n                                null,\n                                new Date(123456),\n                                Collections.emptySet()));\n    }\n\n    @Mocked\n    Container container1;\n\n    @Mocked\n    Container container2;\n\n    @Test\n    public void testIndexAdvanced() {\n        new Expectations() {{\n            container1.getName();result = \"container-1\";\n        }};\n        Assert.assertEquals(\"container-2\",\n                            ContainerNamingUtil.formatContainerName(\n                                imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"container-%i\"),\n                                null,\n                                new Date(123456),\n                                Collections.singleton(container1)));\n    }\n\n\n    @Test\n    public void testContainersToStop() {\n        new Expectations() {{\n            container1.getName();result = \"container-1\";\n            container2.getName();result = \"container-2\";\n\n        }};\n        Collection<Container> containers = Arrays.asList(container1, container2);\n        Collection<Container> filtered = ContainerNamingUtil.getContainersToStop(\n            imageConfiguration(\"jolokia/jolokia_demo\",\"nameAlias\", \"container-%i\"),\n                                null,\n            new Date(123456),\n            containers);\n        Assert.assertEquals(1, filtered.size());\n        Assert.assertEquals(container2, filtered.iterator().next());\n    }\n\n    private ImageConfiguration imageConfiguration(String name, String alias, String containerNamePattern) {\n        ImageConfiguration.Builder builder = new ImageConfiguration.Builder().name(name).alias(alias);\n        if (containerNamePattern != null) {\n            RunImageConfiguration runConfig = new RunImageConfiguration.Builder().containerNamePattern(containerNamePattern).build();\n            builder.runConfig(runConfig);\n        }\n        return builder.build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/CredentialHelperClientTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport mockit.Mocked;\nimport mockit.Tested;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\npublic class CredentialHelperClientTest {\n    private final Gson gson = new Gson();\n\n    @Mocked\n    private Logger logger;\n\n    private CredentialHelperClient credentialHelperClient;\n\n    private JsonObject jsonObject;\n\n    private AuthConfig authConfig;\n\n    @Before\n    public void givenCredentialHelperClient() {\n        this.credentialHelperClient = new CredentialHelperClient(logger, \"desktop\");\n    }\n\n    @Test\n    public void testUsernamePasswordAuthConfig() {\n        givenJson(\"{\\\"ServerURL\\\":\\\"registry.mycompany.com\\\",\\\"Username\\\":\\\"jane_doe\\\",\\\"Secret\\\":\\\"not-really\\\"}\");\n\n        whenJsonObjectConvertedToAuthConfig();\n        \n        assertEquals(\"username should match\", \"jane_doe\", this.authConfig.getUsername());\n        assertEquals(\"password should match\", \"not-really\", this.authConfig.getPassword());\n        assertNull(\"identityToken should not be set\", this.authConfig.getIdentityToken());\n    }\n\n    @Test\n    public void testTokenAuthConfig() {\n        givenJson(\"{\\\"ServerURL\\\":\\\"registry.cloud-provider.com\\\",\\\"Username\\\":\\\"<token>\\\",\\\"Secret\\\":\\\"gigantic-mess-of-jwt\\\"}\");\n\n        whenJsonObjectConvertedToAuthConfig();\n\n        assertNull(\"username should not be set\", this.authConfig.getUsername());\n        assertNull(\"password should not be set\", this.authConfig.getPassword());\n        assertEquals(\"identity token should match\", \"gigantic-mess-of-jwt\", this.authConfig.getIdentityToken());\n    }\n\n    private void givenJson(String json) {\n        this.jsonObject = this.gson.fromJson(json, JsonObject.class);\n    }\n\n    private void whenJsonObjectConvertedToAuthConfig() {\n        this.authConfig = this.credentialHelperClient.toAuthConfig(this.jsonObject);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/DockerFileUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;/*\n *\n * Copyright 2015 Roland Huss\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\nimport java.io.*;\nimport java.nio.file.Files;\nimport java.util.*;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.maven.artifact.repository.ArtifactRepository;\nimport org.apache.maven.artifact.repository.MavenArtifactRepository;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.settings.Settings;\nimport org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;\nimport org.codehaus.plexus.util.IOUtil;\nimport org.junit.Test;\n\nimport static io.fabric8.maven.docker.util.PathTestUtil.createTmpFile;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\n/**\n * @author roland\n * @since 21/01/16\n */\npublic class DockerFileUtilTest {\n\n    @Test\n    public void testSimple() throws Exception {\n        File toTest = copyToTempDir(\"Dockerfile_from_simple\");\n        assertEquals(\"fabric8/s2i-java\", DockerFileUtil.extractBaseImages(\n            toTest, FixedStringSearchInterpolator.create()).get(0));\n    }\n\n    @Test\n    public void testMultiStage() throws Exception {\n        File toTest = copyToTempDir(\"Dockerfile_multi_stage\");\n        Iterator<String> fromClauses = DockerFileUtil.extractBaseImages(\n             toTest, FixedStringSearchInterpolator.create()).iterator();\n\n        assertEquals(\"fabric8/s2i-java\", fromClauses.next());\n        assertEquals(\"fabric8/s1i-java\", fromClauses.next());\n        assertFalse(fromClauses.hasNext());\n    }\n\n    @Test\n    public void testMultiStageNamed() throws Exception {\n        File toTest = copyToTempDir(\"Dockerfile_multi_stage_named_build_stages\");\n        Iterator<String> fromClauses = DockerFileUtil.extractBaseImages(\n                toTest, FixedStringSearchInterpolator.create()).iterator();\n\n        assertEquals(\"fabric8/s2i-java\", fromClauses.next());\n        assertFalse(fromClauses.hasNext());\n    }\n\n    @Test\n    public void testMultiStageWithArgs() throws Exception {\n        File toTest = copyToTempDir(\"Dockerfile_multi_stage_with_args\");\n        Iterator<String> fromClauses = DockerFileUtil.extractBaseImages(\n                toTest, FixedStringSearchInterpolator.create()).iterator();\n\n        assertEquals(\"fabric8/s2i-java:latest\", fromClauses.next());\n        assertEquals(\"busybox:latest\", fromClauses.next());\n        assertFalse(fromClauses.hasNext());\n    }\n\n    @Test\n    public void testExtractArgsFromDockerfile() {\n        assertEquals(\"{VERSION=latest, FULL_IMAGE=busybox:latest}\", DockerFileUtil.extractArgsFromLines(Arrays.asList(new String[]{\"ARG\", \"VERSION:latest\"}, new String[] {\"ARG\", \"FULL_IMAGE=busybox:latest\"})).toString());\n        assertEquals(\"{user1=someuser, buildno=1}\", DockerFileUtil.extractArgsFromLines(Arrays.asList(new String[]{\"ARG\", \"user1=someuser\"}, new String[]{\"ARG\", \"buildno=1\"})).toString());\n        assertEquals(\"{NPM_VERSION=latest, NODE_VERSION=latest}\", DockerFileUtil.extractArgsFromLines(Arrays.asList(new String[]{\"ARG\",\"NODE_VERSION=\\\"latest\\\"\"}, new String[]{\"ARG\",  \"NPM_VERSION=\\\"latest\\\"\"})).toString());\n        assertEquals(\"{NPM_VERSION=latest, NODE_VERSION=latest}\", DockerFileUtil.extractArgsFromLines(Arrays.asList(new String[]{\"ARG\",\"NODE_VERSION='latest'\"}, new String[]{\"ARG\",  \"NPM_VERSION='latest'\"})).toString());\n        assertEquals(\"{MESSAGE=argument with spaces}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[] {\"ARG\", \"MESSAGE='argument with spaces'\"})).toString());\n        assertEquals(\"{MESSAGE=argument with spaces}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[] {\"ARG\", \"MESSAGE=\\\"argument with spaces\\\"\"})).toString());\n        assertEquals(\"{TARGETPLATFORM=}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"TARGETPLATFORM\"})).toString());\n        assertEquals(\"{TARGETPLATFORM=}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"TARGETPLATFORM=\"})).toString());\n        assertEquals(\"{TARGETPLATFORM=}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"TARGETPLATFORM:\"})).toString());\n        assertEquals(\"{MESSAGE=argument:two}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MESSAGE=argument:two\"})).toString());\n        assertEquals(\"{MESSAGE2=argument=two}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MESSAGE2=argument=two\"})).toString());\n        assertEquals(\"{VER=0.0.3}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER=0.0.3\"})).toString());\n        assertEquals(\"{VER={0.0.3}}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER={0.0.3}\"})).toString());\n        assertEquals(\"{VER=[0.0.3]}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER=[0.0.3]\"})).toString());\n        assertEquals(\"{VER={5,6}}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER={5,6}\"})).toString());\n        assertEquals(\"{VER={5,6}}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER={5,6}\"})).toString());\n        assertEquals(\"{VER={}}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER={}\"})).toString());\n        assertEquals(\"{VER=====}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"VER=====\"})).toString());\n        assertEquals(\"{MESSAGE=:message}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MESSAGE=:message\"})).toString());\n        assertEquals(\"{MYAPP_IMAGE=myorg/myapp:latest}\", DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MYAPP_IMAGE=myorg/myapp:latest\"})).toString());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidArgWithSpacesFromDockerfile() {\n        DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MY_IMAGE image with spaces\"}));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidArgWithTrailingArgumentFromDockerfile() {\n        DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MESSAGE=foo bar\"}));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testInvalidArgWithArrayWithSpaceFromDockerfile() {\n        DockerFileUtil.extractArgsFromLines(Collections.singletonList(new String[]{\"ARG\", \"MESSAGE=[5, 6]\"}));\n    }\n\n    @Test\n    public void testMultiStageNamedWithDuplicates() throws Exception {\n        File toTest = copyToTempDir(\"Dockerfile_multi_stage_named_redundant_build_stages\");\n        Iterator<String> fromClauses = DockerFileUtil.extractBaseImages(\n                toTest, FixedStringSearchInterpolator.create()).iterator();\n\n        assertEquals(\"centos\", fromClauses.next());\n        assertFalse(fromClauses.hasNext());\n\n    }\n\n    private File copyToTempDir(String resource) throws IOException {\n        File dir = Files.createTempDirectory(\"d-m-p\").toFile();\n        File ret = new File(dir, \"Dockerfile\");\n        try (FileOutputStream os = new FileOutputStream(ret)) {\n            IOUtil.copy(getClass().getResourceAsStream(resource), os);\n        }\n        return ret;\n    }\n\n    @Test\n    public void interpolate() throws Exception {\n        MojoParameters params = mockMojoParams();\n        Map<String, String> filterMapping = new HashMap<>();\n        filterMapping.put(\"none\", \"false\");\n        filterMapping.put(\"var\", \"${*}\");\n        filterMapping.put(\"at\", \"@\");\n\n        for (Map.Entry<String, String> entry : filterMapping.entrySet()) {\n            for (int i = 1; i < 2; i++) {\n                File dockerFile = getDockerfilePath(i, entry.getKey());\n                File expectedDockerFile = new File(dockerFile.getParent(), dockerFile.getName() + \".expected\");\n                File actualDockerFile = createTmpFile(dockerFile.getName());\n                FixedStringSearchInterpolator interpolator = DockerFileUtil.createInterpolator(params, entry.getValue());\n                FileUtils.write(actualDockerFile, DockerFileUtil.interpolate(dockerFile, interpolator), \"UTF-8\");\n                // Compare text lines without regard to EOL delimiters\n                assertEquals(FileUtils.readLines(expectedDockerFile), FileUtils.readLines(actualDockerFile));\n            }\n        }\n    }\n\n    private File getDockerfilePath(int i, String dir) {\n        ClassLoader classLoader = getClass().getClassLoader();\n        return new File(classLoader.getResource(\n            String.format(\"interpolate/%s/Dockerfile_%d\", dir, i)).getFile());\n    }\n\n    private MojoParameters mockMojoParams() {\n        MavenProject project = new MavenProject();\n        project.setArtifactId(\"docker-maven-plugin\");\n\n        Properties projectProperties = project.getProperties();\n        projectProperties.put(\"base\", \"java\");\n        projectProperties.put(\"name\", \"guenther\");\n        projectProperties.put(\"age\", \"42\");\n        projectProperties.put(\"ext\", \"png\");\n\n        Settings settings = new Settings();\n        ArtifactRepository localRepository = new MavenArtifactRepository() {\n            public String getBasedir() {\n                return \"repository\";\n            }\n        };\n        @SuppressWarnings(\"deprecation\")\n        MavenSession session = new MavenSession(null, settings, localRepository, null, null, Collections.<String>emptyList(), \".\", null, null, new Date(System.currentTimeMillis()));\n        session.getUserProperties().setProperty(\"cliOverride\", \"cliValue\"); // Maven CLI override: -DcliOverride=cliValue\n        session.getSystemProperties().put(\"user.name\", \"somebody\"); // Java system property: -Duser.name=somebody\n        return new MojoParameters(session, project, null, null, null, settings, \"src\", \"target\", Collections.singletonList(project));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/DockerPathUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.io.File;\nimport static io.fabric8.maven.docker.util.PathTestUtil.DOT;\nimport static io.fabric8.maven.docker.util.PathTestUtil.SEP;\nimport static io.fabric8.maven.docker.util.PathTestUtil.createTmpFile;\nimport static io.fabric8.maven.docker.util.PathTestUtil.getFirstDirectory;\nimport static io.fabric8.maven.docker.util.PathTestUtil.join;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Path manipulation tests\n */\npublic class DockerPathUtilTest {\n\n    private final String className = DockerPathUtilTest.class.getSimpleName();\n\n    /**\n     * A sample relative path, that does not begin or end with a file.separator character\n     */\n    private final String RELATIVE_PATH = \"relative\" + SEP + \"path\";\n\n    /**\n     * A sample absolute path, which begins with a file.separator character (or drive letter on the Windows platform)\n     */\n    private final String ABS_BASE_DIR = createTmpFile(className).getAbsolutePath();\n\n    /**\n     * A sample relative path (no different than {@link #RELATIVE_PATH}), provided as the member name is\n     * self-documenting in the test.\n     */\n    private final String REL_BASE_DIR = \"base\" + SEP + \"directory\";\n\n    @Test\n    public void resolveAbsolutelyWithRelativePath() {\n        String toResolve = RELATIVE_PATH; // relative/path\n        String absBaseDir = ABS_BASE_DIR; // /base/directory\n\n        // '/base/directory' and 'relative/path' to '/base/directory/relative/path'\n        assertEquals(new File(absBaseDir + SEP + toResolve),\n                DockerPathUtil.resolveAbsolutely(toResolve, absBaseDir));\n    }\n\n    @Test\n    public void resolveAbsolutelyWithRelativePathAndTrailingSlash() {\n        String toResolve = RELATIVE_PATH + SEP; // relative/path/\n        String absBaseDir = ABS_BASE_DIR;       // /base/directory\n\n        // '/base/directory' and 'relative/path/' to '/base/directory/relative/path'\n        assertEquals(new File(absBaseDir + SEP + toResolve),\n                DockerPathUtil.resolveAbsolutely(toResolve, absBaseDir));\n    }\n\n    @Test\n    public void resolveAbsolutelyWithTrailingSlashWithRelativePath() {\n        String toResolve = RELATIVE_PATH;        // relative/path\n        String absBaseDir = ABS_BASE_DIR + SEP;  // /base/directory/\n\n        // '/base/directory/' and 'relative/path' to '/base/directory/relative/path'\n        assertEquals(new File(absBaseDir + toResolve),\n                DockerPathUtil.resolveAbsolutely(toResolve, absBaseDir));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void resolveAbsolutelyWithRelativePathAndRelativeBaseDir() throws IllegalArgumentException {\n        DockerPathUtil.resolveAbsolutely(RELATIVE_PATH, REL_BASE_DIR);\n    }\n\n    /**\n     * The supplied base directory is relative, but isn't used because the supplied path is absolute.\n     */\n    @Test\n    public void resolveAbsolutelyWithAbsolutePathAndRelativeBaseDir() {\n        String absolutePath = createTmpFile(className).getAbsolutePath();\n        assertEquals(new File(absolutePath), DockerPathUtil.resolveAbsolutely(absolutePath, REL_BASE_DIR));\n    }\n\n    @Test\n    public void resolveAbsolutelyWithExtraSlashes() throws Exception {\n        String toResolve = RELATIVE_PATH + SEP + SEP; // relative/path//\n\n        // '/base/directory' and 'relative/path//' to '/base/directory/relative/path'\n        assertEquals(new File(ABS_BASE_DIR + SEP + RELATIVE_PATH),\n                DockerPathUtil.resolveAbsolutely(toResolve, ABS_BASE_DIR));\n    }\n\n    @Test\n    public void resolveAbsolutelyWithRelativeParentPath() throws Exception {\n        String toResolve = join(SEP, DOT + DOT, RELATIVE_PATH); // ../relative/path\n\n        // '/base/directory' and '../relative/path' to '/base/relative/path'\n        assertEquals(new File(new File(ABS_BASE_DIR).getParent(), RELATIVE_PATH),\n                DockerPathUtil.resolveAbsolutely(toResolve, ABS_BASE_DIR));\n    }\n\n    @Test\n    @Ignore(\"TODO: what does PathUtil do, if anything, when encountering backward slashes?\")\n    public void resolveAbsolutelyWithBackwardSlashes() throws Exception {\n        String toResolve = RELATIVE_PATH.replace(\"/\", \"\\\\\");\n\n        assertEquals(new File(ABS_BASE_DIR + \"/\" + RELATIVE_PATH),\n                DockerPathUtil.resolveAbsolutely(toResolve, ABS_BASE_DIR));\n    }\n\n    @Test\n    @Ignore(\"TODO: there is no parent to the root directory, so how can '../../relative/path' be resolved?\")\n    public void resolveNonExistentPath() throws Exception {\n        String toResolve = join(SEP, DOT + DOT, DOT + DOT, \"relative\", \"path\");        // ../../relative/path\n        String rootDir = getFirstDirectory(\n                createTmpFile(DockerPathUtilTest.class.getName())).getAbsolutePath();  // /\n\n        // '/' and '../../relative/path' to ??\n        assertEquals(new File(rootDir, RELATIVE_PATH), DockerPathUtil.resolveAbsolutely(toResolve, rootDir));\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/EnvUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.*;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\nimport junitparams.JUnitParamsRunner;\nimport junitparams.Parameters;\nimport junitparams.naming.TestCaseName;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 14.10.14\n */\n@RunWith(JUnitParamsRunner.class)\npublic class EnvUtilTest {\n\n\n    @Test\n    public void splitPath() {\n        Iterator<String[]> it = EnvUtil.splitOnLastColon(Arrays.asList(\"db\", \"postgres:9:db\", \"postgres:db\", \"atlast:\")).iterator();\n        String[][] expected = new String[][] {\n                { \"db\", \"db\"},\n                { \"postgres:9\",\"db\"},\n                { \"postgres\", \"db\"},\n                { \"atlast\", \"\"}\n        };\n        for (int i = 0; i < expected.length; i++) {\n            String[] got = it.next();\n            assertEquals(2,got.length);\n            assertEquals(expected[i][0],got[0]);\n            assertEquals(expected[i][1],got[1]);\n        }\n        assertFalse(it.hasNext());\n    }\n\n    @Test\n    public void removeEmptyEntries() {\n        assertEquals(ImmutableList.of(\"ein\"),  EnvUtil.removeEmptyEntries(Arrays.asList(null, \"\", \" ein\", \" \")));\n        assertEquals(ImmutableList.of(), EnvUtil.removeEmptyEntries(null));\n    }\n\n    @Test\n    public void splitAtCommas() {\n        Iterable<String> it = EnvUtil.splitAtCommasAndTrim(Arrays.asList(\"db,postgres:9:db\", \"postgres:db\"));\n        Iterable<String> expected = ImmutableList.of (\"db\", \"postgres:9:db\",\"postgres:db\");\n        assertTrue(Iterables.elementsEqual(it, expected));\n    }\n\n    public void assertEmptyList(Iterable<String> actual) {\n        assertTrue(Iterables.elementsEqual(Collections.emptyList(), actual));\n    }\n    @Test\n    public void splitAtCommasEmpty() {\n        assertEmptyList(EnvUtil.splitAtCommasAndTrim(Collections.<String>emptyList()));\n    }\n\n    @Test\n    public void splitAtCommasSingleEmpty() {\n        assertEmptyList(EnvUtil.splitAtCommasAndTrim(Arrays.asList(\"\")));\n    }\n\n    @Test\n    public void splitAtCommasNullList() {\n        assertEmptyList(EnvUtil.splitAtCommasAndTrim(null));\n    }\n\n    // null occurs when <links><link></link></links>\n    @Test\n    public void splitAtCommasNullInList() {\n        assertEmptyList(EnvUtil.splitAtCommasAndTrim(Collections.<String>singletonList(null)));\n    }\n\n    @Test\n    @TestCaseName(\"{method}: input \\\"{0}\\\" splits to {1}\")\n    @Parameters\n    public void splitOnSpace(String input, String[] expected) {\n        String[] result = EnvUtil.splitOnSpaceWithEscape(input);\n        assertEquals(expected.length, result.length);\n        for (int j = 0; j < expected.length; j++) {\n            assertEquals(expected[j],result[j]);\n        }\n    }\n\n    private Object parametersForSplitOnSpace() {\n        return $(\n            $(\"bla blub\", new String[] { \"bla\", \"blub\"}),\n            $(\"bla\\\\ blub\", new String[] {\"bla blub\"}),\n            $(\"bla blub\\\\ blubber\", new String[] { \"bla\", \"blub blubber\"})\n                );\n    }\n\n    @Test\n    public void extractMapFromProperties() {\n        Properties props = getTestProperties(\n                \"bla.hello\",\"world\",\n                \"bla.max\",\"morlock\",\n                \"bla.\"+EnvUtil.PROPERTY_COMBINE_POLICY_SUFFIX, \"ignored-since-it-is-reserved\",\n                \"blub.not\",\"aMap\");\n        Map<String,String> result = EnvUtil.extractFromPropertiesAsMap(\"bla\", props);\n        assertEquals(2,result.size());\n        assertEquals(\"world\",result.get(\"hello\"));\n        assertEquals(\"morlock\",result.get(\"max\"));\n    }\n\n    @Test\n    public void extractListFromProperties() {\n        Properties props = getTestProperties(\n                \"bla.2\",\"world\",\n                \"bla.1\",\"hello\",\n                \"bla.blub\",\"last\",\n                \"bla.\"+EnvUtil.PROPERTY_COMBINE_POLICY_SUFFIX, \"ignored-since-it-is-reserved\",\n                \"blub.1\",\"unknown\");\n        List<String> result = EnvUtil.extractFromPropertiesAsList(\"bla\", props);\n        assertEquals(3,result.size());\n        assertArrayEquals(new String[]{\"hello\", \"world\", \"last\"}, new ArrayList(result).toArray());\n    }\n\n    @Test\n    @TestCaseName(\"{method}: expression {0} => variable {1}\")\n    @Parameters\n    public void mavenPropertyExtract(String expression, String varName) {\n        assertEquals(varName,EnvUtil.extractMavenPropertyName(expression));\n    }\n\n    private Object parametersForMavenPropertyExtract() {\n        return $(\n            $(\"${var1}\", \"var1\"),\n            $(\"${  var2}\", \"var2\"),\n            $(\" ${ var3}  \", \"var3\"),\n            $(\"nonvar\", null),\n            $(\"${nonvar\", null)\n                );\n    }\n\n    @Test\n    @TestCaseName(\"{method}: max({0},{1}) = {2}, {0} >= {1} ? {3}\")\n    @Parameters\n    public void versionChecks(String versionA, String versionB, String largerVersion, boolean isGreaterOrEquals) {\n        assertEquals(largerVersion,EnvUtil.extractLargerVersion(versionA,versionB));\n        assertEquals(isGreaterOrEquals, EnvUtil.greaterOrEqualsVersion(versionA,versionB));\n    }\n\n    private Object parametersForVersionChecks() {\n        return $(\n            $(null, null, null, false),\n            $(\"1.10\", null, \"1.10\", true),\n            $(null, \"1.10\", \"1.10\", false),\n            $(\"1.22\", \"1.10\", \"1.22\", true),\n            $(\"1.10\", \"1.25\", \"1.25\", false),\n            $(\"1.23\", \"1.23\", \"1.23\", true),\n            $(\"1.23.1\", \"1.23\", \"1.23.1\", true),\n            $(\"1.25\", \"1.25.1\", \"1.25.1\", false),\n            $(\"1.23.1\", \"2.0\", \"2.0\", false)\n                );\n    }\n\n\n    @Test\n    public void fixupPath() throws Exception {\n        String[] data = new String[] {\n            \"my/regular/path\", \"my/regular/path\",\n            \"c:\\\\windows\\\\path\", \"/c/windows/path\",\n            \"Z:\\\\yet another\\\\path\", \"/z/yet another/path\"\n        };\n\n        for (int i = 0; i < data.length; i+=2) {\n            assertEquals(data[i+1], EnvUtil.fixupPath(data[i]));\n        }\n\n\n    }\n\n    private Properties getTestProperties(String ... vals) {\n        Properties ret = new Properties();\n        for (int i = 0; i < vals.length; i+=2) {\n            ret.setProperty(vals[i],vals[i+1]);\n        }\n        return ret;\n    }\n\n    private Object $(Object ... o) { return o; }\n\n    @Test\n    public void ensureRegistryHttpUrl() {\n        String[] data = {\n            \"https://index.docker.io/v1/\", \"https://index.docker.io/v1/\",\n            \"index.docker.io/v1/\", \"https://index.docker.io/v1/\",\n            \"http://index.docker.io/v1/\", \"http://index.docker.io/v1/\",\n            \"registry.fuse-ignite.openshift.com\", \"https://registry.fuse-ignite.openshift.com\"\n        };\n\n        for (int i = 0; i < data.length; i +=2) {\n            assertEquals(\">> \" + data[i], data[i+1], EnvUtil.ensureRegistryHttpUrl(data[i]));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/GavLabelTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n/*\n *\n * Copyright 2014 Roland Huss\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\n/**\n * @author roland\n * @since 31/03/15\n */\npublic class GavLabelTest {\n\n    String g = \"io.fabric8\";\n    String a = \"demo\";\n    String v = \"0.0.1\";\n    String coord = g + \":\" + a + \":\" + v;\n\n    @Test\n    public void simple() throws Exception {\n        GavLabel label = new GavLabel(g, a, v);\n        assertTrue(label.getValue().equals(coord));\n    }\n\n    @Test\n    public void dontMatch() {\n        GavLabel label = new GavLabel(g, a, v);\n        assertFalse(label.equals(new GavLabel(g, a, \"2.1.1\")));\n    }\n\n    @Test\n    public void match() {\n        GavLabel label = new GavLabel(g, a, v);\n        assertTrue(label.equals(new GavLabel(g, a, v)));\n    }\n\n    @Test\n    public void parse() {\n        GavLabel label = new GavLabel(coord);\n        assertEquals(coord, label.getValue());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void invalid() {\n        new GavLabel(\"bla\");\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/ImageArchiveUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.regex.PatternSyntaxException;\nimport java.util.zip.GZIPOutputStream;\n\nimport org.apache.commons.compress.archivers.tar.TarArchiveEntry;\nimport org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\nimport io.fabric8.maven.docker.model.ImageArchiveManifest;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestAdapter;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestEntry;\nimport io.fabric8.maven.docker.model.ImageArchiveManifestEntryAdapter;\n\npublic class ImageArchiveUtilTest {\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void readEmptyArchive() throws IOException {\n        byte[] emptyTar;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            tarOutput.finish();\n            emptyTar = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(emptyTar));\n        Assert.assertNull(manifest);\n    }\n\n    @Test\n    public void readEmptyCompressedArchive() throws IOException {\n        byte[] emptyTar;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n             GZIPOutputStream gzip = new GZIPOutputStream(baos);\n             TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(gzip)) {\n            tarOutput.finish();\n            gzip.finish();\n            emptyTar = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(emptyTar));\n        Assert.assertNull(manifest);\n    }\n\n    @Test\n    public void readEmptyArchiveFromStreamWithoutMarkSupport() throws IOException {\n        byte[] emptyTar;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            tarOutput.finish();\n            emptyTar = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(emptyTar) {\n            public boolean markSupported() { return false; }\n        });\n        Assert.assertNull(manifest);\n    }\n\n    @Test\n    public void readEmptyArchiveFromFile() throws IOException {\n        File temporaryTar = temporaryFolder.newFile();\n\n        try (FileOutputStream fileOutput = new FileOutputStream(temporaryTar);\n             TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(fileOutput)) {\n            tarOutput.finish();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(temporaryTar);\n        Assert.assertNull(manifest);\n    }\n\n    @Test\n    public void readUnrelatedArchive() throws IOException {\n        byte[] archiveBytes;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            final byte[] entryData = UUID.randomUUID().toString().getBytes();\n            TarArchiveEntry tarEntry = new TarArchiveEntry(\"unrelated.data\");\n            tarEntry.setSize(entryData.length);\n            tarOutput.putArchiveEntry(tarEntry);\n            tarOutput.write(entryData);\n            tarOutput.closeArchiveEntry();\n            tarOutput.finish();\n            archiveBytes = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(archiveBytes));\n        Assert.assertNull(manifest);\n    }\n\n    @Test(expected = JsonParseException.class)\n    public void readInvalidManifestInArchive() throws IOException {\n        byte[] archiveBytes;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            final byte[] entryData = (\"}\" + UUID.randomUUID().toString() + \"{\").getBytes();\n            TarArchiveEntry tarEntry = new TarArchiveEntry(ImageArchiveUtil.MANIFEST_JSON);\n            tarEntry.setSize(entryData.length);\n            tarOutput.putArchiveEntry(tarEntry);\n            tarOutput.write(entryData);\n            tarOutput.closeArchiveEntry();\n            tarOutput.finish();\n            archiveBytes = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(archiveBytes));\n        Assert.assertNull(manifest);\n    }\n\n    @Test\n    public void readInvalidJsonInArchive() throws IOException {\n        byte[] archiveBytes;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            final byte[] entryData = (\"}\" + UUID.randomUUID().toString() + \"{\").getBytes();\n            TarArchiveEntry tarEntry = new TarArchiveEntry(\"not-the-\" + ImageArchiveUtil.MANIFEST_JSON);\n            tarEntry.setSize(entryData.length);\n            tarOutput.putArchiveEntry(tarEntry);\n            tarOutput.write(entryData);\n            tarOutput.closeArchiveEntry();\n            tarOutput.finish();\n            archiveBytes = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(archiveBytes));\n        Assert.assertNull(manifest);\n    }\n\n    protected JsonArray createBasicManifestJson() {\n        JsonObject entryJson = new JsonObject();\n\n        entryJson.addProperty(ImageArchiveManifestEntryAdapter.CONFIG, \"image-id-sha256.json\");\n\n        JsonArray repoTagsJson = new JsonArray();\n        repoTagsJson.add(\"test/image:latest\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.REPO_TAGS, repoTagsJson);\n\n        JsonArray layersJson = new JsonArray();\n        layersJson.add(\"layer-id-sha256/layer.tar\");\n        entryJson.add(ImageArchiveManifestEntryAdapter.LAYERS, layersJson);\n\n        JsonArray manifestJson = new JsonArray();\n        manifestJson.add(entryJson);\n\n        return manifestJson;\n    }\n\n    @Test\n    public void readValidArchive() throws IOException {\n        final byte[] entryData = new Gson().toJson(createBasicManifestJson()).getBytes(StandardCharsets.UTF_8);\n        final byte[] relatedData = new Gson().toJson(new JsonObject()).getBytes(StandardCharsets.UTF_8);\n\n        byte[] archiveBytes;\n\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n             TarArchiveOutputStream tarOutput = new TarArchiveOutputStream(baos)) {\n            TarArchiveEntry tarEntry;\n\n            tarEntry = new TarArchiveEntry(\"image-id-sha256.json\");\n            tarEntry.setSize(relatedData.length);\n            tarOutput.putArchiveEntry(tarEntry);\n            tarOutput.write(relatedData);\n            tarOutput.closeArchiveEntry();\n\n            tarEntry = new TarArchiveEntry(ImageArchiveUtil.MANIFEST_JSON);\n            tarEntry.setSize(entryData.length);\n            tarOutput.putArchiveEntry(tarEntry);\n            tarOutput.write(entryData);\n            tarOutput.closeArchiveEntry();\n\n            tarOutput.finish();\n            archiveBytes = baos.toByteArray();\n        }\n\n        ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(new ByteArrayInputStream(archiveBytes));\n        Assert.assertNotNull(manifest);\n        Assert.assertNotNull(manifest.getEntries());\n        Assert.assertFalse(manifest.getEntries().isEmpty());\n\n        ImageArchiveManifestEntry entry = manifest.getEntries().get(0);\n        Assert.assertNotNull(entry);\n        Assert.assertEquals(\"image-id-sha256.json\", entry.getConfig());\n        Assert.assertEquals(\"image-id-sha256\", entry.getId());\n        Assert.assertNotNull(entry.getRepoTags());\n        Assert.assertEquals(Collections.singletonList(\"test/image:latest\"), entry.getRepoTags());\n        Assert.assertNotNull(entry.getLayers());\n        Assert.assertEquals(Collections.singletonList(\"layer-id-sha256/layer.tar\"), entry.getLayers());\n    }\n\n    @Test\n    public void findByRepoTagEmptyManifest() {\n        ImageArchiveManifest empty = new ImageArchiveManifestAdapter(new JsonArray());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(\"anything\", empty));\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(\"anything\", null));\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(null, null));\n    }\n\n    @Test\n    public void findByRepoTagNonEmptyManifest() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(\"anything\", nonEmpty));\n        // Prefix\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(\"test\", nonEmpty));\n        // Prefix\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTag(\"test/image\", nonEmpty));\n    }\n\n    @Test\n    public void findByRepoTagSuccessfully() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n        ImageArchiveManifestEntry found = ImageArchiveUtil.findEntryByRepoTag(\"test/image:latest\", nonEmpty);\n\n        Assert.assertNotNull(found);\n        Assert.assertTrue(found.getRepoTags().contains(\"test/image:latest\"));\n    }\n\n    @Test\n    public void findByRepoTagPatternEmptyManifest() {\n        ImageArchiveManifest empty = new ImageArchiveManifestAdapter(new JsonArray());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\".*\", empty));\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\".*\", null));\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern((String)null, null));\n    }\n\n    @Test(expected = PatternSyntaxException.class)\n    public void findByRepoTagPatternInvalidPattern() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\"*(?\", nonEmpty));\n    }\n\n    @Test\n    public void findByRepoTagPatternNonEmptyManifest() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\"does/not:match\", nonEmpty));\n        // Anchored pattern\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\"^test/image$\", nonEmpty));\n    }\n\n    @Test\n    public void findByRepoTagPatternSuccessfully() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n        Pair<String, ImageArchiveManifestEntry> found;\n\n        // Complete match\n        found = ImageArchiveUtil.findEntryByRepoTagPattern(\"test/image:latest\", nonEmpty);\n        Assert.assertNotNull(found);\n        Assert.assertEquals(\"test/image:latest\", found.getLeft());\n        Assert.assertNotNull(found.getRight());\n        Assert.assertTrue(found.getRight().getRepoTags().contains(\"test/image:latest\"));\n\n        // Unanchored match\n        found = ImageArchiveUtil.findEntryByRepoTagPattern(\"test/image\", nonEmpty);\n        Assert.assertNotNull(found);\n        Assert.assertEquals(\"test/image:latest\", found.getLeft());\n        Assert.assertNotNull(found.getRight());\n        Assert.assertTrue(found.getRight().getRepoTags().contains(\"test/image:latest\"));\n\n        // Initial anchor\n        found = ImageArchiveUtil.findEntryByRepoTagPattern(\"^test/image\", nonEmpty);\n        Assert.assertNotNull(found);\n        Assert.assertEquals(\"test/image:latest\", found.getLeft());\n        Assert.assertNotNull(found.getRight());\n        Assert.assertTrue(found.getRight().getRepoTags().contains(\"test/image:latest\"));\n    }\n\n    @Test\n    public void findEntriesByRepoTagPatternEmptyManifest() {\n        ImageArchiveManifest empty = new ImageArchiveManifestAdapter(new JsonArray());\n        Map<String, ImageArchiveManifestEntry> entries;\n\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern((String)null, null);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\".*\", null);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern((String)null, empty);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\".*\", empty);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n    }\n\n    @Test(expected = PatternSyntaxException.class)\n    public void findEntriesByRepoTagPatternInvalidPattern() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n\n        Assert.assertNull(ImageArchiveUtil.findEntryByRepoTagPattern(\"*(?\", nonEmpty));\n    }\n\n    @Test\n    public void findEntriesByRepoTagPatternNonEmptyManifest() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n        Map<String, ImageArchiveManifestEntry> entries;\n\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\"does/not:match\", nonEmpty);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n\n        // Anchored pattern\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\"^test/image$\", nonEmpty);\n        Assert.assertNotNull(entries);\n        Assert.assertTrue(entries.isEmpty());\n    }\n\n    @Test\n    public void findEntriesByRepoTagPatternSuccessfully() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n        Map<String, ImageArchiveManifestEntry> entries;\n\n        // Complete match\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\"test/image:latest\", nonEmpty);\n        Assert.assertNotNull(entries);\n        Assert.assertNotNull(entries.get(\"test/image:latest\"));\n        Assert.assertTrue(entries.get(\"test/image:latest\").getRepoTags().contains(\"test/image:latest\"));\n\n        // Unanchored match\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\"test/image\", nonEmpty);\n        Assert.assertNotNull(entries);\n        Assert.assertNotNull(entries.get(\"test/image:latest\"));\n        Assert.assertTrue(entries.get(\"test/image:latest\").getRepoTags().contains(\"test/image:latest\"));\n\n        // Initial anchor\n        entries = ImageArchiveUtil.findEntriesByRepoTagPattern(\"^test/image\", nonEmpty);\n        Assert.assertNotNull(entries);\n        Assert.assertNotNull(entries.get(\"test/image:latest\"));\n        Assert.assertTrue(entries.get(\"test/image:latest\").getRepoTags().contains(\"test/image:latest\"));\n    }\n\n    @Test\n    public void mapEntriesByIdSuccessfully() {\n        ImageArchiveManifest nonEmpty = new ImageArchiveManifestAdapter(createBasicManifestJson());\n        Map<String, ImageArchiveManifestEntry> entries = ImageArchiveUtil.mapEntriesById(nonEmpty.getEntries());\n\n        Assert.assertNotNull(entries);\n        Assert.assertEquals(1, entries.size());\n        Assert.assertNotNull(entries.get(\"image-id-sha256\"));\n        Assert.assertTrue(entries.get(\"image-id-sha256\").getRepoTags().contains(\"test/image:latest\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/ImageNameFormatterTest.java",
    "content": "package io.fabric8.maven.docker.util;\n/*\n *\n * Copyright 2016 Roland Huss\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\nimport java.util.Date;\nimport java.util.Properties;\n\nimport mockit.Expectations;\nimport mockit.FullVerifications;\nimport mockit.Injectable;\nimport mockit.Tested;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Test;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.junit.Assert.fail;\n\n/**\n * @author roland\n * @since 07/06/16\n */\n\npublic class ImageNameFormatterTest {\n\n    @Injectable\n    private MavenProject project;\n\n    @Injectable\n    private Date now = new Date();\n\n    @Tested\n    private ImageNameFormatter formatter;\n\n    @Test\n    public void simple() throws Exception {\n        assertThat(formatter.format(\"bla\"),equalTo(\"bla\"));\n    }\n\n    @Test\n    public void invalidFormatChar() throws Exception {\n        try {\n            formatter.format(\"bla %z\");\n            fail();\n        } catch (IllegalArgumentException exp) {\n            System.out.println(exp);\n            assertThat(\"Doesnt match\", exp.getMessage(), containsString(\"%z\"));\n        }\n    }\n\n    @Test\n    public void defaultUserName() throws Exception {\n\n        final String[] data = {\n            \"io.fabric8\", \"fabric8\",\n            \"io.FABRIC8\", \"fabric8\",\n            \"io.fabric8.\", \"fabric8\",\n            \"io.fabric8\", \"fabric8\",\n            \"fabric8....\", \"fabric8\",\n            \"io.fabric8___\", \"fabric8__\"\n        };\n\n        for (int i = 0; i < data.length; i+=2) {\n\n            final int finalI = i;\n            new Expectations() {{\n                project.getProperties(); result = new Properties();\n                project.getGroupId(); result = data[finalI];\n            }};\n\n            String value = formatter.format(\"%g\");\n            assertThat(\"Idx. \" + i / 2,value, equalTo(data[i+1]));\n        }\n    }\n\n    @Test\n    public void artifact() throws Exception {\n        new Expectations() {{\n            project.getArtifactId(); result = \"Docker....Maven.....Plugin\";\n        }};\n\n        assertThat(formatter.format(\"--> %a <--\"),equalTo(\"--> docker.maven.plugin <--\"));\n    }\n\n    @Test\n    public void tagWithProperty() throws Exception {\n        new Expectations() {{\n            Properties props = new Properties();\n            props.put(\"docker.image.tag\",\"1.2.3\");\n            project.getProperties(); result = props;\n\n        }};\n        assertThat(formatter.format(\"%t\"),equalTo(\"1.2.3\"));\n        new FullVerifications() {{ }};\n    }\n\n    @Test\n    public void tag() throws Exception {\n        new Expectations() {{\n            project.getArtifactId(); result = \"docker-maven-plugin\";\n            project.getGroupId(); result = \"io.fabric8\";\n            project.getVersion(); result = \"1.2.3-SNAPSHOT\";\n            project.getProperties(); result = new Properties();\n        }};\n        assertThat(formatter.format(\"%g/%a:%l\"), equalTo(\"fabric8/docker-maven-plugin:latest\"));\n        assertThat(formatter.format(\"%g/%a:%v\"), equalTo(\"fabric8/docker-maven-plugin:1.2.3-SNAPSHOT\"));\n        assertThat(formatter.format(\"%g/%a:%t\").matches(\".*snapshot-[\\\\d-]+$\"), is(true));\n    }\n\n    @Test\n    public void nonSnapshotArtifact() throws Exception {\n        new Expectations() {{\n            project.getArtifactId(); result = \"docker-maven-plugin\";\n            project.getGroupId(); result = \"io.fabric8\";\n            project.getVersion(); result = \"1.2.3\";\n            project.getProperties(); result = new Properties();\n        }};\n\n        assertThat(formatter.format(\"%g/%a:%l\"), equalTo(\"fabric8/docker-maven-plugin:1.2.3\"));\n        assertThat(formatter.format(\"%g/%a:%v\"), equalTo(\"fabric8/docker-maven-plugin:1.2.3\"));\n        assertThat(formatter.format(\"%g/%a:%t\"), equalTo(\"fabric8/docker-maven-plugin:1.2.3\"));\n    }\n\n    @Test\n    public void groupIdWithProperty() throws Exception {\n        new Expectations() {{\n            Properties props = new Properties();\n            props.put(\"docker.image.user\",\"this.it..is\");\n            project.getProperties(); result = props;\n\n        }};\n\n        assertThat(formatter.format(\"%g/name\"),equalTo(\"this.it..is/name\"));\n\n        new FullVerifications() {{ }};\n    }\n\n    private final class GroupIdExpectations extends Expectations {\n        GroupIdExpectations(String groupId) {\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/ImageNameTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\npublic class ImageNameTest {\n\n    @Test\n    public void simple() {\n\n        Object[] data = {\n                \"jolokia/jolokia_demo\",\n                r().repository(\"jolokia/jolokia_demo\")\n                   .fullName(\"jolokia/jolokia_demo\").fullNameWithTag(\"jolokia/jolokia_demo:latest\").simpleName(\"jolokia_demo\").tag(\"latest\"),\n\n                \"jolokia/jolokia_demo:0.9.6\",\n                r().repository(\"jolokia/jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"jolokia/jolokia_demo\").fullNameWithTag(\"jolokia/jolokia_demo:0.9.6\").simpleName(\"jolokia_demo\"),\n\n                \"test.org/jolokia/jolokia_demo:0.9.6\",\n                r().registry(\"test.org\").repository(\"jolokia/jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"test.org/jolokia/jolokia_demo\").fullNameWithTag(\"test.org/jolokia/jolokia_demo:0.9.6\").simpleName(\"jolokia_demo\"),\n\n                \"test.org/jolokia/jolokia_demo\",\n                r().registry(\"test.org\").repository(\"jolokia/jolokia_demo\")\n                        .fullName(\"test.org/jolokia/jolokia_demo\").fullNameWithTag(\"test.org/jolokia/jolokia_demo:latest\").simpleName(\"jolokia_demo\").tag(\"latest\"),\n\n                \"test.org:8000/jolokia/jolokia_demo:8.0\",\n                r().registry(\"test.org:8000\").repository(\"jolokia/jolokia_demo\").tag(\"8.0\")\n                        .fullName(\"test.org:8000/jolokia/jolokia_demo\").fullNameWithTag(\"test.org:8000/jolokia/jolokia_demo:8.0\").simpleName(\"jolokia_demo\"),\n\n                \"jolokia_demo\",\n                r().repository(\"jolokia_demo\")\n                        .fullName(\"jolokia_demo\").fullNameWithTag(\"jolokia_demo:latest\").simpleName(\"jolokia_demo\").tag(\"latest\"),\n\n                \"jolokia_demo:0.9.6\",\n                r().repository(\"jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"jolokia_demo\").fullNameWithTag(\"jolokia_demo:0.9.6\").simpleName(\"jolokia_demo\"),\n\n                \"consol/tomcat-8.0:8.0.9\",\n                r().repository(\"consol/tomcat-8.0\").tag(\"8.0.9\")\n                        .fullName(\"consol/tomcat-8.0\").fullNameWithTag(\"consol/tomcat-8.0:8.0.9\").simpleName(\"tomcat-8.0\"),\n\n                \"test.org/user/subproject/image:latest\",\n                r().registry(\"test.org\").repository(\"user/subproject/image\").tag(\"latest\")\n                        .fullName(\"test.org/user/subproject/image\").fullNameWithTag(\"test.org/user/subproject/image:latest\").simpleName(\"subproject/image\")\n\n        };\n\n        verifyData(data);\n    }\n\n    @Test\n    public void testMultipleSubComponents() {\n        Object[] data = {\n                \"org/jolokia/jolokia_demo\",\n                r().repository(\"org/jolokia/jolokia_demo\")\n                        .fullName(\"org/jolokia/jolokia_demo\").fullNameWithTag(\"org/jolokia/jolokia_demo:latest\").simpleName(\"jolokia/jolokia_demo\").tag(\"latest\"),\n\n                \"org/jolokia/jolokia_demo:0.9.6\",\n                r().repository(\"org/jolokia/jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"org/jolokia/jolokia_demo\").fullNameWithTag(\"org/jolokia/jolokia_demo:0.9.6\").simpleName(\"jolokia/jolokia_demo\"),\n\n                \"repo.example.com/org/jolokia/jolokia_demo:0.9.6\",\n                r().registry(\"repo.example.com\").repository(\"org/jolokia/jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"repo.example.com/org/jolokia/jolokia_demo\").fullNameWithTag(\"repo.example.com/org/jolokia/jolokia_demo:0.9.6\").simpleName(\"jolokia/jolokia_demo\"),\n\n                \"repo.example.com/org/jolokia/jolokia_demo\",\n                r().registry(\"repo.example.com\").repository(\"org/jolokia/jolokia_demo\")\n                        .fullName(\"repo.example.com/org/jolokia/jolokia_demo\").fullNameWithTag(\"repo.example.com/org/jolokia/jolokia_demo:latest\").simpleName(\"jolokia/jolokia_demo\").tag(\"latest\"),\n\n                \"repo.example.com:8000/org/jolokia/jolokia_demo:8.0\",\n                r().registry(\"repo.example.com:8000\").repository(\"org/jolokia/jolokia_demo\").tag(\"8.0\")\n                        .fullName(\"repo.example.com:8000/org/jolokia/jolokia_demo\").fullNameWithTag(\"repo.example.com:8000/org/jolokia/jolokia_demo:8.0\").simpleName(\"jolokia/jolokia_demo\"),\n                \"org/jolokia_demo\",\n                r().repository(\"org/jolokia_demo\")\n                        .fullName(\"org/jolokia_demo\").fullNameWithTag(\"org/jolokia_demo:latest\").simpleName(\"jolokia_demo\").tag(\"latest\"),\n\n                \"org/jolokia_demo:0.9.6\",\n                r().repository(\"org/jolokia_demo\").tag(\"0.9.6\")\n                        .fullName(\"org/jolokia_demo\").fullNameWithTag(\"org/jolokia_demo:0.9.6\").simpleName(\"jolokia_demo\"),\n        };\n\n        verifyData(data);\n    }\n\n    private void verifyData(Object[] data) {\n        for (int i = 0; i < data.length; i += 2) {\n            ImageName name = new ImageName((String) data[i]);\n            Res res = (Res) data[i + 1];\n            assertEquals(\"Registry \" + i,res.registry,name.getRegistry());\n            assertEquals(\"Repository \" + i,res.repository,name.getRepository());\n            assertEquals(\"Tag \" + i,res.tag,name.getTag());\n            assertEquals(\"RepoWithRegistry \" + i,res.fullName, name.getNameWithoutTag(null));\n            assertEquals(\"FullName \" + i,res.fullNameWithTag, name.getFullName(null));\n            assertEquals(\"Simple Name \" + i,res.simpleName, name.getSimpleName());\n        }\n    }\n\n    @Test\n    public void testRegistryNaming() throws Exception {\n        assertEquals(\"docker.jolokia.org/jolokia/jolokia_demo:0.18\",\n                     new ImageName(\"jolokia/jolokia_demo:0.18\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"docker.jolokia.org/jolokia/jolokia_demo:latest\",\n                     new ImageName(\"jolokia/jolokia_demo\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"jolokia/jolokia_demo:latest\",\n                     new ImageName(\"jolokia/jolokia_demo\").getFullName(null));\n        assertEquals(\"docker.jolokia.org/jolokia/jolokia_demo:latest\",\n                     new ImageName(\"docker.jolokia.org/jolokia/jolokia_demo\").getFullName(\"another.registry.org\"));\n        assertEquals(\"docker.jolokia.org/jolokia/jolokia_demo:latest\",\n                     new ImageName(\"docker.jolokia.org/jolokia/jolokia_demo\").getFullName(null));\n    }\n\n    @Test\n    public void testRegistryNamingExtended() throws Exception {\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:0.18\",\n                new ImageName(\"org/jolokia/jolokia_demo:0.18\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:latest\",\n                new ImageName(\"org/jolokia/jolokia_demo\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"org/jolokia/jolokia_demo:latest\",\n                new ImageName(\"org/jolokia/jolokia_demo\").getFullName(null));\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:latest\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo\").getFullName(\"another.registry.org\"));\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:latest\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo\").getFullName(null));\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getFullName(null));\n        assertEquals(\"docker.jolokia.org\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getRegistry());\n        assertEquals(\"docker.jolokia.org\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo:alpine\").getRegistry());\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:alpine\",\n                new ImageName(\"org/jolokia/jolokia_demo:alpine\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"docker.jolokia.org\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo:alpine\").getRegistry());\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:1.2.3.4-alpine\",\n                new ImageName(\"org/jolokia/jolokia_demo:1.2.3.4-alpine\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"docker.jolokia.org\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo:alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getRegistry());\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\",\n                new ImageName(\"org/jolokia/jolokia_demo:alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getFullName(\"docker.jolokia.org\"));\n        assertEquals(\"docker.jolokia.org\",\n                new ImageName(\"docker.jolokia.org/org/jolokia/jolokia_demo:1.2.3.4-alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getRegistry());\n        assertEquals(\"docker.jolokia.org/org/jolokia/jolokia_demo:1.2.3.4-alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\",\n                new ImageName(\"org/jolokia/jolokia_demo:1.2.3.4-alpine@sha256:2781907cc3ae9bb732076f14392128d4b84ff3ebb66379d268e563b10fbfb9da\").getFullName(\"docker.jolokia.org\"));\n    }\n\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testIllegalFormat() throws Exception {\n        new ImageName(\"\");\n    }\n\n    @Test\n    public void namesUsedByDockerTests() {\n        StringBuffer longTag = new StringBuffer();\n        for (int i = 0; i < 130; i++) {\n            longTag.append(\"a\");\n        }\n        String[] illegal = {\n            \"fo$z$\", \"Foo@3cc\", \"Foo$3\", \"Foo*3\", \"Fo^3\", \"Foo!3\", \"F)xcz(\", \"fo%asd\", \"FOO/bar\",\n            \"repo:fo$z$\", \"repo:Foo@3cc\", \"repo:Foo$3\", \"repo:Foo*3\", \"repo:Fo^3\", \"repo:Foo!3\",\n            \"repo:%goodbye\", \"repo:#hashtagit\", \"repo:F)xcz(\", \"repo:-foo\", \"repo:..\",\"repo:\" + longTag.toString(),\n            \"-busybox:test\", \"-test/busybox:test\", \"-index:5000/busybox:test\"\n\n        };\n\n        for (String i : illegal) {\n            try {\n                new ImageName(i);\n                fail(String.format(\"Name '%s' should fail\",i));\n            } catch (IllegalArgumentException exp) { /* expected */};\n        }\n\n        String[] legal = {\n            \"fooo/bar\", \"fooaa/test\", \"foooo:t\", \"HOSTNAME.DOMAIN.COM:443/foo/bar\"\n        };\n\n        for (String l : legal) {\n            new ImageName(l);\n        }\n    }\n\n    @Test\n    public void testGetNameWithOptionalRepository() {\n        // Given\n        ImageName imageName = new ImageName(\"sample/test-project:0.0.1\");\n\n        // When\n        String imageWithOptionalRepo = imageName.getNameWithOptionalRepository(\"quay.io/test-user\");\n        String imageWithNullOptionalRepo = imageName.getNameWithOptionalRepository(null);\n\n        // Then\n        assertEquals(\"quay.io/test-user/test-project:0.0.1\", imageWithOptionalRepo);\n        assertEquals(\"sample/test-project:0.0.1\", imageWithNullOptionalRepo);\n    }\n\n    // =======================================================================================\n    private static Res r() {\n        return new Res();\n    }\n\n    private static class Res {\n        private String registry,repository,tag, fullName, fullNameWithTag, simpleName;\n        boolean hasRegistry = false;\n\n        Res registry(String registry) {\n            this.registry = registry;\n            this.hasRegistry = registry != null;\n            return this;\n        }\n\n        Res repository(String repository) {\n            this.repository = repository;\n            return this;\n        }\n\n        Res tag(String tag) {\n            this.tag = tag;\n            return this;\n        }\n\n        Res fullName(String fullName) {\n            this.fullName = fullName;\n            return this;\n        }\n\n        Res fullNameWithTag(String fullNameWithTag) {\n            this.fullNameWithTag = fullNameWithTag;\n            return this;\n        }\n\n        Res simpleName(String simpleName) {\n            this.simpleName = simpleName;\n            return this;\n        }\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/JibServiceUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport com.google.cloud.tools.jib.api.AbsoluteUnixPath;\nimport com.google.cloud.tools.jib.api.ImageFormat;\nimport com.google.cloud.tools.jib.api.JibContainerBuilder;\nimport com.google.cloud.tools.jib.api.LayerConfiguration;\nimport com.google.cloud.tools.jib.api.Port;\nimport io.fabric8.maven.docker.config.Arguments;\nimport io.fabric8.maven.docker.config.AssemblyConfiguration;\nimport io.fabric8.maven.docker.config.BuildImageConfiguration;\nimport io.fabric8.maven.docker.config.ImageConfiguration;\nimport mockit.Mocked;\nimport mockit.Verifications;\nimport org.apache.maven.plugins.assembly.model.Assembly;\nimport org.apache.maven.plugins.assembly.model.FileItem;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static io.fabric8.maven.docker.util.JibServiceUtil.BUSYBOX;\nimport static io.fabric8.maven.docker.util.JibServiceUtil.containerFromImageConfiguration;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\npublic class JibServiceUtilTest {\n    @Test\n    public void testGetBaseImageWithNullBuildConfig() {\n        assertEquals(BUSYBOX, JibServiceUtil.getBaseImage(new ImageConfiguration.Builder().build()));\n    }\n\n    @Test\n    public void testGetBaseImageWithNotNullBuildConfig() {\n        assertEquals(\"quay.io/jkubeio/jkube-test-image:0.0.1\", JibServiceUtil.getBaseImage(new ImageConfiguration.Builder()\n                .buildConfig(new BuildImageConfiguration.Builder()\n                        .from(\"quay.io/jkubeio/jkube-test-image:0.0.1\")\n                        .build())\n                .build()));\n    }\n\n    @Test\n    public void testContainerFromImageConfiguration(@Mocked JibContainerBuilder containerBuilder) throws Exception {\n        // Given\n        ImageConfiguration imageConfiguration = getSampleImageConfiguration();\n        // When\n        JibContainerBuilder jibContainerBuilder = containerFromImageConfiguration(imageConfiguration, null);\n        // Then\n        // @formatter:off\n        new Verifications() {{\n            jibContainerBuilder.addLabel(\"foo\", \"bar\");\n            times = 1;\n            jibContainerBuilder.setEntrypoint(Arrays.asList(\"java\", \"-jar\", \"foo.jar\"));\n            times = 1;\n            jibContainerBuilder.setExposedPorts(new HashSet<>(Collections.singletonList(Port.tcp(8080))));\n            times = 1;\n            jibContainerBuilder.setUser(\"root\");\n            times = 1;\n            jibContainerBuilder.setWorkingDirectory(AbsoluteUnixPath.get(\"/home/foo\"));\n            times = 1;\n            jibContainerBuilder.setVolumes(new HashSet<>(Collections.singletonList(AbsoluteUnixPath.get(\"/mnt/volume1\"))));\n            times = 1;\n            jibContainerBuilder.setFormat(ImageFormat.OCI);\n            times = 1;\n        }};\n        // @formatter:on\n    }\n\n    @Test\n    public void testCopyToContainer(@Mocked JibContainerBuilder containerBuilder) throws IOException {\n        // Given\n        File temporaryDirectory = Files.createTempDirectory(\"jib-test\").toFile();\n        File temporaryFile = new File(temporaryDirectory, \"foo.txt\");\n        boolean wasNewFileCreated = temporaryFile.createNewFile();\n\n        // When\n        JibServiceUtil.copyToContainer(containerBuilder, temporaryDirectory, \"/tmp\", Collections.emptyMap());\n\n        // Then\n        assertTrue(wasNewFileCreated);\n        new Verifications() {{\n            LayerConfiguration layerConfiguration;\n            containerBuilder.addLayer(layerConfiguration = withCapture());\n\n            assertNotNull(layerConfiguration);\n            assertEquals(1, layerConfiguration.getLayerEntries().size());\n            assertEquals(temporaryFile.toPath(), layerConfiguration.getLayerEntries().get(0).getSourceFile());\n            assertEquals(AbsoluteUnixPath.get(temporaryFile.getAbsolutePath().substring(4)), layerConfiguration.getLayerEntries().get(0).getExtractionPath());\n        }};\n    }\n\n    @Test\n    public void testAppendOriginalImageNameTagIfApplicable() {\n        // Given\n        List<String> imageTagList = Arrays.asList(\"0.0.1\", \"0.0.1-SNAPSHOT\");\n\n        // When\n        Set<String> result = JibServiceUtil.getAllImageTags(imageTagList, \"test-project\");\n\n        // Then\n        assertNotNull(result);\n        assertEquals(3, result.size());\n        assertArrayEquals(new String[]{\"0.0.1-SNAPSHOT\", \"0.0.1\", \"latest\"}, result.toArray());\n    }\n\n    @Test\n    public void testGetFullImageNameWithDefaultTag() {\n        assertEquals(\"test/test-project:latest\", JibServiceUtil.getFullImageName(getSampleImageConfiguration(), null));\n    }\n\n    @Test\n    public void testGetFullImageNameWithProvidedTag() {\n        assertEquals(\"test/test-project:0.0.1\", JibServiceUtil.getFullImageName(getSampleImageConfiguration(), \"0.0.1\"));\n    }\n\n    private ImageConfiguration getSampleImageConfiguration() {\n        Assembly assembly = new Assembly();\n        FileItem fileItem = new FileItem();\n        fileItem.setSource(\"${project.basedir}/foo\");\n        fileItem.setOutputDirectory(\"/deployments\");\n        assembly.addFile(fileItem);\n\n        BuildImageConfiguration bc = new BuildImageConfiguration.Builder()\n                .from(\"quay.io/test/testimage:testtag\")\n                .assembly(new AssemblyConfiguration.Builder()\n                        .assemblyDef(assembly)\n                        .build())\n                .entryPoint(new Arguments.Builder().withParam(\"java\")\n                        .withParam(\"-jar\")\n                        .withParam(\"foo.jar\")\n                        .build())\n                .labels(Collections.singletonMap(\"foo\", \"bar\"))\n                .user(\"root\")\n                .workdir(\"/home/foo\")\n                .ports(Collections.singletonList(\"8080\"))\n                .volumes(Collections.singletonList(\"/mnt/volume1\"))\n                .build();\n\n        return new ImageConfiguration.Builder()\n                .name(\"test/test-project\")\n                .buildConfig(bc)\n                .build();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/NamePatternUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class NamePatternUtilTest {\n    @Test\n    public void convertNonPatternRepoTagPatterns() {\n        Assert.assertEquals(\"^$\", NamePatternUtil.convertNamePattern(\"\"));\n        Assert.assertEquals(\"^a$\", NamePatternUtil.convertNamePattern(\"a\"));\n        Assert.assertEquals(\"^hello$\", NamePatternUtil.convertNamePattern(\"hello\"));\n        Assert.assertEquals(\"^hello/world$\", NamePatternUtil.convertNamePattern(\"hello/world\"));\n        Assert.assertEquals(\"^hello/world:latest$\", NamePatternUtil.convertNamePattern(\"hello/world:latest\"));\n        Assert.assertEquals(\"^\\\\Qregistry.com\\\\E/hello/world:latest$\", NamePatternUtil.convertNamePattern(\"registry.com/hello/world:latest\"));\n        Assert.assertEquals(\"^\\\\Qregistry.com\\\\E:8080/hello/world:latest$\", NamePatternUtil.convertNamePattern(\"registry.com:8080/hello/world:latest\"));\n\n        Assert.assertEquals(\"^hello/world:\\\\Q1.0-SNAPSHOT\\\\E$\", NamePatternUtil.convertNamePattern(\"hello/world:1.0-SNAPSHOT\"));\n        Assert.assertEquals(\"^\\\\Qh\\\\E\\\\\\\\E\\\\Qllo\\\\E/\\\\Qw\\\\Qrld\\\\E:\\\\Q1.0-SNAPSHOT\\\\E$\", NamePatternUtil.convertNamePattern(\"h\\\\Ello/w\\\\Qrld:1.0-SNAPSHOT\"));\n        Assert.assertEquals(\"^\\\\Qhello! [World] \\\\E:\\\\Q not really a tag, right\\\\E$\", NamePatternUtil.convertNamePattern(\"hello! [World] : not really a tag, right\"));\n    }\n\n    @Test\n    public void convertPatternRepoTagPatterns() {\n        Assert.assertEquals(\"^[^/:]$\", NamePatternUtil.convertNamePattern(\"?\"));\n        Assert.assertEquals(\"^[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"??\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"hello??\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]\\\\Qare you there\\\\E$\", NamePatternUtil.convertNamePattern(\"hello??are you there\"));\n        Assert.assertEquals(\"^[^/:][^/:]whaaat$\", NamePatternUtil.convertNamePattern(\"??whaaat\"));\n\n        Assert.assertEquals(\"^([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"*\"));\n        Assert.assertEquals(\"^my-company/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"my-company/*\"));\n        Assert.assertEquals(\"^my-co([^/:]|:(?=.*:))*/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"my-co*/*\"));\n\n        Assert.assertEquals(\"^([^:]|:(?=.*:))*(?<![^/])my-image:([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"**/my-image:*\"));\n        Assert.assertEquals(\"^([^:]|:(?=.*:))*my-image:([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"**my-image:*\"));\n        Assert.assertEquals(\"^([^:]|:(?=.*:))*my-image:([^:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"**my-image:**\"));\n    }\n\n    @Test\n    public void convertPrefixedPatternRepoTagPatterns() {\n        Assert.assertEquals(\"^[^/:]$\", NamePatternUtil.convertNamePattern(\"%ant[?]\"));\n        Assert.assertEquals(\"^[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"%ant[??]\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"%ant[hello??]\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]\\\\Qare you there\\\\E$\", NamePatternUtil.convertNamePattern(\"%ant[hello??are you there]\"));\n        Assert.assertEquals(\"^[^/:][^/:]whaaat$\", NamePatternUtil.convertNamePattern(\"%ant[??whaaat]\"));\n\n        Assert.assertEquals(\"^([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"%ant[*]\"));\n        Assert.assertEquals(\"^my-company/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"%ant[my-company/*]\"));\n        Assert.assertEquals(\"^my-co([^/:]|:(?=.*:))*/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"%ant[my-co*/*]\"));\n\n        Assert.assertEquals(\"^([^:]|:(?=.*:))*(?<![^/])my-image:([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePattern(\"%ant[**/my-image:*]\"));\n\n        // Broken prefixes are ignored\n        Assert.assertEquals(\"^\\\\Q%ant[\\\\E[^/:]$\", NamePatternUtil.convertNamePattern(\"%ant[?\"));\n    }\n\n    @Test\n    public void convertRegexRepoTagPatterns() {\n        Assert.assertEquals(\"^[^/:]$\", NamePatternUtil.convertNamePattern(\"%regex[^[^/:]$]\"));\n        Assert.assertEquals(\"^[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"%regex[^[^/:][^/:]$]\"));\n        Assert.assertEquals(\"^\\\\Qhello\\\\E[^/:][^/:]$\", NamePatternUtil.convertNamePattern(\"%regex[^\\\\Qhello\\\\E[^/:][^/:]$]\"));\n        Assert.assertEquals(\"^\\\\Qhello\\\\E[^/:][^/:]\\\\Qare you there\\\\E$\", NamePatternUtil.convertNamePattern(\"%regex[^\\\\Qhello\\\\E[^/:][^/:]\\\\Qare you there\\\\E$]\"));\n\n        Assert.assertEquals(\"easy literals\", NamePatternUtil.convertNamePattern(\"%regex[easy literals]\"));\n        Assert.assertEquals(\"no .* anchors\", NamePatternUtil.convertNamePattern(\"%regex[no .* anchors]\"));\n        Assert.assertEquals(\"less \\\\? fun for v1\\\\.0\", NamePatternUtil.convertNamePattern(\"%regex[less \\\\? fun for v1\\\\.0]\"));\n\n        // Broken prefixes don't cause failures\n        Assert.assertEquals(\"^\\\\Q%regex[^[^\\\\E/:\\\\Q]$\\\\E$\", NamePatternUtil.convertNamePattern(\"%regex[^[^/:]$\"));\n    }\n    \n    @Test\n    public void convertNamePatternListWithOnePattern() {\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"\"));\n        Assert.assertEquals(\"^a$\", NamePatternUtil.convertNamePatternList(\"a\"));\n        Assert.assertEquals(\"^hello$\", NamePatternUtil.convertNamePatternList(\"hello\"));\n        Assert.assertEquals(\"^hello/world$\", NamePatternUtil.convertNamePatternList(\"hello/world\"));\n        Assert.assertEquals(\"^hello/world:latest$\", NamePatternUtil.convertNamePatternList(\"hello/world:latest\"));\n        Assert.assertEquals(\"^\\\\Qregistry.com\\\\E/hello/world:latest$\", NamePatternUtil.convertNamePatternList(\"registry.com/hello/world:latest\"));\n        Assert.assertEquals(\"^\\\\Qregistry.com\\\\E:8080/hello/world:latest$\", NamePatternUtil.convertNamePatternList(\"registry.com:8080/hello/world:latest\"));\n\n        Assert.assertEquals(\"^hello/world:\\\\Q1.0-SNAPSHOT\\\\E$\", NamePatternUtil.convertNamePatternList(\"hello/world:1.0-SNAPSHOT\"));\n        Assert.assertEquals(\"^\\\\Qh\\\\E\\\\\\\\E\\\\Qllo\\\\E/\\\\Qw\\\\Qrld\\\\E:\\\\Q1.0-SNAPSHOT\\\\E$\", NamePatternUtil.convertNamePatternList(\"h\\\\Ello/w\\\\Qrld:1.0-SNAPSHOT\"));\n\n        Assert.assertEquals(\"^[^/:]$\", NamePatternUtil.convertNamePatternList(\"?\"));\n        Assert.assertEquals(\"^[^/:][^/:]$\", NamePatternUtil.convertNamePatternList(\"??\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]$\", NamePatternUtil.convertNamePatternList(\"hello??\"));\n        Assert.assertEquals(\"^hello[^/:][^/:]\\\\Qare you there\\\\E$\", NamePatternUtil.convertNamePatternList(\"hello??are you there\"));\n        Assert.assertEquals(\"^[^/:][^/:]whaaat$\", NamePatternUtil.convertNamePatternList(\"??whaaat\"));\n\n        Assert.assertEquals(\"^([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePatternList(\"*\"));\n        Assert.assertEquals(\"^my-company/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePatternList(\"my-company/*\"));\n        Assert.assertEquals(\"^my-co([^/:]|:(?=.*:))*/([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePatternList(\"my-co*/*\"));\n\n        Assert.assertEquals(\"^([^:]|:(?=.*:))*(?<![^/])my-image:([^/:]|:(?=.*:))*$\", NamePatternUtil.convertNamePatternList(\"**/my-image:*\"));\n        Assert.assertEquals(\"^[^/:]$\", NamePatternUtil.convertNamePatternList(\"%regex[^[^/:]$]\"));\n        Assert.assertEquals(\"^[^/:][^/:]$\", NamePatternUtil.convertNamePatternList(\"%regex[^[^/:][^/:]$]\"));\n        Assert.assertEquals(\"^\\\\Qhello\\\\E[^/:][^/:]$\", NamePatternUtil.convertNamePatternList(\"%regex[^\\\\Qhello\\\\E[^/:][^/:]$]\"));\n        Assert.assertEquals(\"^\\\\Qhello\\\\E[^/:][^/:]\\\\Qare you there\\\\E$\", NamePatternUtil.convertNamePatternList(\"%regex[^\\\\Qhello\\\\E[^/:][^/:]\\\\Qare you there\\\\E$]\"));\n\n        Assert.assertEquals(\"easy literals\", NamePatternUtil.convertNamePatternList(\"%regex[easy literals]\"));\n        Assert.assertEquals(\"no .* anchors\", NamePatternUtil.convertNamePatternList(\"%regex[no .* anchors]\"));\n        Assert.assertEquals(\"less \\\\? fun for v1\\\\.0\", NamePatternUtil.convertNamePatternList(\"%regex[less \\\\? fun for v1\\\\.0]\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void convertNamePatternListWithOneInvalidPattern() {\n        NamePatternUtil.convertNamePatternList(\"%regex[\\\\E bogus \\\\Q]\");\n    }\n\n    @Test\n    public void convertNamePatternListWithMultiplePatterns() {\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\",\"));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\",,\"));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\" , , ,, \"));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\" , ,%regex[], \"));\n        Assert.assertEquals(\"^$\", NamePatternUtil.convertNamePatternList(\" , ,%ant[], \"));\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a,b\"));\n        Assert.assertEquals(\"(^hello$|^\\\\Qworld!\\\\E$)\", NamePatternUtil.convertNamePatternList(\"hello, world!\"));\n        Assert.assertEquals(\"(^hello/world$|^foo/bar$|^baz/quux$)\", NamePatternUtil.convertNamePatternList(\"hello/world,foo/bar , baz/quux \"));\n\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"%ant[a],b\"));\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"%ant[a],%ant[b]\"));\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a,%ant[b]\"));\n\n        Assert.assertEquals(\"(a|^b$)\", NamePatternUtil.convertNamePatternList(\"%regex[a],b\"));\n        Assert.assertEquals(\"(a|b)\", NamePatternUtil.convertNamePatternList(\"%regex[a],%regex[b]\"));\n        Assert.assertEquals(\"(^a$|b)\", NamePatternUtil.convertNamePatternList(\"a,%regex[b]\"));\n\n        Assert.assertEquals(\"(a|^b$)\", NamePatternUtil.convertNamePatternList(\"%regex[a], %ant[b]\"));\n        Assert.assertEquals(\"(^a$|b)\", NamePatternUtil.convertNamePatternList(\"%ant[a] , %regex[b]\"));\n        Assert.assertEquals(\"(^a$|b)\", NamePatternUtil.convertNamePatternList(\" a , %regex[b]\"));\n\n        Assert.assertEquals(\"(^hello$|^\\\\Qworld!\\\\E$)\", NamePatternUtil.convertNamePatternList(\"hello, world!\"));\n        Assert.assertEquals(\"(^hello/world$|foo/bar|^baz/quux$)\", NamePatternUtil.convertNamePatternList(\"hello/world, %regex[foo/bar] , baz/quux \"));\n        Assert.assertEquals(\"(^hello$|^\\\\Qworld!\\\\E$)\", NamePatternUtil.convertNamePatternList(\",hello, world!,\"));\n        Assert.assertEquals(\"(^hello/world$|foo/bar|^baz/quux$)\", NamePatternUtil.convertNamePatternList(\", , , hello/world , ,,,, %regex[foo/bar] , baz/quux ,\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void convertNamePatternListWithMultiplePatternsOneInvalid() {\n        NamePatternUtil.convertNamePatternList(\"hello, %regex[\\\\Eworld!\\\\Q]\");\n    }\n\n    @Test\n    public void convertNamePatternListWithSpecificField() {\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"\", \"name\", true));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"\", \"name\", false));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"\", null, true));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"\", null, false));\n\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a,b\", \"name\", true));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"a,b\", \"name\", false));\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a,b\", null, true));\n\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a, name= b\", \"name\", true));\n        Assert.assertEquals(\"^b$\", NamePatternUtil.convertNamePatternList(\"a, name=b\", \"name\", false));\n        Assert.assertEquals(\"^a$\", NamePatternUtil.convertNamePatternList(\"a , name = b \", null, true));\n\n        Assert.assertEquals(\"^b$\", NamePatternUtil.convertNamePatternList(\"image = a, name= b\", \"name\", true));\n        Assert.assertEquals(\"(^b$|c)\", NamePatternUtil.convertNamePatternList(\"image = a, name= b, %regex[c]\", \"name\", true));\n        Assert.assertEquals(\"^b$\", NamePatternUtil.convertNamePatternList(\"image = a, name=b\", \"name\", false));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"image = a , name = b \", null, true));\n\n        Assert.assertEquals(\"(^a$|^b$)\", NamePatternUtil.convertNamePatternList(\"a, name= %ant[b]\", \"name\", true));\n        Assert.assertEquals(\"^b$\", NamePatternUtil.convertNamePatternList(\"%regex[a], name=b\", \"name\", false));\n        Assert.assertEquals(\"^a$\", NamePatternUtil.convertNamePatternList(\"%ant[a] , name = b \", null, true));\n\n        Assert.assertEquals(\"^b$\", NamePatternUtil.convertNamePatternList(\"image = %regex[a], name= %ant[b]\", \"name\", true));\n        Assert.assertEquals(\"(b|c)\", NamePatternUtil.convertNamePatternList(\"image = %regex[a], name= %regex[b], %regex[c]\", \"name\", true));\n        Assert.assertEquals(\" b \", NamePatternUtil.convertNamePatternList(\"image = a, name=%regex[ b ]\", \"name\", false));\n        Assert.assertNull(NamePatternUtil.convertNamePatternList(\"image = %ant[a] , name = %regex[ b ]\", null, true));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/PathTestUtil.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Utility methods and constants for path-related tests\n */\npublic class PathTestUtil {\n\n    /**\n     * A dot representing the current working directory\n     */\n    public static final String DOT = \".\";\n\n    /**\n     * A tilde representing the current user's home directory\n     */\n    public static final String TILDE = \"~\";\n\n    /**\n     * The current runtime platform file separator\n     */\n    public static final String SEP = System.getProperty(\"file.separator\");\n\n    /**\n     * Joins the supplied strings.\n     *\n     * @param joinWith the string used to join the strings\n     * @param objects the strings to be joined\n     * @return the joined strings\n     */\n    public static String join(String joinWith, String... objects) {\n        return join(joinWith, false, false, objects);\n    }\n\n    /**\n     * Joins the supplied strings, optionally prefixing and postfixing the returned string.\n     *\n     * @param joinWith the string used to join the strings\n     * @param prefix prefix the returned string with {@code joinWith}\n     * @param postfix postfix the returned string with {@code joinWith}\n     * @param objects the strings to be joined\n     * @return the joined strings\n     */\n    public static String join(String joinWith, boolean prefix, boolean postfix, String... objects) {\n        StringBuilder sb = null;\n        if (prefix) {\n            sb = new StringBuilder(joinWith);\n        } else {\n            sb = new StringBuilder();\n        }\n\n        for (int i = 0; i < objects.length; ) {\n            sb.append(objects[i]);\n            if (i++ < objects.length) {\n                sb.append(joinWith);\n            }\n        }\n\n        if (postfix) {\n            sb.append(joinWith);\n        }\n\n        return sb.toString();\n    }\n\n    /**\n     * Strips \".\" off of the {@code path}, if present.\n     *\n     * @param path the path which may begin with a \".\"\n     * @return the path stripped of a \".\"\n     */\n    public static String stripLeadingPeriod(String path) {\n        if (path.startsWith(DOT)) {\n            return path.substring(1);\n        }\n\n        return path;\n    }\n\n    /**\n     * Strips \"~\" off of the {@code path}, if present.\n     *\n     * @param path the path which may begin with a \"~\"\n     * @return the path stripped of a \"~\"\n     */\n    public static String stripLeadingTilde(String path) {\n        if (path.startsWith(TILDE)) {\n            return path.substring(1);\n        }\n\n        return path;\n    }\n\n    /**\n     * Creates a unique file under {@code java.io.tmpdir} and returns the {@link File#getCanonicalFile() canonical}\n     * {@code File}.  The file is deleted on exit.  This methodology\n     * <ol>\n     *   <li>guarantees a unique file name,</li>\n     *   <li>doesn't clutter the filesystem with test-related directories or files,</li>\n     *   <li>returns an absolute path (important for relative volume binding strings),\n     *   <li>and returns a canonical file name.</li>\n     * </ol>\n     *\n     * @param nameHint a string used to help create the temporary file name, may be {@code null}\n     * @return the temporary file\n     */\n    public static File createTmpFile(String nameHint) {\n        return createTmpFile(nameHint, TMP_FILE_PRESERVE_MODE.DELETE_ON_EXIT);\n    }\n\n    /**\n     * Creates a unique file under {@code java.io.tmpdir} and returns the {@link File#getCanonicalFile() canonical}\n     * {@code File}.  The optional {@code preserveMode} parameter dictates who is responsible for deleting the created\n     * file, and when.  This methodology\n     * <ol>\n     *   <li>guarantees a unique file name,</li>\n     *   <li>doesn't clutter the filesystem with test-related directories or files,</li>\n     *   <li>returns an absolute path (important for relative volume binding strings),\n     *   <li>and returns a canonical file name.</li>\n     * </ol>\n     *\n     * @param nameHint a string used to help create the temporary file name, may be {@code null}\n     * @param preserveMode mechanism for handling the clean up of files created by this method, may be {@code null}\n     *                     which is equivalent to {@link TMP_FILE_PRESERVE_MODE#DELETE_ON_EXIT}\n     * @return the absolute temporary file, which may not exist depending on the {@code preserveMode}\n     */\n    public static File createTmpFile(String nameHint, TMP_FILE_PRESERVE_MODE preserveMode) {\n        try {\n            File tmpFile = File.createTempFile(nameHint, \".tmp\");\n            assertTrue(\"The created temporary file \" + tmpFile + \" is not absolute!\", tmpFile.isAbsolute());\n            if (preserveMode != null) {\n                switch (preserveMode) {\n                    case DELETE_IMMEDIATELY:\n                        assertTrue(\"Unable to delete temporary file \" + tmpFile, tmpFile.delete());\n                        break;\n                    case DELETE_ON_EXIT:\n                        tmpFile.deleteOnExit();\n                        break;\n                    // PRESERVE is a no-op\n                }\n            } else {\n                // default when preserveMode is null\n                tmpFile.deleteOnExit();\n            }\n            return tmpFile.getCanonicalFile();\n        } catch (IOException e) {\n            throw new RuntimeException(\"Unable to create or canonicalize temporary directory\");\n        }\n    }\n\n    public static File getFirstDirectory(File file) {\n        File result = file;\n        while (result.getParentFile() != null) {\n            result = result.getParentFile();\n        }\n\n        return result;\n    }\n\n    /**\n     * Modes for handling the removal of created temporary files\n     */\n    public enum TMP_FILE_PRESERVE_MODE {\n\n        /**\n         * Deletes the created file immediately\n         */\n        DELETE_IMMEDIATELY,\n\n        /**\n         * Asks the JVM to delete the file on exit\n         */\n        DELETE_ON_EXIT,\n\n        /**\n         * Preserve the file, do not delete it.  The caller is responsible for clean up.\n         */\n        PRESERVE\n    };\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/StartOrderResolverTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.util.*;\n\nimport io.fabric8.maven.docker.service.QueryService;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 16.10.14\n */\npublic class StartOrderResolverTest {\n\n    @Mocked\n    private QueryService queryService;\n    \n    @Before\n    @SuppressWarnings(\"unused\")\n    public void setup() throws Exception {\n        new Expectations() {{\n            queryService.hasContainer((String) withNotNull());\n            result = false;\n            minTimes = 1;\n        }};\n    }\n    \n    @Test\n    public void simple() {\n        checkData(new Object[][]{\n                { new T[]{new T(\"1\")}, new T[]{new T(\"1\")}},\n                { new T[]{new T(\"1\", \"2\"), new T(\"2\")}, new T[]{new T(\"2\"), new T(\"1\", \"2\")} },\n                { new T[]{new T(\"1\", \"2\", \"3\"), new T(\"2\", \"3\"), new T(\"3\")}, new T[]{new T(\"3\"), new T(\"2\", \"3\"), new T(\"1\", \"2\", \"3\")} },\n        });\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void circularDep() {\n        checkData(new Object[][] {\n                {new T[]{new T(\"1\", \"2\"), new T(\"2\", \"1\")}, new T[]{new T(\"1\", \"2\"), new T(\"2\", \"1\")}}\n        });\n        fail();\n    }\n\n    private void checkData(Object[][] data) {\n        for (Object[] aData : data) {\n            StartOrderResolver.Resolvable[] input = (StartOrderResolver.Resolvable[]) aData[0];\n            StartOrderResolver.Resolvable[] expected = (StartOrderResolver.Resolvable[]) aData[1];\n            List<StartOrderResolver.Resolvable> result = StartOrderResolver.resolve(queryService, Arrays.asList(input));\n            assertArrayEquals(expected, new ArrayList(result).toArray());\n        }\n    }\n\n\n    // ============================================================================\n\n    private static class T implements StartOrderResolver.Resolvable {\n\n        private String id;\n        private List<String> deps;\n\n        private T(String id,String ... dep) {\n            this.id = id;\n            deps = new ArrayList<>();\n            for (String d : dep) {\n                deps.add(d);\n            }\n        }\n\n        @Override\n        public String getName() {\n            return id;\n        }\n\n        @Override\n        public String getAlias() {\n            return null;\n        }\n\n        @Override\n        public List<String> getDependencies() {\n            return deps;\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            T t = (T) o;\n\n            if (id != null ? !id.equals(t.id) : t.id != null) return false;\n\n            return true;\n        }\n\n        @Override\n        public int hashCode() {\n            return id != null ? id.hashCode() : 0;\n        }\n\n        @Override\n        public String toString() {\n            return \"T{\" +\n                   \"id='\" + id + '\\'' +\n                   '}';\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/TimestampFactoryTest.java",
    "content": "package io.fabric8.maven.docker.util;/*\n *\n * Copyright 2014 Roland Huss\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\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 25/11/14\n */\npublic class TimestampFactoryTest {\n\n    ZonedDateTime ref;\n\n    @Before\n    public void setUp() throws Exception {\n        ref = TimestampFactory.createTimestamp(\"2014-11-24T22:34:00.761764812Z\");\n    }\n\n\n    @Test\n    public void testParse() throws Exception {\n        ZonedDateTime dt = ZonedDateTime.parse(\"2014-11-24T22:34:00.761Z\");\n        assertEquals(dt,ref);\n    }\n\n    @Test\n    public void testDateCompare() throws Exception {\n        ZonedDateTime ts = TimestampFactory.createTimestamp(\"2014-12-24T12:00:00.761764812Z\");\n        assertTrue(ref.compareTo(ts) < 0);\n    }\n\n\n\n    @Test\n    public void testEquals() throws Exception {\n        ZonedDateTime ts = TimestampFactory.createTimestamp(\"2014-11-24T22:34:00.761764812Z\");\n        assertEquals(0,ref.compareTo(ts));\n        assertEquals(ref,ts);\n    }\n\n    @Test\n    public void testHash() throws Exception {\n        ZonedDateTime ts = TimestampFactory.createTimestamp(\"2014-11-24T22:34:00.761764812Z\");\n        Set<ZonedDateTime> set = new HashSet<>();\n        set.add(ref);\n        set.add(ts);\n        assertEquals(1,set.size());\n    }\n\n    @Test\n    public void testNanoCompare() throws Exception {\n        ZonedDateTime ts = TimestampFactory.createTimestamp(\"2014-11-24T12:00:00.761764811Z\");\n        assertTrue(ref.compareTo(ts) > 0);\n    }\n\n    @Test\n    public void testNoNanos() throws Exception {\n        ZonedDateTime ts = TimestampFactory.createTimestamp(\"2014-11-24T12:00:00Z\");\n        assertTrue(ref.compareTo(ts) > 0);\n    }\n\n    @Test(expected = DateTimeParseException.class)\n    public void testInvalidSpec() throws Exception {\n        TimestampFactory.createTimestamp(\"\");\n    }\n\n    @Test\n    public void testNumericTimeZoneOffset() throws Exception {\n        ZonedDateTime ts3p = TimestampFactory.createTimestamp(\"2016-03-16T17:06:30.714387000+03:00\");\n        ZonedDateTime ts530p = TimestampFactory.createTimestamp(\"2016-03-16T17:06:30.714387000+05:30\");\n        ZonedDateTime ts4 = TimestampFactory.createTimestamp(\"2016-03-16T17:06:30.714387000-04:00\");\n        ZonedDateTime ts2 = TimestampFactory.createTimestamp(\"2016-03-16T17:06:30.714387000-02:00\");\n        ZonedDateTime tsz = TimestampFactory.createTimestamp(\"2016-03-16T17:06:30.714387000Z\");\n        assertTrue(ts2.compareTo(ts4) < 0);\n        assertTrue(tsz.compareTo(ts2) < 0);\n        assertTrue(ts3p.compareTo(ts4) < 0);\n        assertTrue(ts530p.compareTo(ts3p) < 0);\n    }\n\n    @Test(expected = NullPointerException.class)\n    public void testNullSpec() throws Exception {\n        TimestampFactory.createTimestamp(null);\n    }\n\n    @Test(expected = DateTimeParseException.class)\n    public void testInvalidNanos() throws Exception {\n        TimestampFactory.createTimestamp(\"2014-11-24T12:00:00.abzeZ\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/VolumeBindingUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport io.fabric8.maven.docker.config.RunVolumeConfiguration;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.io.File;\n\nimport static io.fabric8.maven.docker.util.PathTestUtil.DOT;\nimport static io.fabric8.maven.docker.util.PathTestUtil.TILDE;\nimport static io.fabric8.maven.docker.util.PathTestUtil.TMP_FILE_PRESERVE_MODE.DELETE_IMMEDIATELY;\nimport static io.fabric8.maven.docker.util.PathTestUtil.createTmpFile;\nimport static io.fabric8.maven.docker.util.PathTestUtil.join;\nimport static io.fabric8.maven.docker.util.PathTestUtil.stripLeadingPeriod;\nimport static io.fabric8.maven.docker.util.VolumeBindingUtil.isRelativePath;\nimport static io.fabric8.maven.docker.util.VolumeBindingUtil.isUserHomeRelativePath;\nimport static io.fabric8.maven.docker.util.VolumeBindingUtil.resolveRelativeVolumeBinding;\nimport static io.fabric8.maven.docker.util.VolumeBindingUtil.resolveRelativeVolumeBindings;\nimport static java.lang.String.format;\nimport static java.util.Collections.singletonList;\nimport static junit.framework.TestCase.assertTrue;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\n/**\n *\n */\npublic class VolumeBindingUtilTest {\n\n    private static final String CLASS_NAME = VolumeBindingUtilTest.class.getSimpleName();\n\n    private static final String SEP = System.getProperty(\"file.separator\");\n\n    /**\n     * An absolute file path that represents a base directory.  It is important for the JVM to create the the file so\n     * that the absolute path representation of the test platform is used.\n     */\n    private static final File ABS_BASEDIR = createTmpFile(CLASS_NAME, DELETE_IMMEDIATELY);\n\n    /**\n     * Host portion of a volume binding string representing a directory relative to the current working directory.\n     */\n    private static final String RELATIVE_PATH = DOT + SEP + \"rel\";          // ./rel\n\n    /**\n     * Host portion of a volume binding string representing a directory relative to the current user's home directory.\n     */\n    private static final String USER_PATH = TILDE + SEP + \"relUser\";        // ~/relUser\n\n    /**\n     * Host portion of a volume binding string representing the current user's home directory.\n     */\n    private static final String USER_HOME = TILDE + \"user\";                 // ~user\n\n    /**\n     * Container portion of a volume binding string; the location in the container where the host portion is mounted.\n     */\n    private static final String CONTAINER_PATH = \"/path/to/container/dir\";\n\n    /**\n     * Format of a volume binding string that does not have any access controls.  Format is: host binding string\n     * portion, container binding string portion\n     */\n    private final String BIND_STRING_FMT = \"%s:%s\";\n\n    /**\n     * Format of a volume binding string that contains access controls.  Format is: host binding string portion,\n     * container binding string portion, access control portion.\n     */\n    private final String BIND_STRING_WITH_ACCESS_FMT = \"%s:%s:%s\";\n\n    /**\n     * Access control portion of a volume binding string.\n     */\n    private final String RO_ACCESS = \"ro\";\n\n    /**\n     * Insures the supplied base directory is absolute.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public void relativeBaseDir() {\n        resolveRelativeVolumeBinding(new File(\"relative/\"),\n                format(BIND_STRING_FMT, RELATIVE_PATH, CONTAINER_PATH));\n    }\n\n    /**\n     * Insures that a host volume binding string that contains a path relative to the current working directory is\n     * resolved to the supplied base directory.\n     */\n    @Test\n    public void testResolveRelativeVolumePath() {\n        String volumeString = format(BIND_STRING_FMT, RELATIVE_PATH, CONTAINER_PATH);\n\n        // './rel:/path/to/container/dir' to '/absolute/basedir/rel:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                new File(ABS_BASEDIR, stripLeadingPeriod(RELATIVE_PATH)), CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * Insures that a host volume binding string that contains a path relative to the current working directory <em>and\n     * </em> specifies access controls resolves to the supplied base directory <em>and</em> that the access controls are\n     * preserved through the operation.\n     */\n    @Test\n    public void testResolveRelativeVolumePathWithAccessSpecifications() {\n        String volumeString = format(BIND_STRING_WITH_ACCESS_FMT, RELATIVE_PATH, CONTAINER_PATH, RO_ACCESS);\n\n        // './rel:/path/to/container/dir:ro' to '/absolute/basedir/rel:/path/to/container/dir:ro'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_WITH_ACCESS_FMT,\n                new File(ABS_BASEDIR, stripLeadingPeriod(RELATIVE_PATH)), CONTAINER_PATH, RO_ACCESS);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * Insures that a host volume binding string that contains a path relative to the user's home directory resolves to\n     * the user's home directory and not the supplied base directory.\n     */\n    @Test\n    public void testResolveUserVolumePath() {\n        String volumeString = format(BIND_STRING_FMT, USER_PATH, CONTAINER_PATH);\n\n        // '~/rel:/path/to/container/dir' to '/user/home/rel:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(new File(\"ignored\"), volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                new File(System.getProperty(\"user.home\"), PathTestUtil.stripLeadingTilde(USER_PATH)), CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * Resolving arbitrary user home paths, e.g. represented as {@code ~user}, is not supported.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public void testResolveUserHomeVolumePath() {\n        String volumeString = format(BIND_STRING_FMT, USER_HOME, CONTAINER_PATH);\n\n        // '~user:/path/to/container/dir' to '/home/user:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(new File(\"ignored\"), volumeString);\n    }\n\n    /**\n     * Insures that volume binding strings referencing a named volume are preserved untouched.\n     */\n    @Test\n    public void testResolveNamedVolume() throws Exception {\n        String volumeName = \"volname\";\n        String volumeString = format(BIND_STRING_FMT, volumeName, CONTAINER_PATH);\n\n        // volumeString should be untouched\n        assertEquals(volumeString, resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString));\n    }\n\n    /**\n     * Insures that volume binding strings that contain an absolute path for the host portion are preserved untouched.\n     */\n    @Test\n    public void testResolveAbsolutePathMapping() {\n        String absolutePath =\n                createTmpFile(VolumeBindingUtilTest.class.getSimpleName(), DELETE_IMMEDIATELY).getAbsolutePath();\n        String volumeString = format(BIND_STRING_FMT, absolutePath, CONTAINER_PATH);\n\n        // volumeString should be untouched\n        assertEquals(volumeString, resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString));\n    }\n\n    /**\n     * Insures that volume binding strings with an absolute host portion are returned unchanged (no resolution necessary\n     * because the the path is absolute)\n     */\n    @Test\n    public void testResolveSinglePath() {\n        String absolutePath =\n                createTmpFile(VolumeBindingUtilTest.class.getSimpleName(), DELETE_IMMEDIATELY).getAbsolutePath();\n\n        // volumeString should be untouched\n        assertEquals(absolutePath, resolveRelativeVolumeBinding(ABS_BASEDIR, absolutePath));\n    }\n\n    /**\n     * Insures that relative paths in the host portion of a volume binding string are properly resolved against a base\n     * directory when present in a {@link RunVolumeConfiguration}.\n     */\n    @Test\n    public void testResolveVolumeBindingsWithRunVolumeConfiguration() {\n        RunVolumeConfiguration.Builder builder = new RunVolumeConfiguration.Builder();\n        builder.bind(singletonList(format(BIND_STRING_FMT, RELATIVE_PATH, CONTAINER_PATH)));\n        RunVolumeConfiguration volumeConfiguration = builder.build();\n\n\n        // './rel:/path/to/container/dir' to '/absolute/basedir/rel:/path/to/container/dir'\n        resolveRelativeVolumeBindings(ABS_BASEDIR, volumeConfiguration);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                join(\"\", ABS_BASEDIR.getAbsolutePath(),\n                        stripLeadingPeriod(RELATIVE_PATH)), CONTAINER_PATH);\n        assertEquals(expectedBindingString, volumeConfiguration.getBind().get(0));\n    }\n\n    /**\n     * Insures that a relative path referencing the parent directory are properly resolved against a base directory.\n     */\n    @Test\n    public void testResolveParentRelativeVolumePath() {\n        String relativePath = DOT + RELATIVE_PATH; // '../rel'\n        String volumeString = format(BIND_STRING_FMT, relativePath, CONTAINER_PATH);\n\n        // '../rel:/path/to/container/dir to '/absolute/rel:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                new File(ABS_BASEDIR.getParent(), stripLeadingPeriod(RELATIVE_PATH)), CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * Insures that a relative path referencing the parent directory are properly resolved against a base directory.\n     */\n    @Test\n    @Ignore(\"TODO: fix this test, and DockerPathUtil as well\")\n    public void testResolveParentRelativeVolumePathWithNoParent() {\n        String relativePath = join(SEP, DOT + DOT, DOT + DOT, \"rel\"); // '../../rel'\n        String volumeString = format(BIND_STRING_FMT, relativePath, CONTAINER_PATH);\n        File baseDir = PathTestUtil.getFirstDirectory(ABS_BASEDIR);\n\n        // '../../rel:/path/to/container/dir to '/absolute/rel:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(baseDir, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                new File(baseDir.getParent(), stripLeadingPeriod(RELATIVE_PATH)), CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * The volume binding string: {@code rel:/path/to/container/mountpoint} is not resolved, because {@code rel} is\n     * considered a <em>named volume</em>.\n     */\n    @Test\n    public void testResolveRelativeVolumePathWithoutCurrentDirectory() throws Exception {\n        String relativePath = \"rel\";\n        String volumeString = format(BIND_STRING_FMT, relativePath, CONTAINER_PATH);\n\n        // 'rel:/path/to/container/dir' to 'rel:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT, relativePath, CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    /**\n     * The volume binding string: {@code src/test/docker:/path/to/container/mountpoint} is resolved, because {@code src/\n     * test/docker} is considered a <em>relative path</em>.\n     */\n    @Test\n    public void testResolveRelativeVolumePathContainingSlashes() throws Exception {\n        String relativePath = \"src\" + SEP + \"test\" + SEP + \"docker\";\n        String volumeString = format(BIND_STRING_FMT, relativePath, CONTAINER_PATH);\n\n        // 'src/test/docker:/path/to/container/dir' to '/absolute/basedir/src/test/docker:/path/to/container/dir'\n        String relativizedVolumeString = resolveRelativeVolumeBinding(ABS_BASEDIR, volumeString);\n\n        String expectedBindingString = format(BIND_STRING_FMT,\n                new File(ABS_BASEDIR, relativePath), CONTAINER_PATH);\n        assertEquals(expectedBindingString, relativizedVolumeString);\n    }\n\n    @Test\n    public void testIsRelativePath() throws Exception {\n        assertTrue(isRelativePath(\"rel\" + SEP));                            // rel/\n        assertTrue(isRelativePath(join(SEP, \"src\", \"test\", \"docker\")));         // src/test/docker\n        assertTrue(isRelativePath(join(SEP, DOT, \"rel\")));                      // ./rel\n        assertTrue(isRelativePath(join(SEP, TILDE, \"rel\")));                    // ~/rel\n        assertTrue(isRelativePath(join(SEP, DOT + DOT, \"rel\")));                // ../rel\n        assertFalse(isRelativePath(\"rel\"));                                 // 'rel' is a named volume in this case\n        assertFalse(isRelativePath(\n                createTmpFile(VolumeBindingUtilTest.class.getSimpleName(), DELETE_IMMEDIATELY)\n                        .getAbsolutePath()));                                            // is absolute\n    }\n\n    @Test\n    public void testIsUserRelativeHomeDir() throws Exception {\n        assertFalse(isUserHomeRelativePath(join(TILDE, \"foo\", \"bar\")));         // foo~bar\n        assertFalse(isUserHomeRelativePath(\"foo\" + TILDE));                 // foo~\n        assertFalse(isUserHomeRelativePath(\"foo\"));                         // foo\n        assertTrue(isUserHomeRelativePath(TILDE + \"user\"));                 // ~user\n        assertTrue(isUserHomeRelativePath(join(SEP, TILDE, \"dir\")));            // ~/dir\n        assertTrue(isUserHomeRelativePath(join(SEP, TILDE + \"user\", \"dir\")));   // ~user/dir\n    }\n\n    /**\n     * Test windows paths even if the test JVM runtime is on *nix, specifically the consideration of an 'absolute'\n     * path by {@link VolumeBindingUtil#isRelativePath(String)}.\n     */\n    @Test\n    public void testIsRelativePathForWindows() {\n        assertFalse(isRelativePath(\"C:\\\\foo\"));                            // C:\\foo\n        assertFalse(isRelativePath(\"x:\\\\bar\"));                            // x:\\bar\n        assertFalse(isRelativePath(\"C:\\\\\"));                               // C:\\\n        assertFalse(isRelativePath(\"\\\\\"));                                 // \\\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/WaitUtilTest.java",
    "content": "package io.fabric8.maven.docker.util;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.ServerSocket;\nimport java.util.Collections;\nimport java.util.concurrent.*;\n\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\nimport com.sun.net.httpserver.HttpServer;\nimport io.fabric8.maven.docker.config.WaitConfiguration;\nimport io.fabric8.maven.docker.wait.*;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.junit.Assert.*;\n\n/**\n * @author roland\n * @since 18.10.14\n */\n@SuppressWarnings(\"restriction\")\npublic class WaitUtilTest {\n\n    static HttpServer server;\n    static int port;\n    static String httpPingUrl;\n    private static String serverMethodToAssert;\n\n    @Test(expected = TimeoutException.class)\n    public void httpFail() throws TimeoutException, PreconditionFailedException {\n\n        HttpPingChecker checker = new HttpPingChecker(\"http://127.0.0.1:\" + port + \"/fake-context/\");\n        wait(500, checker);\n    }\n\n    private static long wait(int wait, WaitChecker checker) throws WaitTimeoutException, PreconditionFailedException {\n        return wait(-1, wait, checker);\n    }\n\n    private static long wait(int failAfter, int wait, WaitChecker checker) throws WaitTimeoutException, PreconditionFailedException {\n        return WaitUtil.wait(new TestWaitPrecondition(failAfter), wait, checker);\n    }\n\n    @Test\n    public void httpSuccess() throws TimeoutException, PreconditionFailedException {\n        HttpPingChecker checker = new HttpPingChecker(httpPingUrl);\n        long waited = wait(700, checker);\n        assertTrue(\"Waited less than 700ms: \" + waited, waited < 700);\n    }\n\n    @Test\n    public void containerNotRunningButWaitConditionOk() throws TimeoutException, PreconditionFailedException {\n        HttpPingChecker checker = new HttpPingChecker(httpPingUrl);\n        long waited = wait(1,700, checker);\n        assertTrue(\"Waited less than 700ms: \" + waited, waited < 700);\n    }\n\n    @Test(expected = PreconditionFailedException.class)\n    public void containerNotRunningAndWaitConditionNok() throws TimeoutException, PreconditionFailedException {\n        HttpPingChecker checker = new HttpPingChecker(\"http://127.0.0.1:\" + port + \"/fake-context/\");\n        wait(0, 700, checker);\n    }\n\n    @Test\n    public void httpSuccessWithStatus() throws TimeoutException, PreconditionFailedException {\n        for (String status : new String[] { \"200\", \"200 ... 300\", \"200..250\" }) {\n            long waited = wait(700, new HttpPingChecker(httpPingUrl, WaitConfiguration.DEFAULT_HTTP_METHOD, status));\n            assertTrue(\"Waited less than  700ms: \" + waited, waited < 700);\n        }\n    }\n\n    @Test(expected = TimeoutException.class)\n    public void httpFailWithStatus() throws TimeoutException, PreconditionFailedException {\n        wait(700, new HttpPingChecker(httpPingUrl, WaitConfiguration.DEFAULT_HTTP_METHOD, \"500\"));\n    }\n\n    @Test\n    public void httpSuccessWithGetMethod() throws Exception {\n        serverMethodToAssert = \"GET\";\n        try {\n            HttpPingChecker checker = new HttpPingChecker(httpPingUrl, \"GET\", WaitConfiguration.DEFAULT_STATUS_RANGE);\n            long waited = wait(700, checker);\n            assertTrue(\"Waited less than 500ms: \" + waited, waited < 700);\n        } finally {\n            serverMethodToAssert = \"HEAD\";\n        }\n    }\n\n    @Test\n    public void tcpSuccess() throws TimeoutException, PreconditionFailedException {\n        TcpPortChecker checker = new TcpPortChecker(\"localhost\", Collections.singletonList(port));\n        long waited = wait(700, checker);\n        assertTrue(\"Waited less than 700ms: \" + waited, waited < 700);\n    }\n\n    @Test\n    public void cleanupShouldBeCalledAfterMatchedException() throws WaitTimeoutException, PreconditionFailedException {\n        StubWaitChecker checker = new StubWaitChecker(true);\n        wait(0, checker);\n        assertTrue(checker.isCleaned());\n    }\n\n    @Test\n    public void cleanupShouldBeCalledAfterFailedException() throws PreconditionFailedException {\n        StubWaitChecker checker = new StubWaitChecker(false);\n        try {\n            wait(0, checker);\n            fail(\"Failed expectation expected\");\n        } catch (WaitTimeoutException e) {\n            assertTrue(checker.isCleaned());\n        }\n    }\n\n    @Test\n    public void waitOnCallable() throws Exception {\n        long waited = waitOnCallable(1, 500);\n\n        assertTrue(500 <= waited);\n        assertTrue(1000 > waited);\n    }\n\n    @Test\n    public void waitOnCallableFullWait() throws Exception {\n        long waited = waitOnCallable(1, 1000);\n        assertTrue(1000 <= waited);\n    }\n\n    private long waitOnCallable(long wait, final long sleep) throws WaitTimeoutException, ExecutionException {\n        return WaitUtil.wait(5, new Callable<Void>() {\n            @Override\n            public Void call() throws Exception {\n                Thread.sleep(sleep);\n                return null;\n            }\n        });\n    }\n\n    private static class StubWaitChecker implements WaitChecker {\n\n        private final boolean checkResult;\n        private boolean cleaned = false;\n\n        public StubWaitChecker(boolean checkResult) {\n            this.checkResult = checkResult;\n        }\n\n        @Override\n        public boolean check() {\n            return checkResult;\n        }\n\n        @Override\n        public void cleanUp() {\n            cleaned = true;\n        }\n\n        @Override\n        public String getLogLabel() {\n            return \"\";\n        }\n\n        public boolean isCleaned() {\n            return cleaned;\n        }\n    }\n\n    @BeforeClass\n    public static void createServer() throws IOException {\n        port = getRandomPort();\n        serverMethodToAssert = \"HEAD\";\n        System.out.println(\"Created HTTP server at port \" + port);\n        InetAddress address = InetAddress.getLoopbackAddress();\n        InetSocketAddress socketAddress = new InetSocketAddress(address, port);\n        server = HttpServer.create(socketAddress, 10);\n\n        // Prepare executor pool\n        server.setExecutor(Executors.newSingleThreadExecutor());\n        server.createContext(\"/test/\", new HttpHandler() {\n            @Override\n            public void handle(HttpExchange httpExchange) throws IOException {\n                String method = httpExchange.getRequestMethod();\n                assertEquals(serverMethodToAssert, method);\n                httpExchange.sendResponseHeaders(200, -1);\n            }\n        });\n        httpPingUrl = \"http://127.0.0.1:\" + port + \"/test/\";\n        server.start();\n\n        // preload - first time use almost always lasts much longer (i'm assuming its http client initialization behavior)\n        try {\n            wait(700, new HttpPingChecker(httpPingUrl));\n        } catch (TimeoutException | PreconditionFailedException exp) {\n            // expected\n        }\n    }\n\n    @AfterClass\n    public static void cleanupServer() {\n        server.stop(1);\n    }\n\n    private static int getRandomPort() throws IOException {\n        for (int port = 22332; port < 22500; port++) {\n            if (trySocket(port)) {\n                return port;\n            }\n        }\n        throw new IllegalStateException(\"Cannot find a single free port\");\n    }\n\n    private static boolean trySocket(int port) throws IOException {\n        InetAddress address = InetAddress.getByName(\"localhost\");\n        try (ServerSocket s = new ServerSocket()) {\n            s.bind(new InetSocketAddress(address, port));\n            return true;\n        } catch (IOException exp) {\n            System.err.println(\"Port \" + port + \" already in use, tying next ...\");\n            // exp.printStackTrace();\n            // next try ....\n        }\n        return false;\n    }\n\n    private static class TestWaitPrecondition implements WaitUtil.Precondition {\n        private int nrFailAfter;\n\n        public TestWaitPrecondition(int nrFailAfter) {\n            this.nrFailAfter = nrFailAfter;\n        }\n\n        @Override\n        public boolean isOk() {\n            return nrFailAfter == -1 || nrFailAfter-- > 0;\n        }\n\n        @Override\n        public void cleanup() {}\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java",
    "content": "package io.fabric8.maven.docker.util.aws;\n\nimport io.fabric8.maven.docker.access.AuthConfig;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Mocked;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\n\nimport static java.util.UUID.randomUUID;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\npublic class AwsSdkAuthConfigFactoryTest {\n\n    @Rule\n    public final EnvironmentVariables environmentVariables = new EnvironmentVariables();\n\n    @Mocked\n    private Logger log;\n    private AwsSdkAuthConfigFactory objectUnderTest;\n\n\n    @Before\n    public void setup() {\n        objectUnderTest = new AwsSdkAuthConfigFactory(log);\n    }\n\n    @Test\n    public void nullValueIsPassedOn() {\n        AuthConfig authConfig = objectUnderTest.createAuthConfig();\n\n        assertNull(authConfig);\n    }\n\n    @Test\n    public void reflectionWorksForBasicCredentials() {\n        String accessKey = randomUUID().toString();\n        String secretKey = randomUUID().toString();\n        environmentVariables.set(\"AWSCredentials.AWSAccessKeyId\", accessKey);\n        environmentVariables.set(\"AWSCredentials.AWSSecretKey\", secretKey);\n\n        AuthConfig authConfig = objectUnderTest.createAuthConfig();\n\n        assertNotNull(authConfig);\n        assertEquals(accessKey, authConfig.getUsername());\n        assertEquals(secretKey, authConfig.getPassword());\n        assertNull(authConfig.getAuth());\n        assertNull(authConfig.getIdentityToken());\n    }\n\n    @Test\n    public void reflectionWorksForSessionCredentials() {\n        String accessKey = randomUUID().toString();\n        String secretKey = randomUUID().toString();\n        String sessionToken = randomUUID().toString();\n        environmentVariables.set(\"AWSCredentials.AWSAccessKeyId\", accessKey);\n        environmentVariables.set(\"AWSCredentials.AWSSecretKey\", secretKey);\n        environmentVariables.set(\"AWSSessionCredentials.SessionToken\", sessionToken);\n\n        AuthConfig authConfig = objectUnderTest.createAuthConfig();\n\n        assertNotNull(authConfig);\n        assertEquals(accessKey, authConfig.getUsername());\n        assertEquals(secretKey, authConfig.getPassword());\n        assertEquals(sessionToken, authConfig.getAuth());\n        assertNull(authConfig.getIdentityToken());\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/wait/ExitCodeCheckerTest.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport io.fabric8.maven.docker.access.DockerAccessException;\nimport io.fabric8.maven.docker.model.Container;\nimport io.fabric8.maven.docker.service.QueryService;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n\npublic class ExitCodeCheckerTest {\n\n    private static final String CONTAINER_ID = \"1234\";\n\n    @Mocked\n    private QueryService queryService;\n    @Mocked\n    private Container container;\n\n    @Test\n    public void checkReturnsFalseIfContainerDoesNotExist() throws Exception {\n\n        new Expectations() {{\n            Exception e = new DockerAccessException(\"Cannot find container %s\", CONTAINER_ID);\n            queryService.getMandatoryContainer(CONTAINER_ID); result = e;\n        }};\n\n        ExitCodeChecker checker = new ExitCodeChecker(0, queryService, CONTAINER_ID);\n        assertThat(checker.check()).isFalse();\n    }\n\n    @Test\n    public void checkReturnsFalseIfContainerIsStillRunning() throws Exception {\n\n        new Expectations() {{\n            container.getExitCode(); result = null;\n        }};\n\n        ExitCodeChecker checker = new ExitCodeChecker(0, queryService, CONTAINER_ID);\n        assertThat(checker.check()).isFalse();\n    }\n\n    @Test\n    public void checkReturnsFalseIfActualExitCodeDoesNotMatchExpectedExitCode() throws Exception {\n\n        new Expectations() {{\n            container.getExitCode(); result = 1;\n        }};\n\n        ExitCodeChecker checker = new ExitCodeChecker(0, queryService, CONTAINER_ID);\n        assertThat(checker.check()).isFalse();\n    }\n\n    @Test\n    public void checkReturnsTrueIfActualExitCodeMatchesExpectedExitCode() throws Exception {\n\n        new Expectations() {{\n            container.getExitCode(); result = 0;\n        }};\n\n        ExitCodeChecker checker = new ExitCodeChecker(0, queryService, CONTAINER_ID);\n        assertThat(checker.check()).isTrue();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/wait/LogMatchCallbackTest.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.util.Logger;\nimport io.fabric8.maven.docker.util.TimestampFactory;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Test;\n\npublic class LogMatchCallbackTest {\n\n    private final String defaultPattern = \"Hello, world!\";\n\n    @Mocked\n    private Logger logger;\n\n    @Mocked\n    private LogWaitCheckerCallback callback;\n\n    @Test(expected = LogCallback.DoneException.class)\n    public void matchingSingleLineSucceeds() throws Exception {\n        final String patternString = \"The start has finished right now\";\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, patternString);\n\n        new Expectations() {{\n            callback.matched();\n            times = 1;\n        }};\n\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), patternString);\n    }\n\n    @Test(expected = LogCallback.DoneException.class)\n    public void matchingMultipleLinesSucceeds() throws Exception {\n        final String patterString = \"(?s)ready to accept connections.*\\\\n.*ready to accept connections\";\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, patterString);\n\n        new Expectations() {{\n            callback.matched();\n            times = 1;\n        }};\n\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"LOG:  database system is ready to accept connections\" );\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"LOG:  autovacuum launcher started\");\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"LOG:  database system is shut down\");\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"LOG:  database system is ready to accept connections\");\n    }\n\n    @Test\n    public void matchingLinesNonConformantToThePatternFails() throws Exception {\n        final String patterString = \"The start has started right now\";\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, patterString);\n\n        new Expectations() {{\n            callback.matched();\n            times = 0;\n        }};\n\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"LOG:  database system is ready to accept connections\" );\n    }\n\n    @Test(expected = LogCallback.DoneException.class)\n    public void matchingPartitialLineSucceeds() throws Exception {\n        final String patterString = \"waiting for connections\";\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, patterString);\n\n        new Expectations() {{\n            callback.matched();\n            times = 1;\n        }};\n\n        logMatchCallback.log(1, TimestampFactory.createTimestamp(), \"2017-11-21T12:44:43.678+0000 I NETWORK  [initandlisten] waiting for connections on port 27017\" );\n    }\n\n    @Test\n    public void errorMethodProducesLogMessage() {\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, defaultPattern);\n\n        new Expectations() {{\n            logger.error(anyString, anyString);\n            times = 1;\n        }};\n\n        logMatchCallback.error(\"The message\");\n    }\n\n    @Test\n    public void openMethodProducesLogMessage() {\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, \"\");\n\n        new Expectations() {{\n            logger.debug(anyString);\n            times = 1;\n        }};\n\n        logMatchCallback.open();\n    }\n\n    @Test\n    public void closeMethodProducesLogMessage() {\n        final LogMatchCallback logMatchCallback = new LogMatchCallback(logger, callback, \"\");\n\n        new Expectations() {{\n            logger.debug(anyString);\n            times = 1;\n        }};\n\n        logMatchCallback.close();\n    }\n}"
  },
  {
    "path": "src/test/java/io/fabric8/maven/docker/wait/LogWaitCheckerTest.java",
    "content": "package io.fabric8.maven.docker.wait;\n\nimport io.fabric8.maven.docker.access.DockerAccess;\nimport io.fabric8.maven.docker.access.log.LogCallback;\nimport io.fabric8.maven.docker.access.log.LogGetHandle;\nimport io.fabric8.maven.docker.util.Logger;\nimport mockit.Expectations;\nimport mockit.Mocked;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author roland\n * @since 25/03/2017\n */\n\npublic class LogWaitCheckerTest {\n\n    @Mocked\n    private Logger logger;\n\n    @Mocked\n    private DockerAccess access;\n\n    @Mocked\n    private LogGetHandle handle;\n\n    private LogWaitChecker logWaitChecker;\n\n    @Before\n    public void setup() {\n\n        presetExpections();\n        logWaitChecker = new LogWaitChecker(\"Hello, world!\", access, \"1\", logger);\n    }\n\n    @Test\n    public void checkerRegistersAsyncLogWhenCreated() {\n\n        new Expectations() {{\n            access.getLogAsync(anyString, withInstanceOf(LogCallback.class));\n            times = 1;\n        }};\n\n        final LogWaitChecker logWaitChecker =\n                new LogWaitChecker(\"Hello, world!\", access, \"1\", logger);\n    }\n\n    @Test\n    public void checkingAfterMatchingSucceeds() {\n\n        logWaitChecker.matched();\n\n        assertThat(logWaitChecker.check()).isTrue();\n    }\n\n    @Test\n    public void checkingWithoutMatchingFails() {\n\n        assertThat(logWaitChecker.check()).isFalse();\n    }\n\n    @Test\n    public void checkerClosesLogHandle() {\n\n        new Expectations() {{\n            handle.finish();\n            times = 1;\n        }};\n\n        logWaitChecker.cleanUp();\n    }\n\n    @Test\n    public void checkerReturnsValidLogLabel() {\n\n        final String expectedLogLabel = \"on log out '\" + \"Hello, world!\" + \"'\";\n        assertThat(logWaitChecker.getLogLabel()).isEqualTo(expectedLogLabel);\n    }\n\n    private void presetExpections() {\n\n        new Expectations() {{\n            access.getLogAsync(anyString, withInstanceOf(LogCallback.class));\n            times = 1;\n            result = handle;\n        }};\n    }\n}\n"
  },
  {
    "path": "src/test/resources/compose/docker-compose-network-aliases.yml",
    "content": "version: '2.2'\nservices:\n  service1:\n    image: image\n    networks:\n      network1:\n        aliases:\n          - alias1\n          - alias2\n  service2:\n    image: image\n    networks:\n      - network1\n  service3:\n    image: image\n    networks:\n      network1:\n        aliases:\n          - alias1\n          "
  },
  {
    "path": "src/test/resources/compose/docker-compose.yml",
    "content": "#\n# supported run compose keys\n#\n# supported but not listed here:\n#\n# - container_name\n# - dns (list)\n# - dns_search (list)\n#\nversion: 2.2\nservices:\n  service:\n    cap_add:\n      - CAP\n    cap_drop:\n      - CAP\n    command: command.sh\n    cgroup_parent: cgroup_parent\n    cpu_shares: 1\n    cpuset: 0,1\n    cpus: 1\n    devices:\n    - \"/dev/device:/dev/device\"\n    dns: 8.8.8.8\n    dns_search: example.com\n    tmpfs:\n    - \"/var/lib/mysql:10m\"\n    - \"/var/lib/data\"\n    domainname: domain.com\n    entrypoint: entrypoint.sh\n  # env_file:\n    environment:\n      NAME: name\n      BOOL: \"true\"\n  #   SECRET:\n    external_links:\n      - link1\n    extra_hosts:\n      - \"localhost:127.0.0.1\"\n    hostname: subdomain\n    image: image\n    labels:\n      - \"label1=label\"\n    links:\n      - redis\n  # log_driver: json-file\n  # log_opt:\n  #   key: value\n    mem_limit: 1\n    memswap_limit: 1\n  # net: \"host\"\n  # pid: \"host\"\n    ports:\n      - \"8081:8080\"\n    privileged: true\n  # security_opt:\n  #   - label:user:USER\n  # ulimits\n    restart: on-failure:1\n    user: tomcat\n    volumes:\n      - /foo\n      # mount /tmp with rw access control\n      - /tmp:/tmp:rw\n      # A named volume with access control\n      - namedvolume:/volume:ro\n      # ${project.build.directory}/test-classes/compose/version/ should actually exist, because\n      # src/test/resources/compose is recursively copied to target/test-classes/compose by the Maven lifecycle\n      - compose/version/:/tmp/version\n    volumes_from:\n      - from\n    working_dir: foo"
  },
  {
    "path": "src/test/resources/compose/version/compose-no-version.yml",
    "content": "services:\n  service:\n    image: image"
  },
  {
    "path": "src/test/resources/compose/version/compose-version-2.yml",
    "content": "version: 2\nservices:\n  service:\n    image: image"
  },
  {
    "path": "src/test/resources/compose/version/compose-version-2x.yml",
    "content": "version: 2.4\nservices:\n  service:\n    image: image"
  },
  {
    "path": "src/test/resources/compose/version/compose-wrong-version.yml",
    "content": "version: 23.5\nservices:\n  service:\n    image: image"
  },
  {
    "path": "src/test/resources/docker/Dockerfile.multiline_label.test",
    "content": "FROM image\nLABEL key=unquoted flag=\"\" with_space=\"1.fc nuremberg\" some-json=\"{\\\n  \\\"key\\\": \\\"value\\\"\\\n}\\\n\"\nCOPY /src /maven/dest\nCMD [\"c1\",\"c2\"]\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile.test",
    "content": "FROM image\nMAINTAINER maintainer@example.com\nENV foo=bar\nLABEL com.acme.foobar=\"How are \\\"you\\\" ?\"\nEXPOSE 8080\nCOPY /src /export/dest\nWORKDIR /tmp\nSHELL [\"/bin/sh\",\"-c\"]\nRUN echo something\nRUN echo second\nVOLUME [\"/vol1\"]\nCMD [\"c1\",\"c2\"]\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_assembly_verify_copy_chown_valid.test",
    "content": "FROM image\nCOPY --chown=app maven/* ./\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_assembly_verify_copy_invalid.test",
    "content": "FROM image\nCOPY nonassembly/* ./\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_assembly_verify_copy_valid.test",
    "content": "FROM image\nCOPY maven/* ./\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_optimised.test",
    "content": "FROM image\nMAINTAINER maintainer@example.com\nENV foo=bar\nLABEL com.acme.foobar=\"How are \\\"you\\\" ?\"\nEXPOSE 8080\nCOPY /src /export/dest\nWORKDIR /tmp\nRUN echo something && echo second && echo third && echo fourth && echo fifth\nVOLUME [\"/vol1\"]\nCMD [\"c1\",\"c2\"]\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_tcp.test",
    "content": "FROM image\nMAINTAINER maintainer@example.com\nEXPOSE 8080/tcp\nCOPY /src /export/dest\nWORKDIR /tmp\nRUN echo something\nRUN echo second\nVOLUME [\"/vol1\"]\nCMD [\"c1\",\"c2\"]\n"
  },
  {
    "path": "src/test/resources/docker/Dockerfile_udp.test",
    "content": "FROM image\nMAINTAINER maintainer@example.com\nEXPOSE 8080/udp\nCOPY /src /export/dest\nWORKDIR /tmp\nRUN echo something\nRUN echo second\nVOLUME [\"/vol1\"]\nCMD [\"c1\",\"c2\"]\n"
  },
  {
    "path": "src/test/resources/docker/containerCreateConfigAll.json",
    "content": "{\r\n  \"Image\":\"base\",\r\n  \"Hostname\":\"hostname\",\r\n  \"Domainname\":\"domain.com\",\r\n  \"User\":\"user\",\r\n  \"WorkingDir\":\"/foo\",\r\n  \"Entrypoint\":[\r\n    \"entrypoint\"\r\n  ],\r\n  \"ExposedPorts\":{\r\n    \"22/tcp\":{\r\n\r\n    }\r\n  },\r\n  \"Env\":[\r\n    \"foo=bar\"\r\n  ],\r\n  \"Cmd\":[\r\n    \"date\"\r\n  ],\r\n  \"HostConfig\":{\r\n    \"ExtraHosts\":[\r\n      \"localhost:127.0.0.1\"\r\n    ],\r\n    \"Links\":[\r\n      \"db1:db1\",\r\n      \"db2:db2\"\r\n    ],\r\n    \"PortBindings\":{\r\n      \"22/tcp\":[\r\n        {\r\n          \"HostPort\":\"11022\",\r\n          \"HostIp\":\"0.0.0.0\"\r\n        }\r\n      ]\r\n    },\r\n    \"Privileged\":true,\r\n    \"ShmSize\":1024,\r\n    \"Dns\":[\r\n      \"8.8.8.8\"\r\n    ],\r\n    \"DnsSearch\":[\r\n      \"domain.com\"\r\n    ],\r\n    \"CapAdd\":[\r\n      \"NET_ADMIN\"\r\n    ],\r\n    \"CapDrop\":[\r\n      \"MKNOD\"\r\n    ],\r\n    \"SecurityOpt\":[\r\n      \"seccomp=unconfined\"\r\n    ],\r\n    \"Memory\":1,\r\n    \"MemorySwap\":1,\r\n    \"RestartPolicy\":{\r\n      \"Name\":\"on-failure\",\r\n      \"MaximumRetryCount\":1\r\n    },\r\n    \"Ulimits\":[\r\n      {\r\n        \"Name\":\"memlock\",\r\n        \"Hard\":1024,\r\n        \"Soft\":2048\r\n      }\r\n    ],\r\n    \"CpuShares\":1,\r\n    \"NanoCpus\": 1000000000,\r\n    \"CpusetCpus\":\"0,1\",\r\n    \"ReadonlyRootfs\":false,    \r\n    \"AutoRemove\":false,    \r\n    \"Binds\":[\r\n      \"/host_tmp:/container_tmp\"\r\n    ],\r\n    \"VolumesFrom\":[\r\n      \"parentContainer\",\r\n      \"otherContainer\"\r\n    ],\r\n    \"NetworkMode\":\"custom_network\"\r\n  },\r\n  \"Volumes\":{\r\n    \"/container_tmp\":{\r\n\r\n    }\r\n  },\r\n  \"NetworkingConfig\":{\r\n    \"EndpointsConfig\":{\r\n      \"custom_network\":{\r\n        \"Aliases\":[\r\n          \"net-alias\"\r\n        ]\r\n      }\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/test/resources/docker/containerHostConfigAll.json",
    "content": "{\r\n  \"ExtraHosts\":[\r\n    \"localhost:127.0.0.1\"\r\n  ],\r\n  \"Links\":[\r\n    \"db1:db1\",\r\n    \"db2:db2\"\r\n  ],\r\n  \"PortBindings\":{\r\n    \"22/tcp\":[\r\n      {\r\n        \"HostPort\":\"11022\",\r\n        \"HostIp\":\"0.0.0.0\"\r\n      }\r\n    ]\r\n  },\r\n  \"Privileged\":true,\r\n  \"ShmSize\":1024,\r\n  \"Dns\":[\r\n    \"8.8.8.8\"\r\n  ],\r\n  \"DnsSearch\":[\r\n    \"domain.com\"\r\n  ],\r\n  \"CapAdd\":[\r\n    \"NET_ADMIN\"\r\n  ],\r\n  \"CapDrop\":[\r\n    \"MKNOD\"\r\n  ],\r\n  \"SecurityOpt\":[\r\n    \"seccomp=unconfined\"\r\n  ],\r\n  \"Memory\":1,\r\n  \"MemorySwap\":1,\r\n  \"RestartPolicy\":{\r\n    \"Name\":\"on-failure\",\r\n    \"MaximumRetryCount\":1\r\n  },\r\n  \"Ulimits\":[\r\n    {\r\n      \"Name\":\"memlock\",\r\n      \"Hard\":1024,\r\n      \"Soft\":2048\r\n    }\r\n  ],\r\n  \"CpuShares\":1,\r\n  \"NanoCpus\":1000000000,\r\n  \"CpusetCpus\":\"0,1\",\r\n  \"ReadonlyRootfs\":false,    \r\n  \"AutoRemove\":false,    \r\n  \"Binds\":[\r\n    \"/host_tmp:/container_tmp\"\r\n  ],\r\n  \"VolumesFrom\":[\r\n    \"parentContainer\",\r\n    \"otherContainer\"\r\n  ],\r\n  \"NetworkMode\":\"custom_network\"\r\n}\r\n"
  },
  {
    "path": "src/test/resources/interpolate/at/Dockerfile_1",
    "content": "FROM @base@\nLABEL name ${name} @age@ ${ext} blub @unknown@ @project.artifactId@ @\nENV @name@ @ext@\n"
  },
  {
    "path": "src/test/resources/interpolate/at/Dockerfile_1.expected",
    "content": "FROM java\nLABEL name ${name} 42 ${ext} blub @unknown@ docker-maven-plugin @\nENV guenther png\n"
  },
  {
    "path": "src/test/resources/interpolate/none/Dockerfile_1",
    "content": "FROM @base@\nLABEL name ${name} @age@ ${ext} blub\nENV @name@ @age@\n"
  },
  {
    "path": "src/test/resources/interpolate/none/Dockerfile_1.expected",
    "content": "FROM @base@\nLABEL name ${name} @age@ ${ext} blub\nENV @name@ @age@\n"
  },
  {
    "path": "src/test/resources/interpolate/var/Dockerfile_1",
    "content": "FROM ${base}\nLABEL name ${name} @age@ ${ext} ${unknown} ${project.artifactId}\nENV ${name} ${age}\nENV cli=${cliOverride}\nUSER ${user.name}"
  },
  {
    "path": "src/test/resources/interpolate/var/Dockerfile_1.expected",
    "content": "FROM java\nLABEL name guenther @age@ png ${unknown} docker-maven-plugin\nENV guenther 42\nENV cli=cliValue\nUSER somebody"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/certpath/ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIID7TCCAtWgAwIBAgIJAL2CQAue4q4FMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV\nBAYTAkNSMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQxEDAOBgNVBAMTB2NhLXRlc3QwHhcNMTcwMzA5MTQwNzUw\nWhcNMTcwNDA4MTQwNzUwWjBXMQswCQYDVQQGEwJDUjETMBEGA1UECBMKU29tZS1T\ndGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQD\nEwdjYS10ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2wDXfSG6\nHvtguxTuUvbJSKAtH6E/rINjmtJqrtWVBXOjzuQxQuXiiqRnpvr+l4SgpG2RZ1WI\nl2zKAmcBThXqoF04v3NGX8j2M+532+tcRsG0I5lAcCrCih0jzUPAJfSxXpyYYLdh\nE9f9YPQURD1V9ReeqGj/LI9Dyk8T9GSFP3x2k3kk2V4Py0GIIoumGIYPhS98FcqC\ng1pPPjuVnSNJLMMDwkp82VI3zNb90NDIbNALWgoowAxn3KlTsUH/mHXyBHk3AZe+\nHszKShhBg+mxPBgQaF3QLHqPt8fR5nY0rS7bxWKTkCPSptJo1snEw61L9aqkYYih\nfp9UtY1H74HDhQIDAQABo4G7MIG4MB0GA1UdDgQWBBRVn/zXNPQ59p1OB3m+va0T\nR+aSnTCBiAYDVR0jBIGAMH6AFFWf/Nc09Dn2nU4Heb69rRNH5pKdoVukWTBXMQsw\nCQYDVQQGEwJDUjETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJu\nZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDEwdjYS10ZXN0ggkAvYJAC57irgUw\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAmhDR/NgPif0m48rtCZiN\nSNHA5a9GkjXUuWnqVGIEpcYa6VR0mkUG7Tu6gtGY/Ev+Jcjw/8+pMNXMrdLt/Bmz\nuz4ZiWKKcB42PYyqCZzut/MtQcs5pBm8cYyQzPFwngb3AJ+/aD942TK+kxZwHSM2\nDGJ9j5W1vr7agJemileSKkgSm+iHdg7Wf5fg9PQpRnZcJYB1rM77erFg24yX/I4p\nGE2kRNB69tVEQfxi/Pb8bX2SALC9J/UyvDSZhTZDZVK9jX7lXSmam+uIeY7yVPsC\nQruOo8I5Bhcgrku/WBViNWdHCOvFk1uGhdFElNun6n8/4yshnV4GCzwgYTlBZoHr\ndQ==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIID9DCCAtygAwIBAgIJAIjDwFs6JTALMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV\nBAYTAkNSMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWNhLXRlc3QtMjAeFw0xNzAzMDkxNDMw\nMjlaFw0xNzA0MDgxNDMwMjlaMFkxCzAJBgNVBAYTAkNSMRMwEQYDVQQIEwpTb21l\nLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV\nBAMTCWNhLXRlc3QtMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPbi\nnFVvaRRrDoD5tj/nv8i6T3xptwPLTP83gdFSuuUCuNybaU1q+RWMXpX/Bu1m9y/Q\nG1VJOSVZRna84EOr2dFazHxo/YbGFmnAnPzKyEuJ15mQWGaN0hlB3QH2v6mYDIVN\nIUWCa3gP+7cBeFRp+ow0HOvFYHWQ0rCaAop/Nr100jyl4JzI3vr/Ur9fcV9cBFKz\n0L+mp65psZ9h63KCyxpinHZ/TZ87sqqSLbIJTzhYgqYUWSiDoX3CyQdyNPf7lM+0\n2yoFT/RGSJ0fzY4hv77itQWKTTSMLKWbx6V56OadxO5zQVj3vw/gHstMTvurbWbC\naaTB/4VCfDrQqt6QCe0CAwEAAaOBvjCBuzAdBgNVHQ4EFgQUdFO4RZDq+vcXE1Gu\nB7vLKf9jP+wwgYsGA1UdIwSBgzCBgIAUdFO4RZDq+vcXE1GuB7vLKf9jP+yhXaRb\nMFkxCzAJBgNVBAYTAkNSMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJ\nbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWNhLXRlc3QtMoIJAIjD\nwFs6JTALMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGJ/YQQVQCqZ\nOONpYd6nisL8AtbDXQ5r/j6JWup297YcvzseQOfglGUcD0QZ+rcI4F55RcAvqRM3\nifUXVh6h1OBxV6H6GSqcxanqy6T9G2iMjkz7DMSS9UJrFmGgYCysnB/YlTvlgcR6\nhIbL4mLwLG1PFUrcHiOJkgDMUgveTfGN4AiBqBWTAKCiaCVJRlbAS1hVD1xoJARa\nPsoSJ9XXoe7PoQrLdXcrp0xw8GtCJuW2pDuY2RCprZ2tS+CMD5rs0eL4tGgBc7IY\niqd7oqLXUSXlr9vZAp6FkLlNanz045m5MeTetSo9Fcm+nn3ydDInmxBsAiyOZTI6\nHUvV0zFTq+U=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/certpath/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDAjCCAeqgAwIBAgIJAP9/tvpflc6RMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV\nBAYTAkNSMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQxEDAOBgNVBAMTB2NhLXRlc3QwHhcNMTcwMzA5MTQyMTU2\nWhcNMTcwNDA4MTQyMTU2WjARMQ8wDQYDVQQDEwZjbGllbnQwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQCYJt9YNe8zTA6Bctiemw85snVZqkAVe6Xrc/he\nRRHH89+jxHgS8arLpfffdyMGGYOC6R/4tSS8hCu/3J0S8CZgOfRwnzOWOyt0UbKt\nt6m8RmV7fNnuNVCXdHMZB+W7gK75YDqbYnHHvacDu9o3CA2tguTgWYM2XcDfTu72\nIYhvT3TwMuWPK8oKDs5IKeez/F+dYQ9GA1WYWPIsOVAbclfxWcrTg80NqvzVfttG\nrqExBOXHnCwuiPTg3X41CFwOSkTya0PZ94dq3819Q77FEsXZzrN4fgQA+WX9KTCk\nd5oONI0R870Cz3PjS5/aQm5CLPle/AxwjwQLY3f8rUxrwnI5AgMBAAGjFzAVMBMG\nA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQDILcvBMHz8R8NO\nD3vuHtobRH2bI2PTnxY9immRAoiLB26VdxPGU/aPHKJwj3BvJEC6W4ZX9nNW5Ua7\nWTB+A6TlQ9XN3qL1izK/+C8eA0KDj+UVzq8ZlLzgjz80ZPtzMVBRGFBCWmuTFH0k\n64fH4I+v2tXxSqVbIQ7R/LauaRrRIzMZNwERdQ4RFDhxIRFCCYu3HWw+Wa9OrFxW\nbm9zFwHfBF0jnhV9RcFqLkUtDM+1oN+BKrwFqASQHEm4W4SAec8K7/IaijFRyYLl\nN2dKfmPsBnuhiMiNWbHKUESWoVy4J7rkFlUKxKczlOZYQjQuyI8JlpKu80AxZPlg\nBksOkH+8\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/certpath/key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAmCbfWDXvM0wOgXLYnpsPObJ1WapAFXul63P4XkURx/Pfo8R4\nEvGqy6X333cjBhmDgukf+LUkvIQrv9ydEvAmYDn0cJ8zljsrdFGyrbepvEZle3zZ\n7jVQl3RzGQflu4Cu+WA6m2Jxx72nA7vaNwgNrYLk4FmDNl3A307u9iGIb0908DLl\njyvKCg7OSCnns/xfnWEPRgNVmFjyLDlQG3JX8VnK04PNDar81X7bRq6hMQTlx5ws\nLoj04N1+NQhcDkpE8mtD2feHat/NfUO+xRLF2c6zeH4EAPll/SkwpHeaDjSNEfO9\nAs9z40uf2kJuQiz5XvwMcI8EC2N3/K1Ma8JyOQIDAQABAoIBAQCRCKTnienshPhi\nzp3MelRGFGWmRhJ2J/pd+ZrsCXzMlBw/GH242SKMozUnj4IUQ2fH9v7h7ZIKHRcy\n+VHFBqQeH8BxRmA/OUHPzDy9ORCUBqqfzsBSQAIFjRxV5OBJHn4r+l2DVpDM7Gxy\ns7Nrt8KJ/fa8ILdhsHa0pqJEfmx2ZNfpLT0ih6Nbp59HtwucQxvodOJkKIwzv9GC\nQCD/N8UGnYoyfUB4dgxqMyN16mY5/wZlCLJj7e7JRQ08gCAjQ/Ox4anWioO3qEf+\np9EaV2yPhnyX13A21BIphL/Ue0IkEOSYF7ukgjGwRozUk2VPTz38eEgDUHJ41/Pp\nxtHF8zr9AoGBAMkTjcEfV/gdYZNeqHrMw1vRI2a3L+Fu0PfwRHjsHXtlrtUcWjTL\n31/6FF+JlwXN4qptGdtn1jqTXEuL/n9pc3F9ngC/y0uxqt7MIuFyJphgNLXCBnIJ\nLSZu2yVo7rY35TxHBxD+243EPtDfGn61LrDi5XdoMW4kk1WoIsTlTCjzAoGBAMG2\nOgWb+I/wXFu6C+NX9hMt1EHGMFsWASv+pz3fbnsHxC7ktGhAU4D1It6sjskhaAzf\n+jGc98hbk9l2jyCLZiZ310pz+hLsjr1Ect5fUQoo1OoP3eP1KnNrrIHcnE6xVImn\nvAbeeEsIxubogF1lbg1cg0Xd8kES6KcgwMEyKgMjAoGBAJoZpD/nd5GtYsSAfPpv\nxyCs7UahCc7pELmTp8ZMnmOdkBm/OtrPjAeQLuDxH47RNO+L4Y8myyXlHYzAY3De\nLh1COj80vebq6JsLM4g35aBNytepNStcIQAdYVIQUzHPMJ27iYza5QSG6+VDtph4\n3qmtmlKixY819LxmuQ7Q9EdFAoGAA+8igIipZKr6BTbjFOvtaHhOzkMrKj5pKa6T\nUPNfuAnxVw54B4H+8CiKW8FfaQBPr6tA1o8cjSom94yr3XpXScK3UaDHXGkHgRqJ\nxrBi4fTwVgyacg3BnfR63WxQlFyPnfgVHoKYKNX1zCy+pqbvtD/DvsCCMgYjSXml\n0IanSccCgYEAi4yucdsXNznykCQvXTuRAVS35ZOQqlvomsbihcYDck/wxBNuUJ92\nbpn/IR9ugJngUemERky2XF133/2jO5bXVIc2QAb8aoYCYH8l6J0DvTdyuDyejvGy\nI+CmnrWLC0JviALefRIsappdiwhUJ71R40Hc9YI8Ba0MeKBL5RU7a3E=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/keys/ecdsa.pem",
    "content": "-----BEGIN EC PARAMETERS-----\nMIIBwgIBATBNBgcqhkjOPQEBAkIB////////////////////////////////////\n//////////////////////////////////////////////////8wgZ4EQgH/////\n////////////////////////////////////////////////////////////////\n/////////////////ARBUZU+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8Qnh\nVhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5\nMoSqoNpkugSBhQQAxoWOBrcEBOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUte\nd+/nWSj+HcEnov+o3jNIs8GFakKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY\n9URJV5tEaBevvRcnPmYsl+5ymV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlAC\nQgH///////////////////////////////////////////pRhoeDvy+Wa3/MAUj3\nCaXQO7XJuImcR667b7cekThkCQIBAQ==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMIICnQIBAQRCAc0EGSGuEnQsstuRQ3x/usbMLzswphDOIEJktE5upBTQGu6uXFdg\n0D5Jk/mmHvcqCxwL9nMYCMUxncKnSYy/kqlCoIIBxjCCAcICAQEwTQYHKoZIzj0B\nAQJCAf//////////////////////////////////////////////////////////\n////////////////////////////MIGeBEIB////////////////////////////\n//////////////////////////////////////////////////////////wEQVGV\nPrlhjhyaH5KaIaC2hUDuotpyW5mzFfO4tImRjvEJ4VYZOVHsfpN7FlLAvTuxvwc1\nc9+IPSw08e9FH9RrUD8AAxUA0J6IACkcuFOWzGcXOTKEqqDaZLoEgYUEAMaFjga3\nBATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFLXnfv51ko/h3BJ6L/qN4zSLPB\nhWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZmPVESVebRGgXr70XJz5mLJfu\ncple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQAkIB////////////////////\n///////////////////////6UYaHg78vlmt/zAFI9wml0Du1ybiJnEeuu2+3HpE4\nZAkCAQGhgYkDgYYABAGuVN9cJD6kR0Y4XNiz16Tk672Bn65oc4jEPQHKLDg9LKm6\ng0CYf3/wzdlz3IDXcuWDyPVAXm7ouyXvyvFFTJktQQHosiLYwGSb4F6qt9jWheP7\nW0/hyE87evoe8qKg10G0T0sJtbMxdHxhYm+I5Q3kE9UDFugJk5nN3XpC4ewKFK/E\nxw==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/keys/invalid.pem",
    "content": "some text"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/keys/pkcs1.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAmCbfWDXvM0wOgXLYnpsPObJ1WapAFXul63P4XkURx/Pfo8R4\nEvGqy6X333cjBhmDgukf+LUkvIQrv9ydEvAmYDn0cJ8zljsrdFGyrbepvEZle3zZ\n7jVQl3RzGQflu4Cu+WA6m2Jxx72nA7vaNwgNrYLk4FmDNl3A307u9iGIb0908DLl\njyvKCg7OSCnns/xfnWEPRgNVmFjyLDlQG3JX8VnK04PNDar81X7bRq6hMQTlx5ws\nLoj04N1+NQhcDkpE8mtD2feHat/NfUO+xRLF2c6zeH4EAPll/SkwpHeaDjSNEfO9\nAs9z40uf2kJuQiz5XvwMcI8EC2N3/K1Ma8JyOQIDAQABAoIBAQCRCKTnienshPhi\nzp3MelRGFGWmRhJ2J/pd+ZrsCXzMlBw/GH242SKMozUnj4IUQ2fH9v7h7ZIKHRcy\n+VHFBqQeH8BxRmA/OUHPzDy9ORCUBqqfzsBSQAIFjRxV5OBJHn4r+l2DVpDM7Gxy\ns7Nrt8KJ/fa8ILdhsHa0pqJEfmx2ZNfpLT0ih6Nbp59HtwucQxvodOJkKIwzv9GC\nQCD/N8UGnYoyfUB4dgxqMyN16mY5/wZlCLJj7e7JRQ08gCAjQ/Ox4anWioO3qEf+\np9EaV2yPhnyX13A21BIphL/Ue0IkEOSYF7ukgjGwRozUk2VPTz38eEgDUHJ41/Pp\nxtHF8zr9AoGBAMkTjcEfV/gdYZNeqHrMw1vRI2a3L+Fu0PfwRHjsHXtlrtUcWjTL\n31/6FF+JlwXN4qptGdtn1jqTXEuL/n9pc3F9ngC/y0uxqt7MIuFyJphgNLXCBnIJ\nLSZu2yVo7rY35TxHBxD+243EPtDfGn61LrDi5XdoMW4kk1WoIsTlTCjzAoGBAMG2\nOgWb+I/wXFu6C+NX9hMt1EHGMFsWASv+pz3fbnsHxC7ktGhAU4D1It6sjskhaAzf\n+jGc98hbk9l2jyCLZiZ310pz+hLsjr1Ect5fUQoo1OoP3eP1KnNrrIHcnE6xVImn\nvAbeeEsIxubogF1lbg1cg0Xd8kES6KcgwMEyKgMjAoGBAJoZpD/nd5GtYsSAfPpv\nxyCs7UahCc7pELmTp8ZMnmOdkBm/OtrPjAeQLuDxH47RNO+L4Y8myyXlHYzAY3De\nLh1COj80vebq6JsLM4g35aBNytepNStcIQAdYVIQUzHPMJ27iYza5QSG6+VDtph4\n3qmtmlKixY819LxmuQ7Q9EdFAoGAA+8igIipZKr6BTbjFOvtaHhOzkMrKj5pKa6T\nUPNfuAnxVw54B4H+8CiKW8FfaQBPr6tA1o8cjSom94yr3XpXScK3UaDHXGkHgRqJ\nxrBi4fTwVgyacg3BnfR63WxQlFyPnfgVHoKYKNX1zCy+pqbvtD/DvsCCMgYjSXml\n0IanSccCgYEAi4yucdsXNznykCQvXTuRAVS35ZOQqlvomsbihcYDck/wxBNuUJ92\nbpn/IR9ugJngUemERky2XF133/2jO5bXVIc2QAb8aoYCYH8l6J0DvTdyuDyejvGy\nI+CmnrWLC0JviALefRIsappdiwhUJ71R40Hc9YI8Ba0MeKBL5RU7a3E=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/keys/pkcs8.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCYJt9YNe8zTA6B\nctiemw85snVZqkAVe6Xrc/heRRHH89+jxHgS8arLpfffdyMGGYOC6R/4tSS8hCu/\n3J0S8CZgOfRwnzOWOyt0UbKtt6m8RmV7fNnuNVCXdHMZB+W7gK75YDqbYnHHvacD\nu9o3CA2tguTgWYM2XcDfTu72IYhvT3TwMuWPK8oKDs5IKeez/F+dYQ9GA1WYWPIs\nOVAbclfxWcrTg80NqvzVfttGrqExBOXHnCwuiPTg3X41CFwOSkTya0PZ94dq3819\nQ77FEsXZzrN4fgQA+WX9KTCkd5oONI0R870Cz3PjS5/aQm5CLPle/AxwjwQLY3f8\nrUxrwnI5AgMBAAECggEBAJEIpOeJ6eyE+GLOncx6VEYUZaZGEnYn+l35muwJfMyU\nHD8YfbjZIoyjNSePghRDZ8f2/uHtkgodFzL5UcUGpB4fwHFGYD85Qc/MPL05EJQG\nqp/OwFJAAgWNHFXk4Ekefiv6XYNWkMzsbHKzs2u3won99rwgt2GwdrSmokR+bHZk\n1+ktPSKHo1unn0e3C5xDG+h04mQojDO/0YJAIP83xQadijJ9QHh2DGozI3XqZjn/\nBmUIsmPt7slFDTyAICND87HhqdaKg7eoR/6n0RpXbI+GfJfXcDbUEimEv9R7QiQQ\n5JgXu6SCMbBGjNSTZU9PPfx4SANQcnjX8+nG0cXzOv0CgYEAyRONwR9X+B1hk16o\neszDW9EjZrcv4W7Q9/BEeOwde2Wu1RxaNMvfX/oUX4mXBc3iqm0Z22fWOpNcS4v+\nf2lzcX2eAL/LS7Gq3swi4XImmGA0tcIGcgktJm7bJWjutjflPEcHEP7bjcQ+0N8a\nfrUusOLld2gxbiSTVagixOVMKPMCgYEAwbY6BZv4j/BcW7oL41f2Ey3UQcYwWxYB\nK/6nPd9uewfELuS0aEBTgPUi3qyOySFoDN/6MZz3yFuT2XaPIItmJnfXSnP6EuyO\nvURy3l9RCijU6g/d4/Uqc2usgdycTrFUiae8Bt54SwjG5uiAXWVuDVyDRd3yQRLo\npyDAwTIqAyMCgYEAmhmkP+d3ka1ixIB8+m/HIKztRqEJzukQuZOnxkyeY52QGb86\n2s+MB5Au4PEfjtE074vhjybLJeUdjMBjcN4uHUI6PzS95uromwsziDfloE3K16k1\nK1whAB1hUhBTMc8wnbuJjNrlBIbr5UO2mHjeqa2aUqLFjzX0vGa5DtD0R0UCgYAD\n7yKAiKlkqvoFNuMU6+1oeE7OQysqPmkprpNQ81+4CfFXDngHgf7wKIpbwV9pAE+v\nq0DWjxyNKib3jKvdeldJwrdRoMdcaQeBGonGsGLh9PBWDJpyDcGd9HrdbFCUXI+d\n+BUegpgo1fXMLL6mpu+0P8O+wIIyBiNJeaXQhqdJxwKBgQCLjK5x2xc3OfKQJC9d\nO5EBVLflk5CqW+iaxuKFxgNyT/DEE25Qn3Zumf8hH26AmeBR6YRGTLZcXXff/aM7\nltdUhzZABvxqhgJgfyXonQO9N3K4PJ6O8bIj4KaetYsLQm+IAt59Eixqml2LCFQn\nvVHjQdz1gjwFrQx4oEvlFTtrcQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/access/test-environment.props",
    "content": "TEST_SERVICE=SECURITY\nEXTERNAL_ENV=TRUE"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/Dockerfile_from_simple",
    "content": "# Dockerfile with a simple FROM clause\n\nFROM fabric8/s2i-java\n\nADD blub\n# And other additional stuff"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage",
    "content": "# Dockerfile with a multi FROM clause\n# see https://docs.docker.com/develop/develop-images/multistage-build/\n\nFROM fabric8/s2i-java\n\nFROM fabric8/s1i-java"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage_named_build_stages",
    "content": "# Dockerfile with multi-stage builds with named stages\nFROM fabric8/s2i-java as BUILD\n\nRUN ls -la /\n\nFROM BUILD as DEVELOPMENT\n\nRUN ls -la /\n\nFROM BUILD AS DEPOYLABLE\n\nRUN ls -la /\n"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage_named_redundant_build_stages",
    "content": "FROM centos AS first\n\nRUN ls -la /\n\nFROM centos as second\n\nRUN ls -la /\n\nFROM centos"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/Dockerfile_multi_stage_with_args",
    "content": "ARG VERSION:latest\nFROM fabric8/s2i-java:$VERSION AS BUILD\nARG FULL_IMAGE=busybox:latest\nFROM $FULL_IMAGE AS DEPLOYABLE"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/openshift_nologin_config.yaml",
    "content": "apiVersion: v1\nclusters:\n- cluster:\n    insecure-skip-tls-verify: true\n    server: https://172.28.128.4:8443\n  name: 172-28-128-4:8443\ncontexts:\n- context:\n    cluster: 172-28-128-4:8443\n    namespace: default\n    user: admin/172-28-128-4:8443\n  name: default/172-28-128-4:8443/admin\ncurrent-context: default/172-28-128-4:8443/admin\nkind: Config\npreferences: {}\nusers:\n- name: admin/172-28-128-4:8443\n  user: {}"
  },
  {
    "path": "src/test/resources/io/fabric8/maven/docker/util/openshift_simple_config.yaml",
    "content": "apiVersion: v1\nclusters:\n- cluster:\n    insecure-skip-tls-verify: true\n    server: https://172.28.128.4:8443\n  name: 172-28-128-4:8443\ncontexts:\n- context:\n    cluster: 172-28-128-4:8443\n    namespace: default\n    user: admin/172-28-128-4:8443\n  name: default/172-28-128-4:8443/admin\ncurrent-context: default/172-28-128-4:8443/admin\nkind: Config\npreferences: {}\nusers:\n- name: admin/172-28-128-4:8443\n  user:\n    token: token123"
  }
]