diff --git a/README.md b/README.md index 827638a..21376c7 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This application comprises a number of different processes: - a **kafka** topic - A **top-pick-service** that receives data from Kafka and continuously selects popular products, using... - a **redis** store to tally all the clicks +- A **spark** streaming consumer of the events to calculate metrics like: nr of events per type per 2 minutes ```text + @@ -41,30 +42,39 @@ This application comprises a number of different processes: + ``` -## Prerequisite(s) +## Kubernetes: Ingress Nginx -The following package(s) are required; - - `sbt`. +Our setup uses an [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) with Nginx to define reroutes to the webshop and the +API (aka service) used by the webshop. The following steps +are required: -Install them with your package manager: +For all deployments: ``` -brew update -brew install sbt +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml ``` +Provider specific: + +**For Docker for mac** +``` +kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml +``` + +**For minikube** ``` -apt update -apt install sbt +minikube addons enable ingress ``` -## Running with Docker +For more info, see [here](https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md#installation-guide). + +## Using docker containers The easiest way to get started is with Docker Compose. Make sure you have Docker running locally. You can download a proper version at the [Docker Store][ds]. -We are running 6 containers, and the default size is not large enough. Boost the ram of Docker to at least 4GB, +We are running a couple of containers, and the default size is not large enough. Boost the ram of Docker to at least 4GB, otherwise you will run into startup problems (Exit with code: 137) when running the `docker-compose up` command. We are going to build these containers locally: @@ -73,7 +83,6 @@ We are going to build these containers locally: - shop/docker-shop-service - shop/docker-shop-webapp -We will use these public containers: - redis - docker.elastic.co/elasticsearch/elasticsearch @@ -81,8 +90,22 @@ We will use these public containers: [ds]:https://store.docker.com/ +### Building Project Jars (required) + +Build **jars** from the source-code, before you can wrap them into docker containers for: +- service +- spark-container -### Running with docker compose +```bash +# You can use the utility script, build inside a dockers containers: +bash build-jars.sh + +# Or setup the tools (sbt) yourself and run: +service/gradlew -p service build +cd spark-container/streaming && sbt assembly +``` + +### (Option 1) Running with docker compose When you have the containers up and running you can access the webshop through [localhost:9011](http://localhost:9011/). @@ -90,12 +113,30 @@ through [localhost:9011](http://localhost:9011/). > These ports should be available: 9011, 8080, 8081, 9200, 9300, 8290, 9092, 2181, 6379, 8989 ```bash -service/gradlew -p service build && (cd spark-container/streaming && sbt assembly) && docker-compose up -d --build +# note: make sure the jar's have been build + +docker-compose up -d --build ``` -#### Download new products +### (Option 2) Running with Kubernetes + +```bash +# note: make sure the jar's have been build -Optionally you can download new image-data from flickr. +# Create docker images +docker-compose build + +# Register all services +kubectl apply -f k8s +``` + +### Initial Data + +> Note: Data will be loaded automatically, after all services are started. + +#### Download new products (optional) + +You can download new image-data from flickr. These are stored in `catalog-builder/categories` folder. > Note: you need to fill in your own Flickr Api key, and wait a very long time...! @@ -114,21 +155,4 @@ docker run --rm -it \ --key ${FLICKR_API_KEY}" ``` -#### Loading products -The first time you start the docker composition, you have to load the product catalog, like this: - -```text -docker run -it --rm --volume $PWD:/divolte-shop \ - --workdir /divolte-shop \ - --network host \ - python:3.6 \ - bash -c 'pip install requests && python catalog-builder/put-categories.py \ - data/categories/animals.json \ - data/categories/architecture.json \ - data/categories/cars.json \ - data/categories/cities.json \ - data/categories/flowers.json \ - data/categories/landscape.json \ - data/categories/nautical.json' -``` Go to [localhost:9011](http://localhost:9011/). diff --git a/build-jars.sh b/build-jars.sh new file mode 100755 index 0000000..7c4170d --- /dev/null +++ b/build-jars.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Utility script to build the project code into jars using public containers +# instead of installing tools locally like SBT, gradle. +# +# Just requires docker. + +set -e # exit on error +set -u # Need to bind specified variables + + +# Create local folders for java dependecies (jars) +mkdir -p ~/.ivy2 && mkdir -p ~/.m2 && mkdir -p ~/.sbt + +echo '[1/2] Build "spark-streaming" fat jar using SBT ...' +docker run --rm \ + -v ~/.m2:/root/.m2 \ + -v ~/.ivy2:/root/.ivy2 \ + -v ~/.sbt:/root/.sbt \ + -v "${PWD}/spark-container/streaming":/app \ + -w /app \ + mozilla/sbt sbt assembly + +echo '[2/2] Build "service" fat jar using Gradle...' +docker run --rm \ + -u gradle \ + -v ~/.m2:/root/.m2 \ + -v ~/.ivy2:/root/.ivy2 \ + -v "${PWD}/service":/home/gradle/project \ + -w /home/gradle/project \ + gradle:4.6.0-jdk8-alpine gradle build + +echo '' +echo 'Done building the jars for the projects' diff --git a/catalog-builder/Dockerfile b/catalog-builder/Dockerfile new file mode 100644 index 0000000..c555497 --- /dev/null +++ b/catalog-builder/Dockerfile @@ -0,0 +1,18 @@ +# Data population image for initial data +# +# +FROM python:3.6 + +# Copy all stuff from this dir into the default working directory +COPY . . + +RUN pip install -r requirements.txt +CMD ["sh", "-c", "python catalog-builder/put-categories.py --api-base-url=$SHOP_API_URL \ + categories/animals.json \ + categories/architecture.json \ + categories/cars.json \ + categories/cities.json \ + categories/flowers.json \ + categories/landscape.json \ + categories/nautical.json" \ +] diff --git a/data/categories/animals.json b/catalog-builder/categories/animals.json similarity index 100% rename from data/categories/animals.json rename to catalog-builder/categories/animals.json diff --git a/data/categories/architecture.json b/catalog-builder/categories/architecture.json similarity index 100% rename from data/categories/architecture.json rename to catalog-builder/categories/architecture.json diff --git a/data/categories/cars.json b/catalog-builder/categories/cars.json similarity index 100% rename from data/categories/cars.json rename to catalog-builder/categories/cars.json diff --git a/data/categories/cities.json b/catalog-builder/categories/cities.json similarity index 100% rename from data/categories/cities.json rename to catalog-builder/categories/cities.json diff --git a/data/categories/flowers.json b/catalog-builder/categories/flowers.json similarity index 100% rename from data/categories/flowers.json rename to catalog-builder/categories/flowers.json diff --git a/data/categories/landscape.json b/catalog-builder/categories/landscape.json similarity index 100% rename from data/categories/landscape.json rename to catalog-builder/categories/landscape.json diff --git a/data/categories/nautical.json b/catalog-builder/categories/nautical.json similarity index 100% rename from data/categories/nautical.json rename to catalog-builder/categories/nautical.json diff --git a/catalog-builder/download-category.py b/catalog-builder/download-category.py index c9c8234..9d22a2e 100644 --- a/catalog-builder/download-category.py +++ b/catalog-builder/download-category.py @@ -147,7 +147,9 @@ def parse_args(): parser = argparse.ArgumentParser( description='Create a JSON description of a set of images retrieved from the Flickr API based on keyword searches.') parser.add_argument('--key', '-k', metavar='API_KEY', type=str, required=True, help='The Flickr API key to use.') - parser.add_argument('--dest', '-d', metavar='DEST', type=str, help='The output directory to store the jsons', default="data/categories") + parser.add_argument('--dest', '-d', metavar='DEST', type=str, help='The output directory to ' + 'store the jsons', + default="catalog-builder/categories") parser.add_argument('--searches', '-s', metavar='SEARCH_KEYWORD', type=str, required=True, default='categories.yml', help='Yaml file containing the categories and keywords to search for.') parser.add_argument('--fav-threshold', '-t', metavar='FAVOURITES_THRESHOLD', type=int, default=30, diff --git a/catalog-builder/put-categories.py b/catalog-builder/put-categories.py index e76b008..8ace401 100644 --- a/catalog-builder/put-categories.py +++ b/catalog-builder/put-categories.py @@ -1,11 +1,13 @@ import argparse -import requests import json import os - from functools import reduce from collections import Counter +import requests +from requests.exceptions import ConnectionError +from retrying import retry + def make_item(json, filename): return { @@ -27,10 +29,11 @@ def make_item(json, filename): 'flickr_url': size['url'], 'img_source': size['source'] } - for name,size in json['sizes'].items() + for name, size in json['sizes'].items() } } + def merge_items(*input): items = list(filter(lambda x: x, input)) @@ -43,10 +46,12 @@ def merge_items(*input): result = {} result.update(items[0]) for item in items[1:]: - result['categories'] = list(set(result['categories'] + item['categories'])) + result['categories'] = list( + set(result['categories'] + item['categories'])) return result + def with_price(item, min_favs, max_favs): r = (max_favs - min_favs + 1) / 2.0 result = { @@ -56,19 +61,27 @@ def with_price(item, min_favs, max_favs): return result + +@retry(stop_max_attempt_number=10, + wait_fixed=1000, # wait 1 sec + retry_on_exception=lambda e: isinstance(e, ConnectionError)) +def _requests_put_json(url, data): + headers = {'Content-Type': 'application/json'} + return requests.put(url, data=data, headers=headers) + + def main(args): print('Items from:\n\t%s' % '\n\t'.join(args.files)) item_list = reduce( - lambda x,y: x+y, - [[ - make_item(json.loads(line), fn) - for line in open(fn).readlines()] - for fn in args.files]) + lambda x, y: x+y, + [[make_item(json.loads(line), fn) for line in open(fn).readlines()] + for fn in args.files]) min_favs = min([item['favs'] for item in item_list]) max_favs = max([item['favs'] for item in item_list]) - priced_item_list = [with_price(item, min_favs, max_favs) for item in item_list] + priced_item_list = [ + with_price(item, min_favs, max_favs) for item in item_list] items = {} for item in priced_item_list: @@ -76,7 +89,10 @@ def main(args): responses = Counter() for item in items.values(): - r = requests.put(args.api_base_url + '/catalog/item', data=json.dumps(item), headers={'Content-Type': 'application/json'}) + r = _requests_put_json( + args.api_base_url + '/catalog/item', + data=json.dumps(item) + ) print('PUT %s, %d' % (item['id'], r.status_code)) if r.status_code != 200: print(json.dumps(item)) @@ -84,15 +100,29 @@ def main(args): print('\nSummary:') for status, count in responses.items(): - print('\t%d times status code %d' % (count,status)) + print('\t%d times status code %d' % (count, status)) + def parse_args(): - parser = argparse.ArgumentParser(description='Take downloaded category JSON files and push them into the Catalog API.') - parser.add_argument('--api-base-url', '-u', metavar='API_BASE_URL', type=str, default='http://localhost:8080/api', help='The base URL of the Catalog API.') + parser = argparse.ArgumentParser( + description=( + 'Take downloaded category JSON files and push them into the ' + 'Catalog API.' + ) + ) + parser.add_argument( + '--api-base-url', + '-u', + metavar='API_BASE_URL', + type=str, + default='http://localhost:8080/api', + help='The base URL of the Catalog API.' + ) parser.add_argument('files', metavar='JSON_FILE', type=str, nargs='+') return parser.parse_args() + if __name__ == '__main__': args = parse_args() main(args) diff --git a/catalog-builder/requirements.txt b/catalog-builder/requirements.txt index c2e8e98..5c4bb7d 100644 --- a/catalog-builder/requirements.txt +++ b/catalog-builder/requirements.txt @@ -1,3 +1,4 @@ requests xmltodict pyyaml +retrying diff --git a/docker-compose.yml b/docker-compose.yml index 195c544..90403b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -194,6 +194,17 @@ services: - kafka:kafka networks: - divolte.io + + catalog-builder: + build: + context: ./catalog-builder + container_name: catalog-builder + environment: + SHOP_API_URL: "http://shop-api:8080/api" + depends_on: + - shop-api + + # divolte-druid: # # image: fokkodriesprong/docker-druid # image: shop/divolte-druid diff --git a/install_without_docker.md b/install_without_docker.md index 04ac0e3..78c24b8 100644 --- a/install_without_docker.md +++ b/install_without_docker.md @@ -56,13 +56,13 @@ To load the data from the data/catalog directory into elasticsearch the `catalog ``` python put-catagories.py \ - ../data/categories/animals.json \ - ../data/categories/architecture.json \ - ../data/categories/cars.json \ - ../data/categories/cities.json \ - ../data/categories/flowers.json \ - ../data/categories/landscape.json \ - ../data/categories/nautical.json + categories/animals.json \ + categories/architecture.json \ + categories/cars.json \ + categories/cities.json \ + categories/flowers.json \ + categories/landscape.json \ + categories/nautical.json ``` ## Running the webapp @@ -89,4 +89,4 @@ python consumer.py --schema ../divolte/schema/src/main/resources/ShopEventRecord --client toppick-client --group toppick-group python bandit_service.py -``` \ No newline at end of file +``` diff --git a/k8s/clickstream-analyzer-deployment.yaml b/k8s/clickstream-analyzer-deployment.yaml new file mode 100644 index 0000000..ac29023 --- /dev/null +++ b/k8s/clickstream-analyzer-deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clickstream-analyzer + labels: + app: clickstream-analyzer +spec: + replicas: 1 + selector: + matchLabels: + app: clickstream-analyzer + template: + metadata: + labels: + app: clickstream-analyzer + spec: + containers: + - env: + - name: KAFKA_BROKERS + value: kafka:9092 + - name: KAFKA_TOPIC + value: divolte + - name: SPARK_JOB_CLASS + value: sparkjob.SparkEventsPerMinute + image: shop_clickstream-analyzer + name: clickstream-analyzer + imagePullPolicy: Never # always local + ports: + - containerPort: 7077 + - containerPort: 4040 + - containerPort: 18080 + hostname: clickstream-analyzer + restartPolicy: Always diff --git a/k8s/clickstream-analyzer-service.yaml b/k8s/clickstream-analyzer-service.yaml new file mode 100644 index 0000000..0e0d98f --- /dev/null +++ b/k8s/clickstream-analyzer-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: clickstream-analyzer + labels: + app: clickstream-analyzer +spec: + ports: + - name: "7077" + port: 7077 + targetPort: 7077 + - name: "4040" + port: 4040 + targetPort: 4040 + - name: "18080" + port: 18080 + targetPort: 18080 + selector: + app: clickstream-analyzer diff --git a/k8s/clickstream-collector-deployment.yaml b/k8s/clickstream-collector-deployment.yaml new file mode 100644 index 0000000..10cc96b --- /dev/null +++ b/k8s/clickstream-collector-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clickstream-collector + labels: + app: clickstream-collector +spec: + replicas: 1 + selector: + matchLabels: + app: clickstream-collector + template: + metadata: + labels: + app: clickstream-collector + spec: + containers: + - env: + - name: DIVOLTE_CONF_DIR + value: /etc/shop/divolte + - name: DIVOLTE_KAFKA_BROKER_LIST + value: kafka:9092 + image: shop_clickstream-collector + name: clickstream-collector + imagePullPolicy: Never + ports: + - containerPort: 8290 + hostname: clickstream-collector + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/clickstream-collector-service.yaml b/k8s/clickstream-collector-service.yaml new file mode 100644 index 0000000..3131154 --- /dev/null +++ b/k8s/clickstream-collector-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: clickstream-collector + labels: + app: clickstream-collector +spec: + ports: + - name: "8290" + port: 8290 + targetPort: 8290 + selector: + app: clickstream-collector diff --git a/k8s/elastic-deployment.yaml b/k8s/elastic-deployment.yaml new file mode 100644 index 0000000..1c221e9 --- /dev/null +++ b/k8s/elastic-deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: elastic + labels: + app: elastic +spec: + replicas: 1 + selector: + matchLabels: + app: elastic + template: + metadata: + labels: + app: elastic + spec: + containers: + - env: + - name: ES_JAVA_OPTS + value: -Xms512m -Xmx512m + - name: cluster.name + value: elasticsearch + - name: discovery.type + value: single-node + - name: network.bind_host + value: 0.0.0.0 + - name: network.publish_host + value: elastic + - name: xpack.security.enabled + value: "false" + image: docker.elastic.co/elasticsearch/elasticsearch:6.4.0 + name: elastic + ports: + - containerPort: 9200 + - containerPort: 9300 + hostname: elastic + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/elastic-service.yaml b/k8s/elastic-service.yaml new file mode 100644 index 0000000..a1edd21 --- /dev/null +++ b/k8s/elastic-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: elastic + labels: + app: elastic +spec: + ports: + - name: "9200" + port: 9200 + targetPort: 9200 + - name: "9300" + port: 9300 + targetPort: 9300 + selector: + app: elastic diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..b6482d9 --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,18 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: nginx-ingress + annotations: + ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - http: + paths: + - path: / + backend: + serviceName: shop-webapp + servicePort: 9011 + - path: /api + backend: + serviceName: shop-api + servicePort: 8080 diff --git a/k8s/kafka-deployment.yaml b/k8s/kafka-deployment.yaml new file mode 100644 index 0000000..3565e39 --- /dev/null +++ b/k8s/kafka-deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka + labels: + app: kafka +spec: + replicas: 1 + selector: + matchLabels: + app: kafka + template: + metadata: + labels: + app: kafka + spec: + containers: + - env: + - name: ADVERTISED_HOST + value: kafka + - name: ADVERTISED_LISTENERS + value: PLAINTEXT://kafka:9092,INTERNAL://localhost:9093 + - name: AUTO_CREATE_TOPICS + value: "false" + - name: INTER_BROKER + value: INTERNAL + - name: KAFKA_CREATE_TOPICS + value: divolte:4:1 + - name: LISTENERS + value: PLAINTEXT://0.0.0.0:9092,INTERNAL://0.0.0.0:9093 + - name: LOG_RETENTION_HOURS + value: "1" + - name: SECURITY_PROTOCOL_MAP + value: PLAINTEXT:PLAINTEXT,INTERNAL:PLAINTEXT + image: krisgeus/docker-kafka + name: kafka + ports: + - containerPort: 9092 + - containerPort: 2181 + hostname: kafka + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/kafka-service.yaml b/k8s/kafka-service.yaml new file mode 100644 index 0000000..7228eaa --- /dev/null +++ b/k8s/kafka-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: kafka + labels: + app: kafka +spec: + ports: + - name: "9092" + port: 9092 + targetPort: 9092 + - name: "2182" + port: 2181 + targetPort: 2181 + selector: + app: kafka diff --git a/k8s/kibana-deployment.yaml b/k8s/kibana-deployment.yaml new file mode 100644 index 0000000..434b239 --- /dev/null +++ b/k8s/kibana-deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kibana + labels: + app: kibana +spec: + replicas: 1 + selector: + matchLabels: + app: kibana + template: + metadata: + labels: + app: kibana + spec: + containers: + - env: + - name: ELASTICSEARCH_URL + value: http://elastic:9200 + - name: LOGGING_QUIET + value: "true" + - name: SERVER_NAME + value: kibana.divolte-divolte + image: docker.elastic.co/kibana/kibana:6.4.0 + name: kibana + ports: + - containerPort: 5601 + hostname: kibana + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/kibana-service.yaml b/k8s/kibana-service.yaml new file mode 100644 index 0000000..68c1430 --- /dev/null +++ b/k8s/kibana-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: kibana + labels: + app: kibana +spec: + ports: + - name: "5601" + port: 5601 + targetPort: 5601 + selector: + app: kibana diff --git a/k8s/put-categories-deployment.yaml b/k8s/put-categories-deployment.yaml new file mode 100644 index 0000000..26a9d89 --- /dev/null +++ b/k8s/put-categories-deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: put-categories +spec: + template: + spec: + # Cleans job after it has completed only works when + # '--feature-gate="TTLAfterFinished=True"' is set in kubernetes config + ttlSecondsAfterFinished: 100 + containers: + - env: + - name: SHOP_API_URL + value: "http://shop-api:8080/api" + name: put-categories + image: shop_catalog-builder:latest + imagePullPolicy: Never + restartPolicy: Never + initContainers: + - name: wait-for-shop-api-service + image: busybox:1.28 + command: ['sh', '-c', 'until nslookup shop-api; do echo waiting for shop api; sleep 10; done;'] + - name: wait-for-shop-api-ping + image: busybox:1.28 + command: ['sh', '-c', 'until wget -q --spider "http://shop-api:8080/api/status/ping"; do echo waiting for API ping; sleep 10; done;'] + backoffLimit: 4 diff --git a/k8s/shop-api-deployment.yaml b/k8s/shop-api-deployment.yaml new file mode 100644 index 0000000..4d9acfc --- /dev/null +++ b/k8s/shop-api-deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: shop-api + labels: + app: shop-api +spec: + replicas: 1 + selector: + matchLabels: + app: shop-api + template: + metadata: + labels: + app: shop-api + spec: + containers: + - env: + - name: ELASTICSEARCH_CLUSTER_NAME + value: elasticsearch + - name: ELASTICSEARCH_HOSTS + value: '["elastic"]' + - name: JAVA_OPTS + value: -Xms512m -Xmx512m + image: shop_shop-api + name: shop-api + imagePullPolicy: Never + ports: + - containerPort: 8080 + - containerPort: 8081 + livenessProbe: + httpGet: + path: /api/status/ping + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + hostname: shop-api + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/shop-api-service.yaml b/k8s/shop-api-service.yaml new file mode 100644 index 0000000..c3c06ba --- /dev/null +++ b/k8s/shop-api-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: shop-api + labels: + app: shop-api +spec: + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + - name: "8081" + port: 8081 + targetPort: 8081 + selector: + app: shop-api diff --git a/k8s/shop-webapp-deployment.yaml b/k8s/shop-webapp-deployment.yaml new file mode 100644 index 0000000..97a27c5 --- /dev/null +++ b/k8s/shop-webapp-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: shop-webapp + labels: + app: shop-webapp +spec: + replicas: 1 + selector: + matchLabels: + app: shop-webapp + template: + metadata: + labels: + app: shop-webapp + spec: + containers: + - env: + - name: SHOP_API_URL + value: http://shop-api:8080/api + - name: SHOP_BANDIT_URL + value: http://toppick-bandit:8989/item + - name: SHOP_DIVOLTE_URL + value: //localhost:8290/divolte.js + - name: SHOP_WEBAPP_PORT + value: "9011" + - name: SHOP_WEBAPP_SECRET + value: foo + image: shop_shop-webapp + name: shop-webapp + imagePullPolicy: Never + ports: + - containerPort: 9011 + hostname: shop-webapp + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/shop-webapp-service.yaml b/k8s/shop-webapp-service.yaml new file mode 100644 index 0000000..fa591dd --- /dev/null +++ b/k8s/shop-webapp-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: shop-webapp + labels: + app: shop-webapp +spec: + ports: + - name: "9011" + port: 9011 + targetPort: 9011 + selector: + app: shop-webapp diff --git a/k8s/toppick-bandit-deployment.yaml b/k8s/toppick-bandit-deployment.yaml new file mode 100644 index 0000000..4ddb9c1 --- /dev/null +++ b/k8s/toppick-bandit-deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: toppick-bandit + labels: + app: toppick-bandit +spec: + replicas: 1 + selector: + matchLabels: + app: toppick-bandit + template: + metadata: + labels: + app: toppick-bandit + spec: + containers: + - env: + - name: BIND_HOST + value: 0.0.0.0 + - name: BIND_PORT + value: "8989" + - name: REDIS_HOST_PORT + value: toppick-redis:6379 + image: shop_toppick-bandit + name: toppick-bandit + imagePullPolicy: Never + ports: + - containerPort: 8989 + hostname: toppick-bandit + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/toppick-bandit-service.yaml b/k8s/toppick-bandit-service.yaml new file mode 100644 index 0000000..6ff6c6b --- /dev/null +++ b/k8s/toppick-bandit-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: toppick-bandit + labels: + app: toppick-bandit +spec: + ports: + - name: "8989" + port: 8989 + targetPort: 8989 + selector: + app: toppick-bandit diff --git a/k8s/toppick-consumer-deployment.yaml b/k8s/toppick-consumer-deployment.yaml new file mode 100644 index 0000000..e94b64c --- /dev/null +++ b/k8s/toppick-consumer-deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: toppick-consumer + labels: + app: toppick-consumer +spec: + replicas: 1 + selector: + matchLabels: + app: toppick-consumer + template: + metadata: + labels: + app: toppick-consumer + spec: + containers: + - env: + - name: ELASTIC_HOST_PORT + value: elastic:9200 + - name: KAFKA_BROKERS + value: kafka:9092 + - name: KAFKA_TOPIC + value: divolte + - name: REDIS_HOST_PORT + value: toppick-redis:6379 + image: shop_toppick-consumer + name: toppick-consumer + imagePullPolicy: Never + hostname: toppick-consumer + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/toppick-redis-deployment.yaml b/k8s/toppick-redis-deployment.yaml new file mode 100644 index 0000000..797ada2 --- /dev/null +++ b/k8s/toppick-redis-deployment.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: toppick-redis + labels: + app: toppick-redis +spec: + replicas: 1 + selector: + matchLabels: + app: toppick-redis + template: + metadata: + labels: + app: toppick-redis + spec: + containers: + - image: redis:alpine + name: toppick-redis + ports: + - containerPort: 6379 + hostname: toppick-redis + restartPolicy: Always + subdomain: divolte-divolte diff --git a/k8s/toppick-redis-service.yaml b/k8s/toppick-redis-service.yaml new file mode 100644 index 0000000..02c92bd --- /dev/null +++ b/k8s/toppick-redis-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: toppick-redis + labels: + app: toppick-redis +spec: + ports: + - name: "6379" + port: 6379 + targetPort: 6379 + selector: + app: toppick-redis diff --git a/service/src/main/java/io/divolte/shop/Main.java b/service/src/main/java/io/divolte/shop/Main.java index 0896ca5..5291b29 100644 --- a/service/src/main/java/io/divolte/shop/Main.java +++ b/service/src/main/java/io/divolte/shop/Main.java @@ -1,10 +1,6 @@ package io.divolte.shop; -import io.divolte.shop.catalog.BasketResource; -import io.divolte.shop.catalog.CatalogCategoryResource; -import io.divolte.shop.catalog.DataAccess; -import io.divolte.shop.catalog.CatalogItemResource; -import io.divolte.shop.catalog.CheckoutResource; +import io.divolte.shop.catalog.*; import io.dropwizard.Application; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; @@ -44,6 +40,7 @@ public void run(final ServiceConfiguration configuration, final Environment envi environment.jersey().register(new CatalogCategoryResource(client)); environment.jersey().register(new BasketResource(client)); environment.jersey().register(new CheckoutResource()); + environment.jersey().register(new APIStatusResource()); environment.healthChecks().register("ElasticSearch", new ElasticSearchHealthCheck(client)); } diff --git a/service/src/main/java/io/divolte/shop/catalog/APIStatusResource.java b/service/src/main/java/io/divolte/shop/catalog/APIStatusResource.java new file mode 100644 index 0000000..6a41d9a --- /dev/null +++ b/service/src/main/java/io/divolte/shop/catalog/APIStatusResource.java @@ -0,0 +1,31 @@ +package io.divolte.shop.catalog; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + + +@Path("/api/status") +public class APIStatusResource { + + public APIStatusResource() { + + } + + /** + Purpose is to determine whether the api is still alive and running + */ + @Path("/ping") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Response getStatus() { + Response.ResponseBuilder builder = Response.status(Status.ACCEPTED); + + return builder.entity("pong").build(); + + } + +}