From 5a0ebd8a33e0f60840df845e3391813968a98d42 Mon Sep 17 00:00:00 2001 From: Phil Winder Date: Fri, 12 Aug 2016 11:04:27 +0100 Subject: [PATCH 1/2] Add build and test harnesses. Add travis builder. Coveralls builder. --- .gitignore | 116 ++++++++++ .travis.yml | 27 +++ LICENSE | 201 ++++++++++++++++++ README.md | 16 +- docker/queue-master/Dockerfile | 6 + pom.xml | 116 +++++++++- scripts/build.sh | 31 +++ scripts/push.sh | 55 +++++ .../weave/socks/QueueMasterApplication.java | 45 ---- .../{ => queuemaster}/DockerSpawner.java | 15 +- .../queuemaster/QueueMasterApplication.java | 11 + .../ShippingTaskHandler.java | 3 +- .../configuration}/RabbitMqConfiguration.java | 24 ++- .../ShippingConsumerConfiguration.java | 5 +- .../{ => shipping/entities}/Shipment.java | 4 +- test/Dockerfile | 16 ++ test/component.py | 19 ++ test/coveralls.py | 36 ++++ test/test.sh | 34 +++ test/unit.py | 19 ++ test/util/Api.py | 10 + test/util/Docker.py | 39 ++++ test/util/Dredd.py | 25 +++ test/util/__init__.py | 0 24 files changed, 797 insertions(+), 76 deletions(-) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 docker/queue-master/Dockerfile create mode 100755 scripts/build.sh create mode 100755 scripts/push.sh delete mode 100644 src/main/java/works/weave/socks/QueueMasterApplication.java rename src/main/java/works/weave/socks/{ => queuemaster}/DockerSpawner.java (92%) create mode 100644 src/main/java/works/weave/socks/queuemaster/QueueMasterApplication.java rename src/main/java/works/weave/socks/{ => queuemaster}/ShippingTaskHandler.java (79%) rename src/main/java/works/weave/socks/{ => queuemaster/configuration}/RabbitMqConfiguration.java (74%) rename src/main/java/works/weave/socks/{ => queuemaster/configuration}/ShippingConsumerConfiguration.java (92%) rename src/main/java/works/weave/socks/{ => shipping/entities}/Shipment.java (85%) create mode 100644 test/Dockerfile create mode 100644 test/component.py create mode 100644 test/coveralls.py create mode 100755 test/test.sh create mode 100644 test/unit.py create mode 100644 test/util/Api.py create mode 100644 test/util/Docker.py create mode 100644 test/util/Dredd.py create mode 100644 test/util/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c5d8e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +### Go template +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +### Java template +/target/ +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### OSX template +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea + +## File-based project format: +*.iws +*.iml + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +# Created by .ignore support plugin (hsz.mobi) + +# Maven builds +*/target +*/*/target + +# AWS ECS install scripts generates an SSH key file +weave-ecs-demo-key.pem + +# Load test generates pyc files +*.pyc + +# Ignore Vagrant cache files +*.vagrant/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..267cc9b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +language: java +sudo: required +services: + - docker +jdk: + - oraclejdk8 +install: true + +env: + - GROUP=weaveworksdemos COMMIT=$TRAVIS_COMMIT TAG=$TRAVIS_TAG; + +script: + - set -e + - ./scripts/build.sh; + - ./test/test.sh unit.py + - ./test/test.sh component.py +# - ./test/test.sh container.py --tag $TAG + +after_success: + - set -e; + - ./test/test.sh coveralls.py + - if [ -z "$DOCKER_PASS" ] ; then + echo "This is a build triggered by an external PR. Skipping docker push."; + exit 0; + fi; + - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS; + - ./scripts/push.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 88c1c9b..a0e224d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ -[![Build Status](https://travis-ci.org/microservices-demo/queue-master.svg?branch=master)](https://travis-ci.org/microservices-demo/queue-master) +[![Build Status](https://travis-ci.org/microservices-demo/shipping.svg?branch=master)](https://travis-ci.org/microservices-demo/shipping) [![Coverage Status](https://coveralls.io/repos/github/microservices-demo/shipping/badge.svg?branch=master)](https://coveralls.io/github/microservices-demo/shipping?branch=master) +# shipping +A microservices-demo service that provides shipping capabilities. -queue-master ---- +This build is built, tested and released by travis. -A microservices-demo service that processes the orders queue. +# Test +`./test/test.sh < python testing file >`. For example: `./test/test.sh unit.py` + +# Build +`GROUP=weaveworksdemos COMMIT=test ./scripts/build.sh` + +# Push +`GROUP=weaveworksdemos COMMIT=test ./scripts/push.sh` diff --git a/docker/queue-master/Dockerfile b/docker/queue-master/Dockerfile new file mode 100644 index 0000000..d157724 --- /dev/null +++ b/docker/queue-master/Dockerfile @@ -0,0 +1,6 @@ +FROM java:openjdk-8-alpine + +WORKDIR /usr/src/app +COPY *.jar ./app.jar + +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/urandom","-jar","./app.jar", "--port=80"] diff --git a/pom.xml b/pom.xml index 7db63a7..1e27b19 100644 --- a/pom.xml +++ b/pom.xml @@ -4,29 +4,127 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - works.weave.socks + works.weave.microservices-demo queue-master jar queue-master - Shipping queue master for socks eCommerce application - + Queue service for microservices-demo application - works.weave - socks - 0.0.1-SNAPSHOT + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + UTF-8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-data-rest + + + org.springframework.data + spring-data-rest-hal-browser + org.springframework.boot spring-boot-starter-amqp - com.github.docker-java - docker-java - 3.0.0 + com.github.docker-java + docker-java + 3.0.0 + + + org.springframework.boot + spring-boot-starter-test + test + + + com.openpojo + openpojo + 0.8.4 + test + + + queue-master + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + + **/Unit*.java + + + **/IT*.java + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.18.1 + + + **/IT*.java + + + **/Unit*.java + + + + + + integration-test + verify + + + + + + org.jacoco + jacoco-maven-plugin + 0.7.6.201602180812 + + + prepare-agent + + prepare-agent + + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.2.0 + + + + + + + spring-releases + https://repo.spring.io/libs-release + + + + + spring-releases + https://repo.spring.io/libs-release + + diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..8ce9fcd --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -ev + +SCRIPT_DIR=$(dirname "$0") + +if [[ -z "$GROUP" ]] ; then + echo "Cannot find GROUP env var" + exit 1 +fi + +if [[ -z "$COMMIT" ]] ; then + echo "Cannot find COMMIT env var" + exit 1 +fi + +if [[ "$(uname)" == "Darwin" ]]; then + DOCKER_CMD=docker +else + DOCKER_CMD="sudo docker" +fi +CODE_DIR=$(cd $SCRIPT_DIR/..; pwd) +echo $CODE_DIR +$DOCKER_CMD run --rm -v $HOME/.m2:/root/.m2 -v $CODE_DIR:/usr/src/mymaven -w /usr/src/mymaven maven:3.2-jdk-8 mvn -DskipTests package + +cp $CODE_DIR/target/*.jar $CODE_DIR/docker/$(basename $CODE_DIR) + +for m in ./docker/*/; do + REPO=${GROUP}/$(basename $m) + $DOCKER_CMD build -t ${REPO}:${COMMIT} $CODE_DIR/$m; +done; diff --git a/scripts/push.sh b/scripts/push.sh new file mode 100755 index 0000000..e993474 --- /dev/null +++ b/scripts/push.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +set -ev + +if [[ -z "$GROUP" ]] ; then + echo "Cannot find GROUP env var" + exit 1 +fi + +if [[ -z "$COMMIT" ]] ; then + echo "Cannot find COMMIT env var" + exit 1 +fi + +push() { + DOCKER_PUSH=1; + while [ $DOCKER_PUSH -gt 0 ] ; do + echo "Pushing $1"; + docker push $1; + DOCKER_PUSH=$(echo $?); + if [[ "$DOCKER_PUSH" -gt 0 ]] ; then + echo "Docker push failed with exit code $DOCKER_PUSH"; + fi; + done; +} + +tag_and_push_all() { + if [[ -z "$1" ]] ; then + echo "Please pass the tag" + exit 1 + else + TAG=$1 + fi + for m in ./docker/*/; do + REPO=${GROUP}/$(basename $m) + if [[ "$COMMIT" != "$TAG" ]]; then + docker tag ${REPO}:${COMMIT} ${REPO}:${TAG} + fi + push "$REPO:$TAG"; + done; +} + +# Always push commit +tag_and_push_all $COMMIT + +# Push snapshot when in master +if [ "$TRAVIS_BRANCH" == "master" ]; then + tag_and_push_all snapshot +fi; + +# Push tag and latest when tagged +if [ -n "$TRAVIS_TAG" ]; then + tag_and_push_all ${TRAVIS_TAG} + tag_and_push_all latest +fi; diff --git a/src/main/java/works/weave/socks/QueueMasterApplication.java b/src/main/java/works/weave/socks/QueueMasterApplication.java deleted file mode 100644 index 7851c03..0000000 --- a/src/main/java/works/weave/socks/QueueMasterApplication.java +++ /dev/null @@ -1,45 +0,0 @@ -package works.weave.socks; - -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class QueueMasterApplication implements CommandLineRunner { - - final static String queueName = "shipping-task"; - - @Autowired - RabbitTemplate rabbitTemplate; - - public static void main(String[] args) throws InterruptedException { - SpringApplication.run(QueueMasterApplication.class, args); - } - - @Bean - Queue queue() { - return new Queue(queueName, false); - } - - @Bean - TopicExchange exchange() { - return new TopicExchange("shipping-task-exchange"); - } - - @Bean - Binding binding(Queue queue, TopicExchange exchange) { - return BindingBuilder.bind(queue).to(exchange).with(queueName); - } - - @Override - public void run(String... args) throws Exception { - System.out.println("Starting QueueMasterApplication..."); - } -} diff --git a/src/main/java/works/weave/socks/DockerSpawner.java b/src/main/java/works/weave/socks/queuemaster/DockerSpawner.java similarity index 92% rename from src/main/java/works/weave/socks/DockerSpawner.java rename to src/main/java/works/weave/socks/queuemaster/DockerSpawner.java index 94e003d..7e6f480 100644 --- a/src/main/java/works/weave/socks/DockerSpawner.java +++ b/src/main/java/works/weave/socks/queuemaster/DockerSpawner.java @@ -1,22 +1,17 @@ -package works.weave.socks; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package works.weave.socks.queuemaster; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.model.Network; +import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.command.PullImageResultCallback; -import com.github.dockerjava.core.command.ExecStartResultCallback; -import com.github.dockerjava.api.exception.DockerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import java.lang.Exception; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.List; @Component public class DockerSpawner { @@ -69,4 +64,4 @@ public void run() { } }); } -} \ No newline at end of file +} diff --git a/src/main/java/works/weave/socks/queuemaster/QueueMasterApplication.java b/src/main/java/works/weave/socks/queuemaster/QueueMasterApplication.java new file mode 100644 index 0000000..7e9d185 --- /dev/null +++ b/src/main/java/works/weave/socks/queuemaster/QueueMasterApplication.java @@ -0,0 +1,11 @@ +package works.weave.socks.queuemaster; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class QueueMasterApplication { + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(QueueMasterApplication.class, args); + } +} diff --git a/src/main/java/works/weave/socks/ShippingTaskHandler.java b/src/main/java/works/weave/socks/queuemaster/ShippingTaskHandler.java similarity index 79% rename from src/main/java/works/weave/socks/ShippingTaskHandler.java rename to src/main/java/works/weave/socks/queuemaster/ShippingTaskHandler.java index 7232640..950f817 100644 --- a/src/main/java/works/weave/socks/ShippingTaskHandler.java +++ b/src/main/java/works/weave/socks/queuemaster/ShippingTaskHandler.java @@ -1,7 +1,8 @@ -package works.weave.socks; +package works.weave.socks.queuemaster; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import works.weave.socks.shipping.entities.Shipment; @Component public class ShippingTaskHandler { diff --git a/src/main/java/works/weave/socks/RabbitMqConfiguration.java b/src/main/java/works/weave/socks/queuemaster/configuration/RabbitMqConfiguration.java similarity index 74% rename from src/main/java/works/weave/socks/RabbitMqConfiguration.java rename to src/main/java/works/weave/socks/queuemaster/configuration/RabbitMqConfiguration.java index 0c7d60c..51eade1 100644 --- a/src/main/java/works/weave/socks/RabbitMqConfiguration.java +++ b/src/main/java/works/weave/socks/queuemaster/configuration/RabbitMqConfiguration.java @@ -1,6 +1,6 @@ -package works.weave.socks; +package works.weave.socks.queuemaster.configuration; -import org.springframework.amqp.core.AmqpAdmin; +import org.springframework.amqp.core.*; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; @@ -9,10 +9,13 @@ import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import works.weave.socks.shipping.entities.Shipment; @Configuration public class RabbitMqConfiguration { + final static String queueName = "shipping-task"; + @Bean public ConnectionFactory connectionFactory() { @@ -44,4 +47,19 @@ public DefaultClassMapper classMapper() typeMapper.setDefaultType(Shipment.class); return typeMapper; } -} \ No newline at end of file + + @Bean + Queue queue() { + return new Queue(queueName, false); + } + + @Bean + TopicExchange exchange() { + return new TopicExchange("shipping-task-exchange"); + } + + @Bean + Binding binding(Queue queue, TopicExchange exchange) { + return BindingBuilder.bind(queue).to(exchange).with(queueName); + } +} diff --git a/src/main/java/works/weave/socks/ShippingConsumerConfiguration.java b/src/main/java/works/weave/socks/queuemaster/configuration/ShippingConsumerConfiguration.java similarity index 92% rename from src/main/java/works/weave/socks/ShippingConsumerConfiguration.java rename to src/main/java/works/weave/socks/queuemaster/configuration/ShippingConsumerConfiguration.java index 7cf0737..1e51094 100644 --- a/src/main/java/works/weave/socks/ShippingConsumerConfiguration.java +++ b/src/main/java/works/weave/socks/queuemaster/configuration/ShippingConsumerConfiguration.java @@ -1,4 +1,4 @@ -package works.weave.socks; +package works.weave.socks.queuemaster.configuration; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import works.weave.socks.queuemaster.ShippingTaskHandler; @Configuration public class ShippingConsumerConfiguration extends RabbitMqConfiguration @@ -43,4 +44,4 @@ public SimpleMessageListenerContainer listenerContainer() { public MessageListenerAdapter messageListenerAdapter() { return new MessageListenerAdapter(shippingTaskHandler, jsonMessageConverter()); } -} \ No newline at end of file +} diff --git a/src/main/java/works/weave/socks/Shipment.java b/src/main/java/works/weave/socks/shipping/entities/Shipment.java similarity index 85% rename from src/main/java/works/weave/socks/Shipment.java rename to src/main/java/works/weave/socks/shipping/entities/Shipment.java index 9b2f678..f17de9a 100644 --- a/src/main/java/works/weave/socks/Shipment.java +++ b/src/main/java/works/weave/socks/shipping/entities/Shipment.java @@ -1,4 +1,4 @@ -package works.weave.socks; +package works.weave.socks.shipping.entities; public class Shipment { private String id; @@ -19,4 +19,4 @@ public String getName() { public void setName(String name) { this.name = name; } -} \ No newline at end of file +} diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 0000000..8b921b3 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.6-alpine + +RUN apk add --no-cache \ + ca-certificates \ + curl \ + openssl + +ENV DOCKER_BUCKET get.docker.com +ENV DOCKER_VERSION 1.8.3 + +RUN set -x \ + && curl -fSL "https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz" -o docker.tgz \ + && tar -xzvf docker.tgz \ + && docker -v + +RUN pip install requests diff --git a/test/component.py b/test/component.py new file mode 100644 index 0000000..2623007 --- /dev/null +++ b/test/component.py @@ -0,0 +1,19 @@ +import os +import unittest +from os.path import expanduser + +from util.Docker import Docker + + +class JavaServices(unittest.TestCase): + def test_maven(self): + script_dir = os.path.dirname(os.path.realpath(__file__)) + code_dir = script_dir + "/.." + home = expanduser("~") + command = ['docker', 'run', '--rm', '-v', home + '/.m2:/root/.m2', '-v', code_dir + ':/usr/src/mymaven', '-w', + '/usr/src/mymaven', 'maven:3.2-jdk-8', 'mvn', 'integration-test'] + print(Docker().execute(command)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/coveralls.py b/test/coveralls.py new file mode 100644 index 0000000..097b107 --- /dev/null +++ b/test/coveralls.py @@ -0,0 +1,36 @@ +import os +import unittest +from os.path import expanduser + +from util.Docker import Docker + + +class JavaServices(unittest.TestCase): + def test_maven(self): + script_dir = os.path.dirname(os.path.realpath(__file__)) + code_dir = script_dir + "/.." + home = expanduser("~") + command = ['docker', 'run', '--rm', + '-v', home + '/.m2:/root/.m2', + '-v', code_dir + ':/usr/src/mymaven', + '-w', '/usr/src/mymaven', + 'maven:3.2-jdk-8', + 'mvn', + '-DrepoToken=' + os.getenv('COVERALLS_TOKEN'), + '-DserviceJobId=' + os.getenv('TRAVIS_JOB_ID'), + '-Dbranch=' + os.getenv('TRAVIS_BRANCH'), + '-DpullRequest=' + os.getenv('TRAVIS_PULL_REQUEST'), + '-DserviceName=' + os.getenv('TRAVIS'), + 'verify', + 'jacoco:report', + 'coveralls:report'] + print("Coveralls command: ", + '-DserviceJobId=' + os.getenv('TRAVIS_JOB_ID'), + '-Dbranch=' + os.getenv('TRAVIS_BRANCH'), + '-DpullRequest=' + os.getenv('TRAVIS_PULL_REQUEST'), + '-DserviceName=' + 'TRAVIS') + print(Docker().execute(command)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000..f84b64d --- /dev/null +++ b/test/test.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -ev + +SCRIPT_DIR=`dirname "$0"` +SCRIPT_NAME=`basename "$0"` +SSH_OPTS=-oStrictHostKeyChecking=no + +if [[ "$(uname)" == "Darwin" ]]; then + DOCKER_CMD=docker +else + DOCKER_CMD="sudo docker" +fi + +if [[ -z $($DOCKER_CMD images | grep test-container) ]] ; then + echo "Building test container" + docker build -t test-container $SCRIPT_DIR > /dev/null +fi + +echo "Testing $1" +CODE_DIR=$(cd $SCRIPT_DIR/..; pwd) +echo "$@" +$DOCKER_CMD run \ + --rm \ + --name test \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $CODE_DIR:$CODE_DIR -w $CODE_DIR \ + -e COVERALLS_TOKEN=$COVERALLS_TOKEN \ + -e TRAVIS_JOB_ID=$TRAVIS_JOB_ID \ + -e TRAVIS_BRANCH=$TRAVIS_BRANCH \ + -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \ + -e TRAVIS=$TRAVIS \ + test-container \ + sh -c export PYTHONPATH=\$PYTHONPATH:\$PWD/test ; python test/"$@" diff --git a/test/unit.py b/test/unit.py new file mode 100644 index 0000000..0b8acc5 --- /dev/null +++ b/test/unit.py @@ -0,0 +1,19 @@ +import os +import unittest +from os.path import expanduser + +from util.Docker import Docker + + +class JavaServices(unittest.TestCase): + def test_maven(self): + script_dir = os.path.dirname(os.path.realpath(__file__)) + code_dir = script_dir + "/.." + home = expanduser("~") + command = ['docker', 'run', '--rm', '-v', home + '/.m2:/root/.m2', '-v', code_dir + ':/usr/src/mymaven', '-w', + '/usr/src/mymaven', 'maven:3.2-jdk-8', 'mvn', 'test'] + print(Docker().execute(command)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/util/Api.py b/test/util/Api.py new file mode 100644 index 0000000..34953e0 --- /dev/null +++ b/test/util/Api.py @@ -0,0 +1,10 @@ +import requests + + +class Api: + def noResponse(self, url): + try: + r = requests.get(url, timeout=5) + except requests.exceptions.ConnectionError: + return True + return r.status_code > 299 diff --git a/test/util/Docker.py b/test/util/Docker.py new file mode 100644 index 0000000..c739f03 --- /dev/null +++ b/test/util/Docker.py @@ -0,0 +1,39 @@ +import re +from random import random +from subprocess import Popen, PIPE + + +# From http://blog.bordage.pro/avoid-docker-py/ +class Docker: + def kill_and_remove(self, ctr_name): + command = ['docker', 'rm', '-f', ctr_name] + self.execute(command) + + def random_container_name(self, prefix): + retstr = prefix + '-' + for i in range(5): + retstr += chr(int(round(random() * (122 - 97) + 97))) + return retstr + + def get_container_ip(self, ctr_name): + command = ['docker', 'inspect', + '--format', '\'{{.NetworkSettings.IPAddress}}\'', + ctr_name] + return re.sub(r'[^0-9.]*', '', self.execute(command)) + + def execute(self, command): + print("Running: " + ' '.join(command)) + p = Popen(command, stdout=PIPE, stderr=PIPE) + out = p.stdout.read() + stderr = p.stderr.read() + if p.wait() != 0: + p.stdout.close() + p.stderr.close() + raise RuntimeError(str(stderr.decode('utf-8'))) + p.stdout.close() + p.stderr.close() + return str(out.decode('utf-8')) + + def start_container(self, container_name="", image="", cmd="", host=""): + command = ['docker', 'run', '-d', '-h', host, '--name', container_name, image] + self.execute(command) diff --git a/test/util/Dredd.py b/test/util/Dredd.py new file mode 100644 index 0000000..03b0f89 --- /dev/null +++ b/test/util/Dredd.py @@ -0,0 +1,25 @@ +from util.Docker import Docker + + +class Dredd: + image = 'weaveworksdemos/openapi' + container_name = '' + + def test_against_endpoint(self, json_spec, endpoint_container_name, api_endpoint, mongo_endpoint_url, + mongo_container_name): + self.container_name = Docker().random_container_name('openapi') + command = ['docker', 'run', + '-h', 'openapi', + '--name', self.container_name, + '--link', mongo_container_name, + '--link', endpoint_container_name, + '--env', "MONGO_ENDPOINT={0}".format(mongo_endpoint_url), + Dredd.image, + "/usr/src/app/{0}".format(json_spec), + api_endpoint, + "-f", + "/usr/src/app/hooks.js" + ] + out = Docker().execute(command) + Docker().kill_and_remove(self.container_name) + return out diff --git a/test/util/__init__.py b/test/util/__init__.py new file mode 100644 index 0000000..e69de29 From 70ae23e9da11b3d5b63460fd21783686e74191d4 Mon Sep 17 00:00:00 2001 From: Phil Winder Date: Fri, 12 Aug 2016 11:21:35 +0100 Subject: [PATCH 2/2] Add default test so tests and coveralls don't fail. --- .../socks/shipping/entities/Shipment.java | 39 ++++++++++ .../socks/shipping/entities/UnitPojo.java | 77 +++++++++++++++++++ test/coveralls.py | 5 -- test/test.sh | 2 +- 4 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 src/test/java/works/weave/socks/shipping/entities/UnitPojo.java diff --git a/src/main/java/works/weave/socks/shipping/entities/Shipment.java b/src/main/java/works/weave/socks/shipping/entities/Shipment.java index f17de9a..973f380 100644 --- a/src/main/java/works/weave/socks/shipping/entities/Shipment.java +++ b/src/main/java/works/weave/socks/shipping/entities/Shipment.java @@ -1,9 +1,48 @@ package works.weave.socks.shipping.entities; +import java.util.UUID; + public class Shipment { private String id; private String name; + public Shipment() { + this(""); + } + + public Shipment(String name) { + this(UUID.randomUUID().toString(), name); + } + + public Shipment(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return "Shipment{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Shipment shipment = (Shipment) o; + + return getId() != null ? getId().equals(shipment.getId()) : shipment.getId() == null; + + } + + @Override + public int hashCode() { + return getId() != null ? getId().hashCode() : 0; + } + public String getId() { return id; } diff --git a/src/test/java/works/weave/socks/shipping/entities/UnitPojo.java b/src/test/java/works/weave/socks/shipping/entities/UnitPojo.java new file mode 100644 index 0000000..a3c1c7e --- /dev/null +++ b/src/test/java/works/weave/socks/shipping/entities/UnitPojo.java @@ -0,0 +1,77 @@ +package works.weave.socks.shipping.entities; + +import com.openpojo.reflection.PojoClass; +import com.openpojo.reflection.PojoClassFilter; +import com.openpojo.reflection.filters.FilterClassName; +import com.openpojo.reflection.impl.PojoClassFactory; +import com.openpojo.validation.Validator; +import com.openpojo.validation.ValidatorBuilder; +import com.openpojo.validation.affirm.Affirm; +import com.openpojo.validation.rule.impl.*; +import com.openpojo.validation.test.impl.GetterTester; +import com.openpojo.validation.test.impl.SetterTester; +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +public class UnitPojo { + // Configured for expectation, so we know when a class gets added or removed. + private static final int EXPECTED_CLASS_COUNT = 1; + + // The package to test + private static final String POJO_PACKAGE = "works.weave.socks.shipping.entities"; + + private final PojoClassFilter filter = new FilterClassName("^((?!Unit).)*$"); + + @Test + public void ensureExpectedPojoCount() { + List pojoClasses = PojoClassFactory.getPojoClasses(POJO_PACKAGE, filter); + Affirm.affirmEquals("Classes added / removed?", EXPECTED_CLASS_COUNT, pojoClasses.size()); + } + + @Test + public void testPojoStructureAndBehavior() { + Validator validator = ValidatorBuilder.create() + // Add Rules to validate structure for POJO_PACKAGE + // See com.openpojo.validation.rule.impl for more ... + .with(new GetterMustExistRule()) + .with(new SetterMustExistRule()) + // Add Testers to validate behaviour for POJO_PACKAGE + // See com.openpojo.validation.test.impl for more ... + .with(new SetterTester()) + .with(new GetterTester()) + // Static fields must be final + .with(new NoStaticExceptFinalRule()) + // Don't shadow parent's field names. + .with(new NoFieldShadowingRule()) + // What about public fields, use one of the following rules + // allow them only if they are static and final. + .with(new NoPublicFieldsExceptStaticFinalRule()) + .build(); + + validator.validate(POJO_PACKAGE, filter); + } + + @Test + public void testEquals() throws Exception { + assertThat(new Shipment("id", "name"), is(equalTo(new Shipment("id", "name")))); + assertThat(new Shipment("id", "name"), is(equalTo(new Shipment("id", "another")))); + assertThat(new Shipment("id", "name"), is(not(equalTo(new Shipment("another", "name"))))); + } + + @Test + public void testHashcode() throws Exception { + assertThat(new Shipment("id", "name").hashCode(), is(equalTo(new Shipment("id", "name").hashCode()))); + assertThat(new Shipment("id", "name").hashCode(), is(equalTo(new Shipment("id", "another").hashCode()))); + assertThat(new Shipment("id", "name").hashCode(), is(not(equalTo(new Shipment("aa", "name").hashCode())))); + } + + @Test + public void testString() throws Exception { + assertThat(new Shipment("id").toString(), is(notNullValue())); + + } +} diff --git a/test/coveralls.py b/test/coveralls.py index 097b107..2397b92 100644 --- a/test/coveralls.py +++ b/test/coveralls.py @@ -24,11 +24,6 @@ def test_maven(self): 'verify', 'jacoco:report', 'coveralls:report'] - print("Coveralls command: ", - '-DserviceJobId=' + os.getenv('TRAVIS_JOB_ID'), - '-Dbranch=' + os.getenv('TRAVIS_BRANCH'), - '-DpullRequest=' + os.getenv('TRAVIS_PULL_REQUEST'), - '-DserviceName=' + 'TRAVIS') print(Docker().execute(command)) diff --git a/test/test.sh b/test/test.sh index f84b64d..4bfa0d5 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -ev +set -e SCRIPT_DIR=`dirname "$0"` SCRIPT_NAME=`basename "$0"`