merge upstream

This commit is contained in:
Yoshi Yamaguchi 2019-05-04 04:33:00 -04:00
commit be14c98846
108 changed files with 4703 additions and 4395 deletions

3
.gitignore vendored
View file

@ -5,8 +5,7 @@ pkg/
*.swp *.swp
*~ *~
.vscode/ .vscode/
.vs/slnx.sqlite .vs/
.vs/microservices-demo/v15/.suo
.idea .idea
.skaffold-*.yaml .skaffold-*.yaml
.kubernetes-manifests-*/ .kubernetes-manifests-*/

View file

@ -4,7 +4,7 @@ services:
- docker - docker
install: install:
- curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v0.12.0/skaffold-linux-amd64 - curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v0.20.0/skaffold-linux-amd64
- chmod +x skaffold - chmod +x skaffold
- sudo mv skaffold /usr/local/bin - sudo mv skaffold /usr/local/bin

View file

@ -3,6 +3,12 @@
We'd love to accept your patches and contributions to this project. There are We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow. just a few small guidelines you need to follow.
## Development Principles (for Googlers)
There are a few principles for developing or refactoring the service
implementations. Read the [Development Principles
Guide](./docs/development-principles.md).
## Contributor License Agreement ## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License Contributions to this project must be accompanied by a Contributor License

250
README.md
View file

@ -4,14 +4,22 @@ This project contains a 10-tier microservices application. The application is a
web-based e-commerce app called **“Hipster Shop”** where users can browse items, web-based e-commerce app called **“Hipster Shop”** where users can browse items,
add them to the cart, and purchase them. add them to the cart, and purchase them.
**Google uses this application to demonstrate Kubernetes, GKE, Istio, **Google uses this application to demonstrate use of technologies like
Stackdriver, gRPC** and similar cloud-native technologies nowadays. Kubernetes/GKE, Istio, Stackdriver, gRPC and OpenCensus**. This application
works on any Kubernetes cluster (such as a local one), as well as Google
Kubernetes Engine. Its **easy to deploy with little to no configuration**.
If youre using this demo, please **★Star** this repository to show your interest!
> 👓**Note to Googlers:** Please fill out the form at
> [go/microservices-demo](http://go/microservices-demo) if you are using this
> application.
## Screenshots ## Screenshots
| Home Page | Checkout Screen | | Home Page | Checkout Screen |
|-----------|-----------------| | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [![Screenshot of store homepage](./img/hipster-shop-frontend-1.png)](./img/hipster-shop-frontend-1.png) | [![Screenshot of checkout screen](./img/hipster-shop-frontend-2.png)](./img/hipster-shop-frontend-2.png) | | [![Screenshot of store homepage](./docs/img/hipster-shop-frontend-1.png)](./docs/img/hipster-shop-frontend-1.png) | [![Screenshot of checkout screen](./docs/img/hipster-shop-frontend-2.png)](./docs/img/hipster-shop-frontend-2.png) |
## Service Architecture ## Service Architecture
@ -19,24 +27,23 @@ Stackdriver, gRPC** and similar cloud-native technologies nowadays.
languages that talk to each other over gRPC. languages that talk to each other over gRPC.
[![Architecture of [![Architecture of
microservices](./img/architecture-diagram.png)](./img/architecture-diagram.png) microservices](./docs/img/architecture-diagram.png)](./docs/img/architecture-diagram.png)
Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb). Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
| Service | Language | Description | | Service | Language | Description |
|---------|----------|-------------| | ---------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| [frontend](./src/frontend) | Go | Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically. | | [frontend](./src/frontend) | Go | Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically. |
| [cartservice](./src/cartservice) | C# | Stores the items in the user's shipping cart in Redis and retrieves it. | | [cartservice](./src/cartservice) | C# | Stores the items in the user's shipping cart in Redis and retrieves it. |
| [productcatalogservice](./src/productcatalogservice) | Go | Provides the list of products from a JSON file and ability to search products and get individual products. | | [productcatalogservice](./src/productcatalogservice) | Go | Provides the list of products from a JSON file and ability to search products and get individual products. |
| [currencyservice](./src/currencyservice) | Node.js | Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service. | | [currencyservice](./src/currencyservice) | Node.js | Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service. |
| [paymentservice](./src/paymentservice) | Node.js | Charges the given credit card info (hypothetically😇) with the given amount and returns a transaction ID. | | [paymentservice](./src/paymentservice) | Node.js | Charges the given credit card info (mock) with the given amount and returns a transaction ID. |
| [shippingservice](./src/shippingservice) | Go | Gives shipping cost estimates based on the shopping cart. Ships items to the given address (hypothetically😇) | | [shippingservice](./src/shippingservice) | Go | Gives shipping cost estimates based on the shopping cart. Ships items to the given address (mock) |
| [emailservice](./src/emailservice) | Python | Sends users an order confirmation email (hypothetically😇). | | [emailservice](./src/emailservice) | Python | Sends users an order confirmation email (mock). |
| [checkoutservice](./src/checkoutservice) | Go | Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification. | | [checkoutservice](./src/checkoutservice) | Go | Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification. |
| [recommendationservice](./src/recommendationservice) | Python | Recommends other products based on what's given in the cart. | | [recommendationservice](./src/recommendationservice) | Python | Recommends other products based on what's given in the cart. |
| [adservice](./src/adservice) | Java | Provides text ads based on given context words. | | [adservice](./src/adservice) | Java | Provides text ads based on given context words. |
| [loadgenerator](./src/loadgenerator) | Python/Locust | Continuously sends requests imitating realistic user shopping flows to the frontend. | | [loadgenerator](./src/loadgenerator) | Python/Locust | Continuously sends requests imitating realistic user shopping flows to the frontend. |
## Features ## Features
@ -53,7 +60,7 @@ Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
addition to these, using Istio enables features like Request/Response addition to these, using Istio enables features like Request/Response
**Metrics** and **Context Graph** out of the box. When it is running out of **Metrics** and **Context Graph** out of the box. When it is running out of
Google Cloud, this code path remains inactive. Google Cloud, this code path remains inactive.
- **[Skaffold](https://github.com/GoogleContainerTools/skaffold):** Application - **[Skaffold](https://skaffold.dev):** Application
is deployed to Kubernetes with a single command using Skaffold. is deployed to Kubernetes with a single command using Skaffold.
- **Synthetic Load Generation:** The application demo comes with a background - **Synthetic Load Generation:** The application demo comes with a background
job that creates realistic usage patterns on the website using job that creates realistic usage patterns on the website using
@ -61,27 +68,45 @@ Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
## Installation ## Installation
> **Note:** that the first build can take up to 20-30 minutes. Consequent builds We offer three installation methods:
> will be faster.
1. **Running locally with “Docker for Desktop”** (~20 minutes) You will build
and deploy microservices images to a single-node Kubernetes cluster running
on your development machine.
2. **Running on Google Kubernetes Engine (GKE)”** (~30 minutes) You will build,
upload and deploy the container images to a Kubernetes cluster on Google
Cloud.
3. **Using pre-built container images:** (~10 minutes, you will still need to
follow one of the steps above up until `skaffold run` command). With this
option, you will use pre-built container images that are available publicly,
instead of building them yourself, which takes a long time).
### Option 1: Running locally with “Docker for Desktop” ### Option 1: Running locally with “Docker for Desktop”
> 💡 Recommended if you're planning to develop the application. > 💡 Recommended if you're planning to develop the application or giving it a
> try on your local cluster.
1. Install tools to run a Kubernetes cluster locally: 1. Install tools to run a Kubernetes cluster locally:
- kubectl (can be installed via `gcloud components install kubectl`) - kubectl (can be installed via `gcloud components install kubectl`)
- Docker for Desktop (Mac/Windows): It provides Kubernetes support as [noted - Docker for Desktop (Mac/Windows): It provides Kubernetes support as [noted
here](https://docs.docker.com/docker-for-mac/kubernetes/). here](https://docs.docker.com/docker-for-mac/kubernetes/).
- [skaffold](https://github.com/GoogleContainerTools/skaffold/#installation) - [skaffold](https://skaffold.dev/docs/getting-started/#installing-skaffold)
(ensure version ≥v0.20)
1. Launch “Docker for Desktop”. Go to Preferences and choose “Enable Kubernetes”. 1. Launch “Docker for Desktop”. Go to Preferences:
- choose “Enable Kubernetes”,
- set CPUs to at least 3, and Memory to at least 6.0 GiB
- on the "Disk" tab, set at least 32 GB disk space
1. Run `kubectl get nodes` to verify you're connected to “Kubernetes on Docker”. 1. Run `kubectl get nodes` to verify you're connected to “Kubernetes on Docker”.
1. Run `skaffold run` (first time will be slow, it can take ~20-30 minutes). 1. Run `skaffold run` (first time will be slow, it can take ~20 minutes).
This will build and deploy the application. If you need to rebuild the images This will build and deploy the application. If you need to rebuild the images
automatically as you refactor he code, run `skaffold dev` command. automatically as you refactor the code, run `skaffold dev` command.
1. Run `kubectl get pods` to verify the Pods are ready and running. The 1. Run `kubectl get pods` to verify the Pods are ready and running. The
application frontend should be available at http://localhost:80 on your application frontend should be available at http://localhost:80 on your
@ -89,86 +114,165 @@ Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
### Option 2: Running on Google Kubernetes Engine (GKE) ### Option 2: Running on Google Kubernetes Engine (GKE)
> 💡 Recommended for demos and making it available publicly. > 💡 Recommended if you're using Google Cloud Platform and want to try it on
> a realistic cluster.
1. Install tools specified in the previous section (Docker, kubectl, skaffold) 1. Install tools specified in the previous section (Docker, kubectl, skaffold)
1. Create a Google Kubernetes Engine cluster and make sure `kubectl` is pointing 1. Create a Google Kubernetes Engine cluster and make sure `kubectl` is pointing
to the cluster. to the cluster.
gcloud services enable container.googleapis.com ```sh
gcloud services enable container.googleapis.com
```
gcloud container clusters create demo --enable-autoupgrade \ ```sh
--enable-autoscaling --min-nodes=3 --max-nodes=10 --num-nodes=5 gcloud container clusters create demo --enable-autoupgrade \
--enable-autoscaling --min-nodes=3 --max-nodes=10 --num-nodes=5 --zone=us-central1-a
```
kubectl get nodes ```
kubectl get nodes
```
2. Enable Google Container Registry (GCR) on your GCP project and configure the 1. Enable Google Container Registry (GCR) on your GCP project and configure the
`docker` CLI to authenticate to GCR: `docker` CLI to authenticate to GCR:
gcloud services enable containerregistry.googleapis.com ```sh
gcloud services enable containerregistry.googleapis.com
```
gcloud auth configure-docker -q ```sh
gcloud auth configure-docker -q
```
3. Set your project ID on image names: 1. In the root of this repository, run `skaffold run --default-repo=gcr.io/[PROJECT_ID]`,
where [PROJECT_ID] is your GCP project ID.
- Edit `skaffold.yaml`, update the `imageName:` fields that look like This command:
`gcr.io/[PROJECT_ID]` with your own GCP project ID.
- Similarly, edit all Kubernetes Deployment manifests in the - builds the container images
[`./kubernetes-manifests`](./kubernetes-manifests) directory. Find the - pushes them to GCR
`image:` fields with `gcr.io/[...]` and change them to your own GCP project - applies the `./kubernetes-manifests` deploying the application to
ID. Kubernetes.
5. Run `skaffold run` from the root of this repository. This command: **Troubleshooting:** If you get "No space left on device" error on Google
- builds the container images Cloud Shell, you can build the images on Google Cloud Build: [Enable the
- pushes them to GCR Cloud Build
- applies the `./kubernetes-manifests` deploying the application to API](https://console.cloud.google.com/flows/enableapi?apiid=cloudbuild.googleapis.com),
Kubernetes. then run `skaffold run -p gcb --default-repo=gcr.io/[PROJECT_ID]` instead.
6. Find the IP address of your application, then visit the application on your 1. Find the IP address of your application, then visit the application on your
browser to confirm installation. browser to confirm installation.
kubectl get service frontend-external kubectl get service frontend-external
### (Optional) Deploying on a Istio-installed cluster **Troubleshooting:** A Kubernetes bug (will be fixed in 1.12) combined with
a Skaffold [bug](https://github.com/GoogleContainerTools/skaffold/issues/887)
causes load balancer to not to work even after getting an IP address. If you
are seeing this, run `kubectl get service frontend-external -o=yaml | kubectl apply -f-`
to trigger load balancer reconfiguration.
### Option 3: Using Pre-Built Container Images
> 💡 Recommended if you want to deploy the app faster in fewer steps to an
> existing cluster.
**NOTE:** If you need to create a Kubernetes cluster locally or on the cloud,
follow "Option 1" or "Option 2" until you reach the `skaffold run` step.
This option offers you pre-built public container images that are easy to deploy
by deploying the [release manifest](./release) directly to an existing cluster.
**Prerequisite**: a running Kubernetes cluster (either local or on cloud).
1. Clone this repository, and go to the repository directory
1. Run `kubectl apply -f ./release/kubernetes-manifests.yaml` to deploy the app.
1. Run `kubectl get pods` to see pods are in a Ready state.
1. Find the IP address of your application, then visit the application on your
browser to confirm installation.
```sh
kubectl get service/frontend-external
```
### (Optional) Deploying on a Istio-installed GKE cluster
> **Note:** you followed GKE deployment steps above, run `skaffold delete` first > **Note:** you followed GKE deployment steps above, run `skaffold delete` first
> to delete what's deployed. > to delete what's deployed.
1. Create a GKE cluster. 1. Create a GKE cluster (described in "Option 2").
2. Install Istio **without mutual TLS** option. (Istio mTLS is not yet supported 1. Use [Istio on GKE add-on](https://cloud.google.com/istio/docs/istio-on-gke/installing)
on this demo.) to install Istio to your existing GKE cluster.
3. Install the automatic sidecar injection (annotate the `default` namespace ```sh
gcloud beta container clusters update demo \
--zone=us-central1-a \
--update-addons=Istio=ENABLED \
--istio-config=auth=MTLS_PERMISSIVE
```
> NOTE: If you need to enable `MTLS_STRICT` mode, you will need to update
> several manifest files:
>
> - `kubernetes-manifests/frontend.yaml`: delete "livenessProbe" and
> "readinessProbe" fields.
> - `kubernetes-manifests/loadgenerator.yaml`: delete "initContainers" field.
1. (Optional) Enable Stackdriver Tracing/Logging with Istio Stackdriver Adapter
by [following this guide](https://cloud.google.com/istio/docs/istio-on-gke/installing#enabling_tracing_and_logging).
1. Install the automatic sidecar injection (annotate the `default` namespace
with the label): with the label):
kubectl label namespace default istio-injection=enabled ```sh
kubectl label namespace default istio-injection=enabled
```
4. Apply the manifests in [`./istio-manifests`](./istio-manifests) directory. 1. Apply the manifests in [`./istio-manifests`](./istio-manifests) directory.
(This is required only once.)
kubectl apply -f ./istio-manifests ```sh
kubectl apply -f ./istio-manifests
```
This is required only once. 1. Deploy the application with `skaffold run --default-repo=gcr.io/[PROJECT_ID]`.
5. Deploy the application with `skaffold run`. 1. Run `kubectl get pods` to see pods are in a healthy and ready state.
6. Run `kubectl get pods` to see pods are in a healthy and ready state. 1. Find the IP address of your Istio gateway Ingress or Service, and visit the
7. Find the IP address of your istio gateway Ingress or Service, and visit the
application. application.
INGRESS_HOST="$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" ```sh
INGRESS_HOST="$(kubectl -n istio-system get service istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
echo "$INGRESS_HOST"
```
echo "$INGRESS_HOST" ```sh
curl -v "http://$INGRESS_HOST"
```
curl -v "http://$INGRESS_HOST" ### Cleanup
If you've deployed the application with `skaffold run` command, you can run
`skaffold delete` to clean up the deployed resources.
If you've deployed the application with `kubectl apply -f [...]`, you can
run `kubectl delete -f [...]` with the same argument to clean up the deployed
resources.
## Conferences featuring Hipster Shop
- [Google Cloud Next'18 London Keynote](https://youtu.be/nIq2pkNcfEI?t=3071)
showing Stackdriver Incident Response Management
- Google Cloud Next'18 SF
- [Day 1 Keynote](https://youtu.be/vJ9OaAqfxo4?t=2416) showing GKE On-Prem
- [Day 3 Keynote](https://youtu.be/JQPOPV_VH5w?t=815) showing Stackdriver
APM (Tracing, Code Search, Profiler, Google Cloud Build)
- [Introduction to Service Management with Istio](https://www.youtube.com/watch?v=wCJrdKdD6UM&feature=youtu.be&t=586)
--- ---
**Note to fellow Googlers:** Please fill out the form at
[go/microservices-demo](http://go/microservices-demo) if you are using this
application.
This is not an official Google project. This is not an official Google project.

25
cloudbuild.yaml Normal file
View file

@ -0,0 +1,25 @@
# This configuration file is used to build and deploy the app into a
# GKE cluster using Google Cloud Build.
#
# PREREQUISITES:
# - Cloud Build service account must have role: "Kubernetes Engine Developer"
# USAGE:
# GCP zone and GKE target cluster must be specified as substitutions
# Example invocation:
# `gcloud builds submit --config=cloudbuild.yaml --substitutions=_ZONE=us-central1-b,_CLUSTER=demo-app-staging .`
steps:
- id: 'Deploy application to cluster'
name: 'gcr.io/k8s-skaffold/skaffold:v0.20.0'
entrypoint: 'bash'
args:
- '-c'
- >
gcloud container clusters get-credentials --zone=$_ZONE $_CLUSTER;
skaffold run -f=skaffold.yaml --default-repo=gcr.io/$PROJECT_ID;
# Add more power, and more time, for heavy Skaffold build
timeout: '3600s'
options:
machineType: 'N1_HIGHCPU_8'

View file

@ -0,0 +1,44 @@
# Development Principles
> **Note:** This document outlines guidances behind some development decisions
> behind the Hipster Shop demo application.
### Minimal configuration
Running the demo locally or on GCP should require minimal to no
configuration unless absolutely necessary to run critical parts of the demo.
Configuration that takes multiple steps, especially such as creating service
accounts should be avoided.
### App must work well outside GCP
Demo application should work reasonably well when it is not deployed to GCP
services. The experience of running the application locally or on GCP should
be close.
For example:
- OpenCensus prints the traces to stdout when it cannot connect to GCP.
- Stackdriver Debugging tries connecting to GCP multiple times, eventually gives
up.
### Running on GCP must not reduce functionality
Running the demo on the GCP must not reduce/lose any of the capabilities
developers have when running locally.
For example: Logs should still be printed to stdout/stderr even though logs are
uploaded to Stackdriver Logging when on GCP, so that developers can use "kubectl
logs" to diagnose each container.
### Microservice implementations should not be complex
Each service should provide a minimal implementation and try to avoid
unnecessary code and logic that's not executed.
Keep in mind that any service implementation is a decent example of “a GRPC
application that runs on Kubernetes”. Keeping the source code short and
navigable will serve this purpose.
It is okay to have intentional inefficiencies in the code as they help
illustrate the capabilities of profiling and diagnostics offerings.

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View file

Before

Width:  |  Height:  |  Size: 776 KiB

After

Width:  |  Height:  |  Size: 776 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 358 KiB

After

Width:  |  Height:  |  Size: 358 KiB

Before After
Before After

18
hack/README.md Executable file
View file

@ -0,0 +1,18 @@
## `hack/`
This directory provides scripts for building and pushing Docker images, and tagging new demo
releases.
### env variables
- `TAG` - git release tag / Docker tag.
- `REPO_PREFIX` - Docker repo prefix to push images. Format: `$user/$project`. Resulting images will be of the
format `$user/$project/$svcname:$tag` (where `svcname` = `adservice`, `cartservice`,
etc.)
### scripts
1. `./make-docker-images.sh`: builds and pushes images to the specified Docker repository.
2. `./make-release-artifacts.sh`: generates a combined YAML file with image $TAG at:
`./release/kubernetes-manifests/demo.yaml`.
3. `./make-release.sh`: runs scripts 1 and 2, then runs `git tag` / pushes updated manifests to master.

View file

@ -1,5 +1,3 @@
#!/bin/bash -eu
#
# Copyright 2018 Google LLC # Copyright 2018 Google LLC
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@ -13,10 +11,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
#!/bin/bash -e
PATH=$PATH:$GOPATH/bin
protodir=../../../pb
protoc --go_out=plugins=grpc:genproto -I $protodir $protodir/demo.proto

41
hack/make-docker-images.sh Executable file
View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# 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.
# Builds and pushes docker image for each demo microservice.
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
log() { echo "$1" >&2; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
while IFS= read -d $'\0' -r dir; do
# build image
svcname="$(basename "${dir}")"
image="${REPO_PREFIX}/$svcname:$TAG"
(
cd "${dir}"
log "Building: ${image}"
docker build -t "${image}" .
log "Pushing: ${image}"
docker push "${image}"
)
done < <(find "${SCRIPTDIR}/../src" -mindepth 1 -maxdepth 1 -type d -print0)
log "Successfully built and pushed all images."

101
hack/make-release-artifacts.sh Executable file
View file

@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# 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.
# This script compiles manifest files with the image tags and places them in
# /release/...
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
[[ -n "${DEBUG:-}" ]] && set -x
log() { echo "$1" >&2; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
OUT_DIR="${OUT_DIR:-${SCRIPTDIR}/../release}"
print_license_header() {
cat "${SCRIPTDIR}/license_header.txt"
echo
}
print_autogenerated_warning() {
cat<<EOF
# ----------------------------------------------------------
# WARNING: This file is autogenerated. Do not manually edit.
# ----------------------------------------------------------
EOF
}
# define gsed as a function on Linux for compatibility
[ "$(uname -s)" == "Linux" ] && gsed() {
sed "$@"
}
read_manifests() {
local dir
dir="$1"
while IFS= read -d $'\0' -r file; do
# strip license headers (pattern "^# ")
awk '
/^[^# ]/ { found = 1 }
found { print }' "${file}"
echo "---"
done < <(find "${dir}" -name '*.yaml' -type f -print0)
}
mk_kubernetes_manifests() {
out_manifest="$(read_manifests "${SCRIPTDIR}/../kubernetes-manifests")"
# replace "image" repo, tag for each service
for dir in ./src/*/
do
svcname="$(basename "${dir}")"
image="$REPO_PREFIX/$svcname:$TAG"
pattern="^(\s*)image:\s.*$svcname(.*)(\s*)"
replace="\1image: $image\3"
out_manifest="$(gsed -r "s|$pattern|$replace|g" <(echo "${out_manifest}") )"
done
print_license_header
print_autogenerated_warning
echo "${out_manifest}"
}
mk_istio_manifests() {
print_license_header
print_autogenerated_warning
read_manifests "${SCRIPTDIR}/../istio-manifests"
}
main() {
mkdir -p "${OUT_DIR}"
local k8s_manifests_file istio_manifests_file
k8s_manifests_file="${OUT_DIR}/kubernetes-manifests.yaml"
mk_kubernetes_manifests > "${k8s_manifests_file}"
log "Written ${k8s_manifests_file}"
istio_manifests_file="${OUT_DIR}/istio-manifests.yaml"
mk_istio_manifests > "${istio_manifests_file}"
log "Written ${istio_manifests_file}"
}
main

51
hack/make-release.sh Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# 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.
# This script creates a new release by:
# - 1. building/pushing images
# - 2. injecting tags into YAML manifests
# - 3. creating a new git tag
# - 4. pushing the tag/commit to master.
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
[[ -n "${DEBUG:-}" ]] && set -x
log() { echo "$1" >&2; }
fail() { log "$1"; exit 1; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified e.g. gcr.io\/google-samples\/microservices-demo}"
if [[ "$TAG" != v* ]]; then
fail "\$TAG must start with 'v', e.g. v0.1.0 (got: $TAG)"
fi
# build and push images
"${SCRIPTDIR}"/make-docker-images.sh
# update yaml
"${SCRIPTDIR}"/make-release-artifacts.sh
# create git release / push to master
git add "${SCRIPTDIR}/../release/"
git commit --allow-empty -m "Release $TAG"
log "Pushing k8s manifests to master..."
git tag "$TAG"
git push --tags
git push origin master
log "Successfully tagged release $TAG."

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View file

@ -1,28 +0,0 @@
# Copyright 2018 Google LLC
#
# 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.
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: currency-provider-external
spec:
hosts:
- www.ecb.europa.eu
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS

View file

@ -18,9 +18,6 @@ metadata:
name: whitelist-egress-googleapis name: whitelist-egress-googleapis
spec: spec:
hosts: hosts:
- "169.254.169.254" # GCE metadata server
- "metadata.google" # GCE metadata server
- "metadata.google.internal" # GCE metadata server
- "accounts.google.com" # Used to get token - "accounts.google.com" # Used to get token
- "*.googleapis.com" - "*.googleapis.com"
ports: ports:
@ -30,3 +27,20 @@ spec:
- number: 443 - number: 443
protocol: HTTPS protocol: HTTPS
name: https name: https
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: whitelist-egress-google-metadata
spec:
hosts:
- metadata.google.internal
addresses:
- 169.254.169.254 # GCE metadata server
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS

View file

@ -0,0 +1,8 @@
# ./kubernetes-manifests
:warning: Kubernetes manifests provided in this directory are not directly
deployable to a cluster. They are meant to be used with `skaffold` command to
insert the correct `image:` tags.
Use the manifests in [/release](/release) directory which are configured with
pre-built public images.

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: adservice name: adservice
spec: spec:
selector:
matchLabels:
app: adservice
template: template:
metadata: metadata:
labels: labels:
@ -25,12 +28,14 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/adservice image: adservice
ports: ports:
- containerPort: 9555 - containerPort: 9555
env: env:
- name: PORT - name: PORT
value: "9555" value: "9555"
#- name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources: resources:
requests: requests:
cpu: 200m cpu: 200m
@ -40,12 +45,12 @@ spec:
memory: 300Mi memory: 300Mi
readinessProbe: readinessProbe:
initialDelaySeconds: 20 initialDelaySeconds: 20
periodSeconds: 5 periodSeconds: 15
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:9555"] command: ["/bin/grpc_health_probe", "-addr=:9555"]
livenessProbe: livenessProbe:
initialDelaySeconds: 20 initialDelaySeconds: 20
periodSeconds: 5 periodSeconds: 15
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:9555"] command: ["/bin/grpc_health_probe", "-addr=:9555"]
--- ---

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: cartservice name: cartservice
spec: spec:
selector:
matchLabels:
app: cartservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/cartservice image: cartservice
ports: ports:
- containerPort: 7070 - containerPort: 7070
env: env:
@ -45,12 +48,12 @@ spec:
readinessProbe: readinessProbe:
initialDelaySeconds: 15 initialDelaySeconds: 15
exec: exec:
command: ["/cartservice_probe"] command: ["/bin/grpc_health_probe", "-addr=:7070"]
livenessProbe: livenessProbe:
initialDelaySeconds: 15 initialDelaySeconds: 15
periodSeconds: 10 periodSeconds: 10
exec: exec:
command: ["/cartservice_probe"] command: ["/bin/grpc_health_probe", "-addr=:7070"]
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: checkoutservice name: checkoutservice
spec: spec:
selector:
matchLabels:
app: checkoutservice
template: template:
metadata: metadata:
labels: labels:
@ -24,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/checkoutservice image: checkoutservice
ports: ports:
- containerPort: 5050 - containerPort: 5050
readinessProbe: readinessProbe:
@ -46,6 +49,8 @@ spec:
value: "currencyservice:7000" value: "currencyservice:7000"
- name: CART_SERVICE_ADDR - name: CART_SERVICE_ADDR
value: "cartservice:7070" value: "cartservice:7070"
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: currencyservice name: currencyservice
spec: spec:
selector:
matchLabels:
app: currencyservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/currencyservice image: currencyservice
ports: ports:
- name: grpc - name: grpc
containerPort: 7000 containerPort: 7000

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: emailservice name: emailservice
spec: spec:
selector:
matchLabels:
app: emailservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/emailservice image: emailservice
ports: ports:
- containerPort: 8080 - containerPort: 8080
readinessProbe: readinessProbe:
@ -36,6 +39,9 @@ spec:
periodSeconds: 5 periodSeconds: 5
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"] command: ["/bin/grpc_health_probe", "-addr=:8080"]
env:
- name: ENABLE_PROFILER
value: "0"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: frontend name: frontend
spec: spec:
selector:
matchLabels:
app: frontend
template: template:
metadata: metadata:
labels: labels:
@ -24,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/frontend image: frontend
ports: ports:
- containerPort: 8080 - containerPort: 8080
readinessProbe: readinessProbe:
@ -38,7 +41,7 @@ spec:
livenessProbe: livenessProbe:
initialDelaySeconds: 10 initialDelaySeconds: 10
httpGet: httpGet:
path: "/" path: "/_healthz"
port: 8080 port: 8080
httpHeaders: httpHeaders:
- name: "Cookie" - name: "Cookie"
@ -58,6 +61,8 @@ spec:
value: "checkoutservice:5050" value: "checkoutservice:5050"
- name: AD_SERVICE_ADDR - name: AD_SERVICE_ADDR
value: "adservice:9555" value: "adservice:9555"
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: loadgenerator name: loadgenerator
spec: spec:
selector:
matchLabels:
app: loadgenerator
replicas: 1 replicas: 1
template: template:
metadata: metadata:
@ -28,7 +31,7 @@ spec:
initContainers: initContainers:
- name: wait-frontend - name: wait-frontend
image: alpine:3.6 image: alpine:3.6
command: ['sh', '-c', 'set -x; apk add --no-cache curl; command: ['sh', '-c', 'set -x; apk add --no-cache curl &&
until timeout -t 2 curl -f "http://${FRONTEND_ADDR}"; do until timeout -t 2 curl -f "http://${FRONTEND_ADDR}"; do
echo "waiting for http://${FRONTEND_ADDR}"; echo "waiting for http://${FRONTEND_ADDR}";
sleep 2; sleep 2;
@ -38,7 +41,7 @@ spec:
value: "frontend:80" value: "frontend:80"
containers: containers:
- name: main - name: main
image: gcr.io/microservices-demo-app/loadgenerator image: loadgenerator
env: env:
- name: FRONTEND_ADDR - name: FRONTEND_ADDR
value: "frontend:80" value: "frontend:80"

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: paymentservice name: paymentservice
spec: spec:
selector:
matchLabels:
app: paymentservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/paymentservice image: paymentservice
ports: ports:
- containerPort: 50051 - containerPort: 50051
readinessProbe: readinessProbe:

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: productcatalogservice name: productcatalogservice
spec: spec:
selector:
matchLabels:
app: productcatalogservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/productcatalogservice image: productcatalogservice
ports: ports:
- containerPort: 3550 - containerPort: 3550
readinessProbe: readinessProbe:
@ -34,6 +37,9 @@ spec:
livenessProbe: livenessProbe:
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:3550"] command: ["/bin/grpc_health_probe", "-addr=:3550"]
# env:
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: recommendationservice name: recommendationservice
spec: spec:
selector:
matchLabels:
app: recommendationservice
template: template:
metadata: metadata:
labels: labels:
@ -25,7 +28,7 @@ spec:
terminationGracePeriodSeconds: 5 terminationGracePeriodSeconds: 5
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/recommendationservice image: recommendationservice
ports: ports:
- containerPort: 8080 - containerPort: 8080
readinessProbe: readinessProbe:
@ -39,6 +42,8 @@ spec:
env: env:
- name: PRODUCT_CATALOG_SERVICE_ADDR - name: PRODUCT_CATALOG_SERVICE_ADDR
value: "productcatalogservice:3550" value: "productcatalogservice:3550"
- name: ENABLE_PROFILER
value: "0"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: redis-cart name: redis-cart
spec: spec:
selector:
matchLabels:
app: redis-cart
template: template:
metadata: metadata:
labels: labels:

View file

@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: shippingservice name: shippingservice
spec: spec:
selector:
matchLabels:
app: shippingservice
template: template:
metadata: metadata:
labels: labels:
@ -24,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: server - name: server
image: gcr.io/microservices-demo-app/shippingservice image: shippingservice
ports: ports:
- containerPort: 50051 - containerPort: 50051
readinessProbe: readinessProbe:
@ -34,6 +37,9 @@ spec:
livenessProbe: livenessProbe:
exec: exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"] command: ["/bin/grpc_health_probe", "-addr=:50051"]
# env:
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View file

@ -64,6 +64,10 @@ message Product {
string description = 3; string description = 3;
string picture = 4; string picture = 4;
Money price_usd = 5; Money price_usd = 5;
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
repeated string categories = 6;
} }
message ListProductsResponse { message ListProductsResponse {

View file

@ -0,0 +1,111 @@
# Copyright 2018 Google LLC
#
# 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.
# ----------------------------------------------------------
# WARNING: This file is autogenerated. Do not manually edit.
# ----------------------------------------------------------
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: frontend-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: frontend-ingress
spec:
hosts:
- "*"
gateways:
- frontend-gateway
http:
- route:
- destination:
host: frontend
port:
number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: currency-provider-external
spec:
hosts:
- www.ecb.europa.eu
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: frontend
spec:
hosts:
- "frontend.default.svc.cluster.local"
http:
- route:
- destination:
host: frontend
port:
number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: whitelist-egress-googleapis
spec:
hosts:
- "accounts.google.com" # Used to get token
- "*.googleapis.com"
ports:
- number: 80
protocol: HTTP
name: http
- number: 443
protocol: HTTPS
name: https
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: whitelist-egress-google-metadata
spec:
hosts:
- metadata.google.internal
addresses:
- 169.254.169.254 # GCE metadata server
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
---

View file

@ -0,0 +1,660 @@
# Copyright 2018 Google LLC
#
# 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.
# ----------------------------------------------------------
# WARNING: This file is autogenerated. Do not manually edit.
# ----------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: emailservice
spec:
selector:
matchLabels:
app: emailservice
template:
metadata:
labels:
app: emailservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/emailservice:v0.1.0
ports:
- containerPort: 8080
readinessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
livenessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: emailservice
spec:
type: ClusterIP
selector:
app: emailservice
ports:
- name: grpc
port: 5000
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkoutservice
spec:
selector:
matchLabels:
app: checkoutservice
template:
metadata:
labels:
app: checkoutservice
spec:
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/checkoutservice:v0.1.0
ports:
- containerPort: 5050
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:5050"]
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:5050"]
env:
- name: PRODUCT_CATALOG_SERVICE_ADDR
value: "productcatalogservice:3550"
- name: SHIPPING_SERVICE_ADDR
value: "shippingservice:50051"
- name: PAYMENT_SERVICE_ADDR
value: "paymentservice:50051"
- name: EMAIL_SERVICE_ADDR
value: "emailservice:5000"
- name: CURRENCY_SERVICE_ADDR
value: "currencyservice:7000"
- name: CART_SERVICE_ADDR
value: "cartservice:7070"
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: checkoutservice
spec:
type: ClusterIP
selector:
app: checkoutservice
ports:
- name: grpc
port: 5050
targetPort: 5050
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: recommendationservice
spec:
selector:
matchLabels:
app: recommendationservice
template:
metadata:
labels:
app: recommendationservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/recommendationservice:v0.1.0
ports:
- containerPort: 8080
readinessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
livenessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
env:
- name: PRODUCT_CATALOG_SERVICE_ADDR
value: "productcatalogservice:3550"
resources:
requests:
cpu: 100m
memory: 220Mi
limits:
cpu: 200m
memory: 450Mi
---
apiVersion: v1
kind: Service
metadata:
name: recommendationservice
spec:
type: ClusterIP
selector:
app: recommendationservice
ports:
- name: grpc
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/frontend:v0.1.0
ports:
- containerPort: 8080
readinessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8080
httpHeaders:
- name: "Cookie"
value: "shop_session-id=x-readiness-probe"
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8080
httpHeaders:
- name: "Cookie"
value: "shop_session-id=x-liveness-probe"
env:
- name: PRODUCT_CATALOG_SERVICE_ADDR
value: "productcatalogservice:3550"
- name: CURRENCY_SERVICE_ADDR
value: "currencyservice:7000"
- name: CART_SERVICE_ADDR
value: "cartservice:7070"
- name: RECOMMENDATION_SERVICE_ADDR
value: "recommendationservice:8080"
- name: SHIPPING_SERVICE_ADDR
value: "shippingservice:50051"
- name: CHECKOUT_SERVICE_ADDR
value: "checkoutservice:5050"
- name: AD_SERVICE_ADDR
value: "adservice:9555"
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
type: ClusterIP
selector:
app: frontend
ports:
- name: http
port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontend-external
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- name: http
port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: paymentservice
spec:
selector:
matchLabels:
app: paymentservice
template:
metadata:
labels:
app: paymentservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/paymentservice:v0.1.0
ports:
- containerPort: 50051
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"]
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"]
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: paymentservice
spec:
type: ClusterIP
selector:
app: paymentservice
ports:
- name: grpc
port: 50051
targetPort: 50051
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: productcatalogservice
spec:
selector:
matchLabels:
app: productcatalogservice
template:
metadata:
labels:
app: productcatalogservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/productcatalogservice:v0.1.0
ports:
- containerPort: 3550
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:3550"]
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:3550"]
# env:
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: productcatalogservice
spec:
type: ClusterIP
selector:
app: productcatalogservice
ports:
- name: grpc
port: 3550
targetPort: 3550
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cartservice
spec:
selector:
matchLabels:
app: cartservice
template:
metadata:
labels:
app: cartservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/cartservice:v0.1.0
ports:
- containerPort: 7070
env:
- name: REDIS_ADDR
value: "redis-cart:6379"
- name: PORT
value: "7070"
- name: LISTEN_ADDR
value: "0.0.0.0"
resources:
requests:
cpu: 200m
memory: 64Mi
limits:
cpu: 300m
memory: 128Mi
readinessProbe:
initialDelaySeconds: 15
exec:
command: ["/bin/grpc_health_probe", "-addr=:7070"]
livenessProbe:
initialDelaySeconds: 15
periodSeconds: 10
exec:
command: ["/bin/grpc_health_probe", "-addr=:7070"]
---
apiVersion: v1
kind: Service
metadata:
name: cartservice
spec:
type: ClusterIP
selector:
app: cartservice
ports:
- name: grpc
port: 7070
targetPort: 7070
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: loadgenerator
spec:
selector:
matchLabels:
app: loadgenerator
replicas: 1
template:
metadata:
labels:
app: loadgenerator
spec:
terminationGracePeriodSeconds: 5
restartPolicy: Always
initContainers:
- name: wait-frontend
image: alpine:3.6
command: ['sh', '-c', 'set -x; apk add --no-cache curl &&
until timeout -t 2 curl -f "http://${FRONTEND_ADDR}"; do
echo "waiting for http://${FRONTEND_ADDR}";
sleep 2;
done;']
env:
- name: FRONTEND_ADDR
value: "frontend:80"
containers:
- name: main
image: gcr.io/google-samples/microservices-demo/loadgenerator:v0.1.0
env:
- name: FRONTEND_ADDR
value: "frontend:80"
- name: USERS
value: "10"
resources:
requests:
cpu: 300m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: currencyservice
spec:
selector:
matchLabels:
app: currencyservice
template:
metadata:
labels:
app: currencyservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/currencyservice:v0.1.0
ports:
- name: grpc
containerPort: 7000
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:7000"]
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:7000"]
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: currencyservice
spec:
type: ClusterIP
selector:
app: currencyservice
ports:
- name: grpc
port: 7000
targetPort: 7000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: shippingservice
spec:
selector:
matchLabels:
app: shippingservice
template:
metadata:
labels:
app: shippingservice
spec:
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/shippingservice:v0.1.0
ports:
- containerPort: 50051
readinessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"]
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"]
# env:
# - name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: shippingservice
spec:
type: ClusterIP
selector:
app: shippingservice
ports:
- name: grpc
port: 50051
targetPort: 50051
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cart
spec:
selector:
matchLabels:
app: redis-cart
template:
metadata:
labels:
app: redis-cart
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
readinessProbe:
periodSeconds: 5
tcpSocket:
port: 6379
livenessProbe:
periodSeconds: 5
tcpSocket:
port: 6379
volumeMounts:
- mountPath: /data
name: redis-data
resources:
limits:
memory: 256Mi
cpu: 125m
requests:
cpu: 70m
memory: 200Mi
volumes:
- name: redis-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: redis-cart
spec:
type: ClusterIP
selector:
app: redis-cart
ports:
- name: redis
port: 6379
targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: adservice
spec:
selector:
matchLabels:
app: adservice
template:
metadata:
labels:
app: adservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/adservice:v0.1.0
ports:
- containerPort: 9555
env:
- name: PORT
value: "9555"
#- name: JAEGER_SERVICE_ADDR
# value: "jaeger-collector:14268"
resources:
requests:
cpu: 200m
memory: 180Mi
limits:
cpu: 300m
memory: 300Mi
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 15
exec:
command: ["/bin/grpc_health_probe", "-addr=:9555"]
livenessProbe:
initialDelaySeconds: 20
periodSeconds: 15
exec:
command: ["/bin/grpc_health_probe", "-addr=:9555"]
---
apiVersion: v1
kind: Service
metadata:
name: adservice
spec:
type: ClusterIP
selector:
app: adservice
ports:
- name: grpc
port: 9555
targetPort: 9555
---

View file

@ -12,57 +12,60 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
apiVersion: skaffold/v1alpha2 apiVersion: skaffold/v1beta2
kind: Config kind: Config
build: build:
artifacts:
# image tags are relative; to specify an image repo (e.g. GCR), you
# must provide a "default repo" using one of the methods described
# here:
# https://skaffold.dev/docs/concepts/#image-repository-handling
- image: emailservice
context: src/emailservice
- image: productcatalogservice
context: src/productcatalogservice
- image: recommendationservice
context: src/recommendationservice
- image: shippingservice
context: src/shippingservice
- image: checkoutservice
context: src/checkoutservice
- image: paymentservice
context: src/paymentservice
- image: currencyservice
context: src/currencyservice
- image: cartservice
context: src/cartservice
- image: frontend
context: src/frontend
- image: loadgenerator
context: src/loadgenerator
- image: adservice
context: src/adservice
tagPolicy: tagPolicy:
gitCommit: {} gitCommit: {}
artifacts:
- imageName: gcr.io/microservices-demo-app/emailservice
workspace: src/emailservice
- imageName: gcr.io/microservices-demo-app/productcatalogservice
workspace: src/productcatalogservice
- imageName: gcr.io/microservices-demo-app/recommendationservice
workspace: src/recommendationservice
- imageName: gcr.io/microservices-demo-app/shippingservice
workspace: src/shippingservice
- imageName: gcr.io/microservices-demo-app/checkoutservice
workspace: src/checkoutservice
- imageName: gcr.io/microservices-demo-app/paymentservice
workspace: src/paymentservice
- imageName: gcr.io/microservices-demo-app/currencyservice
workspace: src/currencyservice
- imageName: gcr.io/microservices-demo-app/cartservice
workspace: src/cartservice
- imageName: gcr.io/microservices-demo-app/frontend
workspace: src/frontend
- imageName: gcr.io/microservices-demo-app/loadgenerator
workspace: src/loadgenerator
- imageName: gcr.io/microservices-demo-app/adservice
workspace: src/adservice
deploy: deploy:
kubectl: kubectl:
manifests: manifests:
- ./kubernetes-manifests/**.yaml - ./kubernetes-manifests/**.yaml
profiles: profiles:
# "travis-ci" profile is used to build the images without # "travis-ci" profile is used to build the images without
# pushing them. # pushing them.
- name: travis-ci - name: travis-ci
build: build:
local: local:
skipPush: true push: false
# "gcb" profile allows building and pushing the images # "gcb" profile allows building and pushing the images
# on Google Container Builder without requiring docker # on Google Container Builder without requiring docker
# installed on the developer machine. However, note that # installed on the developer machine. However, note that
# since GCB does not cache the builds, each build will # since GCB does not cache the builds, each build will
# start from scratch and therefore take a long time. # start from scratch and therefore take a long time.
# #
# This is not used by default. To use it, run: # This is not used by default. To use it, run:
# skaffold run -p gcb # skaffold run -p gcb
- name: gcb - name: gcb
build: build:
googleCloudBuild: googleCloudBuild:
diskSizeGb: 300 diskSizeGb: 300
machineType: "N1_HIGHCPU_32" machineType: N1_HIGHCPU_32
timeout: 4000s

View file

@ -1,16 +1,26 @@
FROM openjdk:8 FROM openjdk:8-slim as builder
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe
WORKDIR /app WORKDIR /app
# Next three steps are for caching dependency downloads
# to improve subsequent docker build.
COPY ["build.gradle", "gradlew", "./"] COPY ["build.gradle", "gradlew", "./"]
COPY gradle gradle COPY gradle gradle
RUN chmod +x gradlew
RUN ./gradlew downloadRepos RUN ./gradlew downloadRepos
COPY . . COPY . .
RUN chmod +x gradlew
RUN ./gradlew installDist RUN ./gradlew installDist
FROM openjdk:8-alpine
RUN apk add --no-cache libc6-compat
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.1 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe
WORKDIR /app
COPY --from=builder /app .
EXPOSE 9555 EXPOSE 9555
ENTRYPOINT ["/app/build/install/hipstershop/bin/AdService"] ENTRYPOINT ["/app/build/install/hipstershop/bin/AdService"]

View file

@ -2,23 +2,25 @@
The Ad service provides advertisement based on context keys. If no context keys are provided then it returns random ads. The Ad service provides advertisement based on context keys. If no context keys are provided then it returns random ads.
## Local Build ## Building locally
The Ad service uses gradlew to compile/install/distribute. Gradle wrapper is already part of the source code. To build Ad Service, run:
The Ad service uses gradlew to compile/install/distribute. Gradle wrapper is already part of the source code. To build Ad Service, run
``` ```
cd src/adservice; ./gradlew installDist ./gradlew installDist
``` ```
It will create executable script src/adservice/build/install/hipstershop/bin/AdService It will create executable script src/adservice/build/install/hipstershop/bin/AdService
### Upgrade gradle version ### Upgrading gradle version
If you need to upgrade the version of gradle then run If you need to upgrade the version of gradle then run
``` ```
cd src/adservice ; ./gradlew wrapper --gradle-version <new-version> ./gradlew wrapper --gradle-version <new-version>
``` ```
## Docker Build ## Building docker image
From repository root, run: From the repository root, run:
``` ```
docker build --file src/adservice/Dockerfile . docker build --file src/adservice/Dockerfile .

View file

@ -10,12 +10,14 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
classpath "gradle.plugin.com.github.sherter.google-java-format:google-java-format-gradle-plugin:0.7.1"
} }
} }
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'com.google.protobuf' apply plugin: 'com.google.protobuf'
apply plugin: 'com.github.sherter.google-java-format'
repositories { repositories {
mavenCentral() mavenCentral()
@ -23,11 +25,11 @@ repositories {
} }
group = "adservice" group = "adservice"
version = "0.1.0-SNAPSHOT" // CURRENT_OPENCENSUS_VERSION version = "0.1.0-SNAPSHOT"
def opencensusVersion = "0.15.0" // LATEST_OPENCENSUS_RELEASE_VERSION def opencensusVersion = "0.18.0"
def grpcVersion = "1.10.1" // CURRENT_GRPC_VERSION def grpcVersion = "1.17.0"
def prometheusVersion = "0.3.0" def jacksonVersion = "2.9.6"
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
sourceCompatibility = '1.8' sourceCompatibility = '1.8'
@ -43,8 +45,10 @@ dependencies {
if (speed) { if (speed) {
compile fileTree(dir: offlineCompile, include: '*.jar') compile fileTree(dir: offlineCompile, include: '*.jar')
} else { } else {
compile "com.google.api.grpc:proto-google-common-protos:1.11.0", compile "com.google.api.grpc:proto-google-common-protos:1.12.0",
"io.opencensus:opencensus-exporter-stats-prometheus:${opencensusVersion}", "io.opencensus:opencensus-api:${opencensusVersion}",
"io.opencensus:opencensus-contrib-grpc-util:${opencensusVersion}",
"io.opencensus:opencensus-exporter-trace-jaeger:${opencensusVersion}",
"io.opencensus:opencensus-exporter-stats-stackdriver:${opencensusVersion}", "io.opencensus:opencensus-exporter-stats-stackdriver:${opencensusVersion}",
"io.opencensus:opencensus-exporter-trace-stackdriver:${opencensusVersion}", "io.opencensus:opencensus-exporter-trace-stackdriver:${opencensusVersion}",
"io.opencensus:opencensus-exporter-trace-logging:${opencensusVersion}", "io.opencensus:opencensus-exporter-trace-logging:${opencensusVersion}",
@ -52,9 +56,12 @@ dependencies {
"io.grpc:grpc-stub:${grpcVersion}", "io.grpc:grpc-stub:${grpcVersion}",
"io.grpc:grpc-netty:${grpcVersion}", "io.grpc:grpc-netty:${grpcVersion}",
"io.grpc:grpc-services:${grpcVersion}", "io.grpc:grpc-services:${grpcVersion}",
"io.prometheus:simpleclient_httpserver:${prometheusVersion}" "org.apache.logging.log4j:log4j-core:2.11.1"
runtime "io.opencensus:opencensus-impl:${opencensusVersion}", runtime "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}",
"com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}",
"io.opencensus:opencensus-contrib-log-correlation-log4j2:${opencensusVersion}",
"io.opencensus:opencensus-impl:${opencensusVersion}",
"io.netty:netty-tcnative-boringssl-static:2.0.8.Final" "io.netty:netty-tcnative-boringssl-static:2.0.8.Final"
} }
} }
@ -76,6 +83,10 @@ protobuf {
} }
} }
googleJavaFormat {
toolVersion '1.7'
}
// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code. // Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
sourceSets { sourceSets {
main { main {
@ -106,6 +117,8 @@ task adService(type: CreateStartScripts) {
applicationName = 'AdService' applicationName = 'AdService'
outputDir = new File(project.buildDir, 'tmp') outputDir = new File(project.buildDir, 'tmp')
classpath = jar.outputs.files + project.configurations.runtime classpath = jar.outputs.files + project.configurations.runtime
defaultJvmOpts =
["-Dlog4j2.contextDataInjector=io.opencensus.contrib.logcorrelation.log4j2.OpenCensusTraceContextDataInjector"]
} }
task adServiceClient(type: CreateStartScripts) { task adServiceClient(type: CreateStartScripts) {
@ -113,6 +126,8 @@ task adServiceClient(type: CreateStartScripts) {
applicationName = 'AdServiceClient' applicationName = 'AdServiceClient'
outputDir = new File(project.buildDir, 'tmp') outputDir = new File(project.buildDir, 'tmp')
classpath = jar.outputs.files + project.configurations.runtime classpath = jar.outputs.files + project.configurations.runtime
defaultJvmOpts =
["-Dlog4j2.contextDataInjector=io.opencensus.contrib.logcorrelation.log4j2.OpenCensusTraceContextDataInjector"]
} }
applicationDistribution.into('bin') { applicationDistribution.into('bin') {

View file

@ -16,7 +16,9 @@
package hipstershop; package hipstershop;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import hipstershop.Demo.Ad; import hipstershop.Demo.Ad;
import hipstershop.Demo.AdRequest; import hipstershop.Demo.AdRequest;
import hipstershop.Demo.AdResponse; import hipstershop.Demo.AdResponse;
@ -24,48 +26,50 @@ import io.grpc.Server;
import io.grpc.ServerBuilder; import io.grpc.ServerBuilder;
import io.grpc.StatusRuntimeException; import io.grpc.StatusRuntimeException;
import io.grpc.health.v1.HealthCheckResponse.ServingStatus; import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
import io.grpc.stub.StreamObserver;
import io.grpc.services.*; import io.grpc.services.*;
import io.grpc.stub.StreamObserver;
import io.opencensus.common.Duration; import io.opencensus.common.Duration;
import io.opencensus.common.Scope;
import io.opencensus.contrib.grpc.metrics.RpcViews; import io.opencensus.contrib.grpc.metrics.RpcViews;
import io.opencensus.exporter.stats.prometheus.PrometheusStatsCollector;
import io.opencensus.exporter.trace.logging.LoggingTraceExporter;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration; import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter; import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.exporter.trace.jaeger.JaegerTraceExporter;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration; import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter; import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter;
import io.opencensus.trace.AttributeValue; import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.Span; import io.opencensus.trace.Span;
import io.opencensus.trace.SpanBuilder;
import io.opencensus.trace.Tracer; import io.opencensus.trace.Tracer;
import io.opencensus.trace.Tracing; import io.opencensus.trace.Tracing;
import io.opencensus.trace.samplers.Samplers;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import org.apache.logging.log4j.Level;
import java.util.logging.Logger; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class AdService { public final class AdService {
private static final Logger logger = Logger.getLogger(AdService.class.getName());
private static final Logger logger = LogManager.getLogger(AdService.class);
private static final Tracer tracer = Tracing.getTracer(); private static final Tracer tracer = Tracing.getTracer();
private int MAX_ADS_TO_SERVE = 2; private static int MAX_ADS_TO_SERVE = 2;
private Server server; private Server server;
private HealthStatusManager healthMgr; private HealthStatusManager healthMgr;
static final AdService service = new AdService(); private static final AdService service = new AdService();
private void start() throws IOException { private void start() throws IOException {
int port = Integer.parseInt(System.getenv("PORT")); int port = Integer.parseInt(System.getenv("PORT"));
healthMgr = new HealthStatusManager(); healthMgr = new HealthStatusManager();
server = ServerBuilder.forPort(port).addService(new AdServiceImpl()) server =
.addService(healthMgr.getHealthService()).build().start(); ServerBuilder.forPort(port)
.addService(new AdServiceImpl())
.addService(healthMgr.getHealthService())
.build()
.start();
logger.info("Ad Service started, listening on " + port); logger.info("Ad Service started, listening on " + port);
Runtime.getRuntime() Runtime.getRuntime()
.addShutdownHook( .addShutdownHook(
@ -88,29 +92,23 @@ public class AdService {
} }
} }
static class AdServiceImpl extends hipstershop.AdServiceGrpc.AdServiceImplBase { private static class AdServiceImpl extends hipstershop.AdServiceGrpc.AdServiceImplBase {
/** /**
* Retrieves ads based on context provided in the request {@code AdRequest}. * Retrieves ads based on context provided in the request {@code AdRequest}.
* *
* @param req the request containing context. * @param req the request containing context.
* @param responseObserver the stream observer which gets notified with the value of * @param responseObserver the stream observer which gets notified with the value of {@code
* {@code AdResponse} * AdResponse}
*/ */
@Override @Override
public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) { public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
AdService service = AdService.getInstance(); AdService service = AdService.getInstance();
Span parentSpan = tracer.getCurrentSpan(); Span span = tracer.getCurrentSpan();
SpanBuilder spanBuilder = try {
tracer
.spanBuilderWithExplicitParent("Retrieve Ads", parentSpan)
.setRecordEvents(true)
.setSampler(Samplers.alwaysSample());
try (Scope scope = spanBuilder.startScopedSpan()) {
Span span = tracer.getCurrentSpan();
span.putAttribute("method", AttributeValue.stringAttributeValue("getAds")); span.putAttribute("method", AttributeValue.stringAttributeValue("getAds"));
List<Ad> ads = new ArrayList<>(); List<Ad> allAds = new ArrayList<>();
logger.info("received ad request (context_words=" + req.getContextKeysCount() + ")"); logger.info("received ad request (context_words=" + req.getContextKeysList() + ")");
if (req.getContextKeysCount() > 0) { if (req.getContextKeysCount() > 0) {
span.addAnnotation( span.addAnnotation(
"Constructing Ads using context", "Constructing Ads using context",
@ -120,47 +118,46 @@ public class AdService {
"Context Keys length", "Context Keys length",
AttributeValue.longAttributeValue(req.getContextKeysCount()))); AttributeValue.longAttributeValue(req.getContextKeysCount())));
for (int i = 0; i < req.getContextKeysCount(); i++) { for (int i = 0; i < req.getContextKeysCount(); i++) {
Ad ad = service.getAdsByKey(req.getContextKeys(i)); Collection<Ad> ads = service.getAdsByCategory(req.getContextKeys(i));
if (ad != null) { allAds.addAll(ads);
ads.add(ad);
}
} }
} else { } else {
span.addAnnotation("No Context provided. Constructing random Ads."); span.addAnnotation("No Context provided. Constructing random Ads.");
ads = service.getDefaultAds(); allAds = service.getRandomAds();
} }
if (ads.isEmpty()) { if (allAds.isEmpty()) {
// Serve default ads. // Serve random ads.
span.addAnnotation("No Ads found based on context. Constructing random Ads."); span.addAnnotation("No Ads found based on context. Constructing random Ads.");
ads = service.getDefaultAds(); allAds = service.getRandomAds();
} }
AdResponse reply = AdResponse.newBuilder().addAllAds(ads).build(); AdResponse reply = AdResponse.newBuilder().addAllAds(allAds).build();
responseObserver.onNext(reply); responseObserver.onNext(reply);
responseObserver.onCompleted(); responseObserver.onCompleted();
} catch (StatusRuntimeException e) { } catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "GetAds Failed", e.getStatus()); logger.log(Level.WARN, "GetAds Failed", e.getStatus());
return; responseObserver.onError(e);
} }
} }
} }
static final HashMap<String, Ad> cacheMap = new HashMap<String, Ad>(); private static final ImmutableListMultimap<String, Ad> adsMap = createAdsMap();
Ad getAdsByKey(String key) { private Collection<Ad> getAdsByCategory(String category) {
return cacheMap.get(key); return adsMap.get(category);
} }
private static final Random random = new Random();
public List<Ad> getDefaultAds() { private List<Ad> getRandomAds() {
List<Ad> ads = new ArrayList<>(MAX_ADS_TO_SERVE); List<Ad> ads = new ArrayList<>(MAX_ADS_TO_SERVE);
Object[] keys = cacheMap.keySet().toArray(); Collection<Ad> allAds = adsMap.values();
for (int i=0; i<MAX_ADS_TO_SERVE; i++) { for (int i = 0; i < MAX_ADS_TO_SERVE; i++) {
ads.add(cacheMap.get(keys[new Random().nextInt(keys.length)])); ads.add(Iterables.get(allAds, random.nextInt(allAds.size())));
} }
return ads; return ads;
} }
public static AdService getInstance() { private static AdService getInstance() {
return service; return service;
} }
@ -171,46 +168,86 @@ public class AdService {
} }
} }
static void initializeAds() { private static ImmutableListMultimap<String, Ad> createAdsMap() {
cacheMap.put("camera", Ad.newBuilder().setRedirectUrl( "/product/2ZYFJ3GM2N") Ad camera =
.setText("Film camera for sale. 50% off.").build()); Ad.newBuilder()
cacheMap.put("bike", Ad.newBuilder().setRedirectUrl("/product/9SIQT8TOJO") .setRedirectUrl("/product/2ZYFJ3GM2N")
.setText("City Bike for sale. 10% off.").build()); .setText("Film camera for sale. 50% off.")
cacheMap.put("kitchen", Ad.newBuilder().setRedirectUrl("/product/1YMWWN1N4O") .build();
.setText("Home Barista kitchen kit for sale. Buy one, get second kit for free").build()); Ad lens =
logger.info("Default Ads initialized"); Ad.newBuilder()
.setRedirectUrl("/product/66VCHSJNUP")
.setText("Vintage camera lens for sale. 20% off.")
.build();
Ad recordPlayer =
Ad.newBuilder()
.setRedirectUrl("/product/0PUK6V6EV0")
.setText("Vintage record player for sale. 30% off.")
.build();
Ad bike =
Ad.newBuilder()
.setRedirectUrl("/product/9SIQT8TOJO")
.setText("City Bike for sale. 10% off.")
.build();
Ad baristaKit =
Ad.newBuilder()
.setRedirectUrl("/product/1YMWWN1N4O")
.setText("Home Barista kitchen kit for sale. Buy one, get second kit for free")
.build();
Ad airPlant =
Ad.newBuilder()
.setRedirectUrl("/product/6E92ZMYYFZ")
.setText("Air plants for sale. Buy two, get third one for free")
.build();
Ad terrarium =
Ad.newBuilder()
.setRedirectUrl("/product/L9ECAV7KIM")
.setText("Terrarium for sale. Buy one, get second one for free")
.build();
return ImmutableListMultimap.<String, Ad>builder()
.putAll("photography", camera, lens)
.putAll("vintage", camera, lens, recordPlayer)
.put("cycling", bike)
.put("cookware", baristaKit)
.putAll("gardening", airPlant, terrarium)
.build();
} }
public static void initStackdriver() { private static void initStackdriver() {
logger.info("Initialize StackDriver"); logger.info("Initialize StackDriver");
// Registers all RPC views.
RpcViews.registerAllViews();
// Registers logging trace exporter.
LoggingTraceExporter.register();
long sleepTime = 10; /* seconds */ long sleepTime = 10; /* seconds */
int maxAttempts = 3; int maxAttempts = 5;
boolean statsExporterRegistered = false;
boolean traceExporterRegistered = false;
for (int i=0; i<maxAttempts; i++) { for (int i = 0; i < maxAttempts; i++) {
try { try {
StackdriverTraceExporter.createAndRegister( if (!traceExporterRegistered) {
StackdriverTraceConfiguration.builder().build()); StackdriverTraceExporter.createAndRegister(
StackdriverStatsExporter.createAndRegister( StackdriverTraceConfiguration.builder().build());
StackdriverStatsConfiguration.builder() traceExporterRegistered = true;
.setExportInterval(Duration.create(15, 0)) }
.build()); if (!statsExporterRegistered) {
StackdriverStatsExporter.createAndRegister(
StackdriverStatsConfiguration.builder()
.setExportInterval(Duration.create(60, 0))
.build());
statsExporterRegistered = true;
}
} catch (Exception e) { } catch (Exception e) {
if (i==(maxAttempts-1)) { if (i == (maxAttempts - 1)) {
logger.log(Level.WARNING, "Failed to register Stackdriver Exporter." + logger.log(
" Tracing and Stats data will not reported to Stackdriver. Error message: " + e Level.WARN,
.toString()); "Failed to register Stackdriver Exporter."
+ " Tracing and Stats data will not reported to Stackdriver. Error message: "
+ e.toString());
} else { } else {
logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds "); logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds ");
try { try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime)); Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime));
} catch (Exception se) { } catch (Exception se) {
logger.log(Level.WARNING, "Exception while sleeping" + se.toString()); logger.log(Level.WARN, "Exception while sleeping" + se.toString());
} }
} }
} }
@ -218,20 +255,40 @@ public class AdService {
logger.info("StackDriver initialization complete."); logger.info("StackDriver initialization complete.");
} }
private static void initJaeger() {
String jaegerAddr = System.getenv("JAEGER_SERVICE_ADDR");
if (jaegerAddr != null && !jaegerAddr.isEmpty()) {
String jaegerUrl = String.format("http://%s/api/traces", jaegerAddr);
// Register Jaeger Tracing.
JaegerTraceExporter.createAndRegister(jaegerUrl, "adservice");
logger.info("Jaeger initialization complete.");
} else {
logger.info("Jaeger initialization disabled.");
}
}
/** Main launches the server from the command line. */ /** Main launches the server from the command line. */
public static void main(String[] args) throws IOException, InterruptedException { public static void main(String[] args) throws IOException, InterruptedException {
// Add final keyword to pass checkStyle. // Registers all RPC views.
/**
* [TODO:rghetia] replace registerAllViews with registerAllGrpcViews.
* registerAllGrpcViews registers new views using new measures however current grpc version records against
* old measures. When new version of grpc (0.19) is release revert back to new. After reverting
* back to new the new measure will not provide any tags (like method). This will create
* some discrepencies when compared grpc measurements in Go services.
*/
RpcViews.registerAllViews();
initializeAds(); new Thread(
new Runnable() {
public void run() {
initStackdriver();
}
})
.start();
new Thread( new Runnable() { // Register Jaeger
public void run(){ initJaeger();
initStackdriver();
}
}).start();
// Register Prometheus exporters and export metrics to a Prometheus HTTPServer.
PrometheusStatsCollector.createAndRegister();
// Start the RPC server. You shouldn't see any output from gRPC before this. // Start the RPC server. You shouldn't see any output from gRPC before this.
logger.info("AdService starting."); logger.info("AdService starting.");
@ -239,4 +296,4 @@ public class AdService {
service.start(); service.start();
service.blockUntilShutdown(); service.blockUntilShutdown();
} }
} }

View file

@ -16,7 +16,6 @@
package hipstershop; package hipstershop;
import hipstershop.Demo.Ad; import hipstershop.Demo.Ad;
import hipstershop.Demo.AdRequest; import hipstershop.Demo.AdRequest;
import hipstershop.Demo.AdResponse; import hipstershop.Demo.AdResponse;
@ -26,33 +25,32 @@ import io.grpc.StatusRuntimeException;
import io.opencensus.common.Duration; import io.opencensus.common.Duration;
import io.opencensus.common.Scope; import io.opencensus.common.Scope;
import io.opencensus.contrib.grpc.metrics.RpcViews; import io.opencensus.contrib.grpc.metrics.RpcViews;
import io.opencensus.exporter.trace.logging.LoggingTraceExporter; import io.opencensus.contrib.grpc.util.StatusConverter;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration; import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter; import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration; import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter; import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter;
import io.opencensus.trace.SpanBuilder; import io.opencensus.trace.Span;
import io.opencensus.trace.Status.CanonicalCode;
import io.opencensus.trace.Tracer; import io.opencensus.trace.Tracer;
import io.opencensus.trace.Tracing; import io.opencensus.trace.Tracing;
import io.opencensus.trace.samplers.Samplers; import io.opencensus.trace.samplers.Samplers;
import java.io.IOException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** A simple client that requests ads from the Ads Service. */ /** A simple client that requests ads from the Ads Service. */
public class AdServiceClient { public class AdServiceClient {
private static final Logger logger = Logger.getLogger(AdServiceClient.class.getName());
private static final Logger logger = LogManager.getLogger(AdServiceClient.class);
private static final Tracer tracer = Tracing.getTracer(); private static final Tracer tracer = Tracing.getTracer();
private final ManagedChannel channel; private final ManagedChannel channel;
private final hipstershop.AdServiceGrpc.AdServiceBlockingStub blockingStub; private final hipstershop.AdServiceGrpc.AdServiceBlockingStub blockingStub;
/** Construct client connecting to Ad Service at {@code host:port}. */ /** Construct client connecting to Ad Service at {@code host:port}. */
public AdServiceClient(String host, int port) { private AdServiceClient(String host, int port) {
this( this(
ManagedChannelBuilder.forAddress(host, port) ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
@ -62,12 +60,12 @@ public class AdServiceClient {
} }
/** Construct client for accessing RouteGuide server using the existing channel. */ /** Construct client for accessing RouteGuide server using the existing channel. */
AdServiceClient(ManagedChannel channel) { private AdServiceClient(ManagedChannel channel) {
this.channel = channel; this.channel = channel;
blockingStub = hipstershop.AdServiceGrpc.newBlockingStub(channel); blockingStub = hipstershop.AdServiceGrpc.newBlockingStub(channel);
} }
public void shutdown() throws InterruptedException { private void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
} }
@ -77,42 +75,42 @@ public class AdServiceClient {
AdRequest request = AdRequest.newBuilder().addContextKeys(contextKey).build(); AdRequest request = AdRequest.newBuilder().addContextKeys(contextKey).build();
AdResponse response; AdResponse response;
SpanBuilder spanBuilder = Span span =
tracer.spanBuilder("AdsClient").setRecordEvents(true).setSampler(Samplers.alwaysSample()); tracer
try (Scope scope = spanBuilder.startScopedSpan()) { .spanBuilder("AdsClient")
.setRecordEvents(true)
.setSampler(Samplers.alwaysSample())
.startSpan();
try (Scope scope = tracer.withSpan(span)) {
tracer.getCurrentSpan().addAnnotation("Getting Ads"); tracer.getCurrentSpan().addAnnotation("Getting Ads");
response = blockingStub.getAds(request); response = blockingStub.getAds(request);
tracer.getCurrentSpan().addAnnotation("Received response from Ads Service."); tracer.getCurrentSpan().addAnnotation("Received response from Ads Service.");
} catch (StatusRuntimeException e) { } catch (StatusRuntimeException e) {
tracer tracer.getCurrentSpan().setStatus(StatusConverter.fromGrpcStatus(e.getStatus()));
.getCurrentSpan() logger.log(Level.WARN, "RPC failed: " + e.getStatus());
.setStatus(
CanonicalCode.valueOf(e.getStatus().getCode().name())
.toStatus()
.withDescription(e.getMessage()));
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return; return;
} finally {
span.end();
} }
for(Ad ads: response.getAdsList()) { for (Ad ads : response.getAdsList()) {
logger.info("Ads: " + ads.getText()); logger.info("Ads: " + ads.getText());
} }
} }
static int getPortOrDefaultFromArgs(String[] args, int index, int defaultPort) { private static int getPortOrDefaultFromArgs(String[] args, int index, int defaultPort) {
int portNumber = defaultPort; int portNumber = defaultPort;
if (index < args.length) { if (index < args.length) {
try { try {
portNumber = Integer.parseInt(args[index]); portNumber = Integer.parseInt(args[index]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.warning( logger.warn(
String.format("Port %s is invalid, use default port %d.", args[index], defaultPort)); String.format("Port %s is invalid, use default port %d.", args[index], defaultPort));
} }
} }
return portNumber; return portNumber;
} }
private static String getStringOrDefaultFromArgs(
static String getStringOrDefaultFromArgs(
String[] args, int index, @Nullable String defaultString) { String[] args, int index, @Nullable String defaultString) {
String s = defaultString; String s = defaultString;
if (index < args.length) { if (index < args.length) {
@ -125,54 +123,46 @@ public class AdServiceClient {
* Ads Service Client main. If provided, the first element of {@code args} is the context key to * Ads Service Client main. If provided, the first element of {@code args} is the context key to
* get the ads from the Ads Service * get the ads from the Ads Service
*/ */
public static void main(String[] args) throws IOException, InterruptedException { public static void main(String[] args) throws InterruptedException {
// Add final keyword to pass checkStyle. // Add final keyword to pass checkStyle.
final String contextKeys = getStringOrDefaultFromArgs(args, 0, "camera"); final String contextKeys = getStringOrDefaultFromArgs(args, 0, "camera");
final String host = getStringOrDefaultFromArgs(args, 1, "localhost"); final String host = getStringOrDefaultFromArgs(args, 1, "localhost");
final int serverPort = getPortOrDefaultFromArgs(args, 2, 9555); final int serverPort = getPortOrDefaultFromArgs(args, 2, 9555);
final String cloudProjectId = getStringOrDefaultFromArgs(args, 3, null);
//final int zPagePort = getPortOrDefaultFromArgs(args, 4, 3001);
// Registers all RPC views. // Registers all RPC views.
RpcViews.registerAllViews(); RpcViews.registerAllGrpcViews();
// Registers logging trace exporter.
LoggingTraceExporter.register();
// Registers Stackdriver exporters. // Registers Stackdriver exporters.
if (cloudProjectId != null) { long sleepTime = 10; /* seconds */
long sleepTime = 10; /* seconds */ int maxAttempts = 3;
int maxAttempts = 3;
for (int i=0; i<maxAttempts; i++) { for (int i = 0; i < maxAttempts; i++) {
try { try {
StackdriverTraceExporter.createAndRegister( StackdriverTraceExporter.createAndRegister(StackdriverTraceConfiguration.builder().build());
StackdriverTraceConfiguration.builder().setProjectId(cloudProjectId).build()); StackdriverStatsExporter.createAndRegister(
StackdriverStatsExporter.createAndRegister( StackdriverStatsConfiguration.builder()
StackdriverStatsConfiguration.builder() .setExportInterval(Duration.create(15, 0))
.setProjectId(cloudProjectId) .build());
.setExportInterval(Duration.create(15, 0)) } catch (Exception e) {
.build()); if (i == (maxAttempts - 1)) {
} catch (Exception e) { logger.log(
if (i==(maxAttempts-1)) { Level.WARN,
logger.log(Level.WARNING, "Failed to register Stackdriver Exporter." + "Failed to register Stackdriver Exporter."
" Tracing and Stats data will not reported to Stackdriver. Error message: " + e + " Tracing and Stats data will not reported to Stackdriver. Error message: "
.toString()); + e.toString());
} else { } else {
logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds"); logger.info("Attempt to register Stackdriver Exporter in " + sleepTime + " seconds");
try { try {
Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime)); Thread.sleep(TimeUnit.SECONDS.toMillis(sleepTime));
} catch (Exception se) { } catch (Exception se) {
logger.log(Level.WARNING, "Exception while sleeping" + e.toString()); logger.log(Level.WARN, "Exception while sleeping" + e.toString());
}
} }
} }
} }
} }
// Register Prometheus exporters and export metrics to a Prometheus HTTPServer. // Register Prometheus exporters and export metrics to a Prometheus HTTPServer.
//PrometheusStatsCollector.createAndRegister(); // PrometheusStatsCollector.createAndRegister();
AdServiceClient client = new AdServiceClient(host, serverPort); AdServiceClient client = new AdServiceClient(host, serverPort);
try { try {

View file

@ -64,6 +64,10 @@ message Product {
string description = 3; string description = 3;
string picture = 4; string picture = 4;
Money price_usd = 5; Money price_usd = 5;
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
repeated string categories = 6;
} }
message ListProductsResponse { message ListProductsResponse {

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<!-- This is a JSON format that can be read by the Stackdriver Logging agent. The trace ID,
span ID, sampling decision, and timestamp are interpreted by Stackdriver. It uses the
special JSON keys that the Stackdriver Logging agent converts to "trace", "spanId",
"traceSampled", and "timestamp" in the Stackdriver LogEntry
(https://cloud.google.com/logging/docs/agent/configuration#special-fields). -->
<JsonLayout compact="true" eventEol="true">
<KeyValuePair key="logging.googleapis.com/trace" value="$${ctx:traceId}"/>
<KeyValuePair key="logging.googleapis.com/spanId" value="$${ctx:spanId}"/>
<KeyValuePair key="logging.googleapis.com/traceSampled" value="$${ctx:traceSampled}"/>
<KeyValuePair key="time" value="$${date:yyyy-MM-dd}T$${date:HH:mm:ss.SSS}Z"/>
</JsonLayout>
</Console>
</Appenders>
<Loggers>
<Logger name="io.grpc.netty" level="INFO"/>
<Logger name="io.netty" level="INFO"/>
<Logger name="sun.net" level="INFO"/>
<Root level="TRACE">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

View file

@ -1,22 +1,28 @@
# cartservice_probe FROM microsoft/dotnet:2.1-sdk-alpine as builder
FROM golang:1.10 as builder
RUN wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \
chmod +x /go/bin/dep
ENV PROJECT github.com/GoogleCloudPlatform/microservices-demo/src/cartservice/probe
WORKDIR /go/src/$PROJECT
COPY probe/Gopkg.* ./
RUN dep ensure --vendor-only -v
COPY ./probe ./
RUN go build -o /cartservice_probe .
# cartservice
FROM gcr.io/google-appengine/aspnetcore:2.1.0
COPY --from=builder /cartservice_probe /cartservice_probe
RUN apt-get update && apt-get install net-tools telnet
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN dotnet restore && \ RUN dotnet restore && \
dotnet build && \ dotnet build && \
dotnet publish dotnet publish -c release -r linux-musl-x64 -o /cartservice
WORKDIR /app/bin/Debug/netcoreapp2.0
ENTRYPOINT ["dotnet", "cartservice.dll", "start"] # cartservice
FROM alpine:3.8
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe
# Dependencies for runtime
# busybox-extras => telnet
RUN apk add --no-cache \
busybox-extras \
libc6-compat \
libunwind \
libuuid \
libgcc \
libstdc++ \
libintl \
icu
WORKDIR /app
COPY --from=builder /cartservice .
ENTRYPOINT ["./cartservice", "start"]

View file

@ -1,5 +1,7 @@
using System; using System;
using System.Threading.Tasks;
using cartservice.interfaces; using cartservice.interfaces;
using Grpc.Core;
using Grpc.Health.V1; using Grpc.Health.V1;
using StackExchange.Redis; using StackExchange.Redis;
using static Grpc.Health.V1.Health; using static Grpc.Health.V1.Health;
@ -11,12 +13,12 @@ namespace cartservice {
this.dependency = dependency; this.dependency = dependency;
} }
public HealthCheckResponse Check (HealthCheckRequest request) { public override Task<HealthCheckResponse> Check(HealthCheckRequest request, ServerCallContext context){
Console.WriteLine ("Checking CartService Health"); Console.WriteLine ("Checking CartService Health");
return new HealthCheckResponse { return Task.FromResult(new HealthCheckResponse {
Status = dependency.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing Status = dependency.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing
}; });
} }
} }
} }

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -10,7 +10,7 @@
<PackageReference Include="Google.Protobuf" Version="3.6.1" /> <PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.5.1" /> <PackageReference Include="Google.Protobuf.Tools" Version="3.5.1" />
<PackageReference Include="grpc" Version="1.12.0" /> <PackageReference Include="grpc" Version="1.12.0" />
<PackageReference Include="Grpc.HealthCheck" Version="1.15.0" /> <PackageReference Include="Grpc.HealthCheck" Version="1.12.0" />
<PackageReference Include="grpc.tools" Version="1.12.0" /> <PackageReference Include="grpc.tools" Version="1.12.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />

View file

@ -1 +0,0 @@
vendor/

View file

@ -1,101 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"trace"
]
revision = "f4c29de78a2a91c00474a2e689954305c350adf9"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "daca94659cb50e9f37c1b834680f2e46358f10b0"
[[projects]]
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"balancer/base",
"balancer/roundrobin",
"codes",
"connectivity",
"credentials",
"encoding",
"encoding/proto",
"grpclog",
"internal",
"internal/backoff",
"internal/channelz",
"internal/envconfig",
"internal/grpcrand",
"internal/transport",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"resolver/dns",
"resolver/passthrough",
"stats",
"status",
"tap"
]
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
version = "v1.14.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "8f3c07df5ebc7dc4d50ca8a6c274eef95f3045d33bbe5f384271fa87166a457e"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,42 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/golang/protobuf"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "golang.org/x/net"
[[constraint]]
name = "google.golang.org/grpc"
version = "1.14.0"
[prune]
go-tests = true
unused-packages = true

View file

@ -1,4 +0,0 @@
# cartservice Probe
This probe makes a call to the cartservice over localhost and exits to verify
liveness of the cartservice.

File diff suppressed because it is too large Load diff

View file

@ -1,51 +0,0 @@
// Copyright 2018 Google LLC
//
// 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.
package main
import (
"context"
"log"
"os"
"time"
pb "github.com/GoogleCloudPlatform/microservices-demo/src/cartservice/probe/genproto"
"google.golang.org/grpc"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
log.Fatal("probe is executed without PORT env var")
}
log.Printf("probe executing on port %q", port)
conn, err := grpc.DialContext(context.TODO(),
"127.0.0.1:"+port,
grpc.WithBlock(),
grpc.WithTimeout(time.Second*3),
grpc.WithInsecure(),
)
if err != nil {
log.Fatalf("probe failed: failed to connect: %+v", err)
}
defer conn.Close()
if _, err := pb.NewCartServiceClient(conn).GetCart(context.TODO(),
&pb.GetCartRequest{UserId: "exec-probe-nonexistinguser"}); err != nil {
log.Fatalf("probe failed: failed to query: %+v", err)
}
log.Println("probe successful")
}

View file

@ -1,4 +1,4 @@
FROM golang:1.10-alpine as builder FROM golang:1.12-alpine as builder
RUN apk add --no-cache ca-certificates git && \ RUN apk add --no-cache ca-certificates git && \
wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \ wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \
chmod +x /go/bin/dep chmod +x /go/bin/dep
@ -15,7 +15,7 @@ RUN go build -gcflags='-N -l' -o /checkoutservice .
FROM alpine as release FROM alpine as release
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe chmod +x /bin/grpc_health_probe
COPY --from=builder /checkoutservice /checkoutservice COPY --from=builder /checkoutservice /checkoutservice

View file

@ -2,27 +2,49 @@
[[projects]] [[projects]]
digest = "1:467af0aad47996b25b838d6f14c8371123a8a76ec239020a6c5894e1f8f60272"
name = "cloud.google.com/go" name = "cloud.google.com/go"
packages = [ packages = [
"compute/metadata", "compute/metadata",
"internal/version", "internal/version",
"monitoring/apiv3", "monitoring/apiv3",
"profiler", "profiler",
"trace/apiv2" "trace/apiv2",
] ]
pruneopts = "UT"
revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad" revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad"
version = "v0.27.0" version = "v0.27.0"
[[projects]] [[projects]]
digest = "1:4b96dcd8534bc6450a922bd16a76360ba3381f0d1daf40abbaec91c053fbfeb5"
name = "contrib.go.opencensus.io/exporter/stackdriver" name = "contrib.go.opencensus.io/exporter/stackdriver"
packages = [ packages = ["."]
".", pruneopts = "UT"
"propagation"
]
revision = "37aa2801fbf0205003e15636096ebf0373510288" revision = "37aa2801fbf0205003e15636096ebf0373510288"
version = "v0.5.0" version = "v0.5.0"
[[projects]] [[projects]]
branch = "master"
digest = "1:1d8f3cb93c42c5652bb509fde29ecdd1feede9334e355e8bbdc0f9f95b40c254"
name = "git.apache.org/thrift.git"
packages = ["lib/go/thrift"]
pruneopts = "UT"
revision = "a5df39032ca206e2e6a9ec975147e81746d9a255"
source = "github.com/apache/thrift"
[[projects]]
branch = "master"
digest = "1:f6bdf3d8d3cbb2f98d3ebaa66b3ac25166a06830027ece7d592d9ea09dedf504"
name = "github.com/GoogleCloudPlatform/microservices-demo"
packages = [
"src/checkoutservice/genproto",
"src/checkoutservice/money",
]
pruneopts = "UT"
revision = "33ca3b63d82698035ffc1230dcb650885a005197"
[[projects]]
digest = "1:72856926f8208767b837bf51e3373f49139f61889b67dc7fd3c2a0fd711e3f7a"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = [ packages = [
"proto", "proto",
@ -33,41 +55,62 @@
"ptypes/empty", "ptypes/empty",
"ptypes/struct", "ptypes/struct",
"ptypes/timestamp", "ptypes/timestamp",
"ptypes/wrappers" "ptypes/wrappers",
] ]
pruneopts = "UT"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0" version = "v1.2.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:089d56c0adb79140365b5c86815ce97233986da6f3a525c6b706773e4b83876f"
name = "github.com/google/pprof" name = "github.com/google/pprof"
packages = ["profile"] packages = ["profile"]
revision = "e027b505a088ac3c68c339a1d7ce7724bf34538b" pruneopts = "UT"
revision = "985cf9b4fdd2b479e4563ec57352005f69d5f9f6"
[[projects]] [[projects]]
digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8"
name = "github.com/google/uuid" name = "github.com/google/uuid"
packages = ["."] packages = ["."]
revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" pruneopts = "UT"
version = "v1.0.0" revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8"
version = "v1.1.0"
[[projects]] [[projects]]
digest = "1:cd9864c6366515827a759931746738ede6079faa08df9c584596370d6add135c"
name = "github.com/googleapis/gax-go" name = "github.com/googleapis/gax-go"
packages = ["."] packages = [
revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" ".",
version = "v2.0.0" "v2",
]
pruneopts = "UT"
revision = "c8a15bac9b9fe955bd9f900272f9a306465d28cf"
version = "v2.0.3"
[[projects]] [[projects]]
digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc" digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8"
name = "github.com/konsorten/go-windows-terminal-sequences"
packages = ["."]
pruneopts = "UT"
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
version = "v1.0.1"
[[projects]]
digest = "1:87c2e02fb01c27060ccc5ba7c5a407cc91147726f8f40b70cceeedbc52b1f3a8"
name = "github.com/sirupsen/logrus" name = "github.com/sirupsen/logrus"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2" revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5"
version = "v1.0.6" version = "v1.3.0"
[[projects]] [[projects]]
digest = "1:a5154dfd6b37bef5a3eab759e13296348e639dc8c7604f538368167782b08ccd"
name = "go.opencensus.io" name = "go.opencensus.io"
packages = [ packages = [
".", ".",
"exporter/jaeger",
"exporter/jaeger/internal/gen-go/jaeger",
"internal", "internal",
"internal/tagencoding", "internal/tagencoding",
"plugin/ocgrpc", "plugin/ocgrpc",
@ -80,21 +123,23 @@
"trace", "trace",
"trace/internal", "trace/internal",
"trace/propagation", "trace/propagation",
"trace/tracestate" "trace/tracestate",
] ]
pruneopts = "UT"
revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89"
version = "v0.16.0" version = "v0.16.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ssh/terminal"] packages = ["ssh/terminal"]
pruneopts = "UT" pruneopts = "UT"
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:8ecb828bb550a8c6b7d75b8261a42c369461311616ebe5451966d067f5f993bf"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"context", "context",
@ -104,35 +149,46 @@
"http2/hpack", "http2/hpack",
"idna", "idna",
"internal/timeseries", "internal/timeseries",
"trace" "trace",
] ]
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" pruneopts = "UT"
revision = "1e06a53dbb7e2ed46e91183f219db23c6943c532"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:23443edff0740e348959763085df98400dcfd85528d7771bb0ce9f5f2754ff4a"
name = "golang.org/x/oauth2" name = "golang.org/x/oauth2"
packages = [ packages = [
".", ".",
"google", "google",
"internal", "internal",
"jws", "jws",
"jwt" "jwt",
] ]
revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" pruneopts = "UT"
revision = "d668ce993890a79bda886613ee587a69dd5da7a6"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:75515eedc0dc2cb0b40372008b616fa2841d831c63eedd403285ff286c593295"
name = "golang.org/x/sync" name = "golang.org/x/sync"
packages = ["semaphore"] packages = ["semaphore"]
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" pruneopts = "UT"
revision = "37e7f081c4d4c64e13b10787722085407fe5d15f"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:12ef3ef293a3a3930a82e5f38a3c45a1b03a9ed72dedc192d90e89d59b1f13a5"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix"] packages = [
revision = "1561086e645b2809fb9f8a1e2a38160bf8d53bf4" "unix",
"windows",
]
pruneopts = "UT"
revision = "7fbe1cd0fcc20051e1fcb87fbabec4a1bacaaeba"
[[projects]] [[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text" name = "golang.org/x/text"
packages = [ packages = [
"collate", "collate",
@ -148,13 +204,15 @@
"unicode/bidi", "unicode/bidi",
"unicode/cldr", "unicode/cldr",
"unicode/norm", "unicode/norm",
"unicode/rangetable" "unicode/rangetable",
] ]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0" version = "v0.3.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:b74a0ae8b7755bf9cdadead4dea674d76517cd2fea7bd482a6a46bb6b3ce085b"
name = "google.golang.org/api" name = "google.golang.org/api"
packages = [ packages = [
"googleapi/transport", "googleapi/transport",
@ -164,11 +222,14 @@
"support/bundler", "support/bundler",
"transport", "transport",
"transport/grpc", "transport/grpc",
"transport/http" "transport/http",
"transport/http/internal/propagation",
] ]
revision = "19ff8768a5c0b8e46ea281065664787eefc24121" pruneopts = "UT"
revision = "032faecc3e7e2c445ec37a1b1ec4e5a3016d96f2"
[[projects]] [[projects]]
digest = "1:c4eaa5f79d36f76ef4bd0c4f96e36bc1b7b5a359528d1267f0cb7a5d58b7b5bb"
name = "google.golang.org/appengine" name = "google.golang.org/appengine"
packages = [ packages = [
".", ".",
@ -182,13 +243,15 @@
"internal/socket", "internal/socket",
"internal/urlfetch", "internal/urlfetch",
"socket", "socket",
"urlfetch" "urlfetch",
] ]
revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06" pruneopts = "UT"
version = "v1.2.0" revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.4.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3552e7267a98c605e6cbfe6b03c34589265ab72e6078b95fb2e10e0cb44f5cc8"
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
packages = [ packages = [
"googleapis/api/annotations", "googleapis/api/annotations",
@ -201,11 +264,13 @@
"googleapis/monitoring/v3", "googleapis/monitoring/v3",
"googleapis/rpc/errdetails", "googleapis/rpc/errdetails",
"googleapis/rpc/status", "googleapis/rpc/status",
"protobuf/field_mask" "protobuf/field_mask",
] ]
revision = "c3f76f3b92d1ffa4c58a9ff842a58b8877655e0f" pruneopts = "UT"
revision = "ae2f86662275e140f395167f1dab7081a5bd5fa8"
[[projects]] [[projects]]
digest = "1:6497ab07ec89179db8d5a563d33635be04ceffaa29007a3ae74b9f15f4d3068e"
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
packages = [ packages = [
".", ".",
@ -235,14 +300,32 @@
"resolver/passthrough", "resolver/passthrough",
"stats", "stats",
"status", "status",
"tap" "tap",
] ]
pruneopts = "UT"
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
version = "v1.14.0" version = "v1.14.0"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "0c103e5e32d8ecdd70048b6d91e02b910e30bad54f06b230a9664f585433a669" input-imports = [
"cloud.google.com/go/profiler",
"contrib.go.opencensus.io/exporter/stackdriver",
"github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/genproto",
"github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/money",
"github.com/golang/protobuf/proto",
"github.com/google/uuid",
"github.com/sirupsen/logrus",
"go.opencensus.io/exporter/jaeger",
"go.opencensus.io/plugin/ocgrpc",
"go.opencensus.io/stats/view",
"go.opencensus.io/trace",
"golang.org/x/net/context",
"google.golang.org/grpc",
"google.golang.org/grpc/codes",
"google.golang.org/grpc/health/grpc_health_v1",
"google.golang.org/grpc/status",
]
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,7 @@ import (
"contrib.go.opencensus.io/exporter/stackdriver" "contrib.go.opencensus.io/exporter/stackdriver"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.opencensus.io/exporter/jaeger"
"go.opencensus.io/plugin/ocgrpc" "go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view" "go.opencensus.io/stats/view"
"go.opencensus.io/trace" "go.opencensus.io/trace"
@ -98,7 +99,30 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
func initJaegerTracing() {
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
if svcAddr == "" {
log.Info("jaeger initialization disabled.")
return
}
// Register the Jaeger exporter to be able to retrieve
// the collected spans.
exporter, err := jaeger.NewExporter(jaeger.Options{
Endpoint: fmt.Sprintf("http://%s", svcAddr),
Process: jaeger.Process{
ServiceName: "checkoutservice",
},
})
if err != nil {
log.Fatal(err)
}
trace.RegisterExporter(exporter)
log.Info("jaeger initialization completed.")
}
func initStats(exporter *stackdriver.Exporter) { func initStats(exporter *stackdriver.Exporter) {
view.SetReportingPeriod(60 * time.Second)
view.RegisterExporter(exporter) view.RegisterExporter(exporter)
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil { if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
log.Warn("Error registering default server views") log.Warn("Error registering default server views")
@ -107,7 +131,7 @@ func initStats(exporter *stackdriver.Exporter) {
} }
} }
func initTracing() { func initStackDriverTracing() {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.
for i := 1; i <= 3; i++ { for i := 1; i <= 3; i++ {
@ -116,7 +140,6 @@ func initTracing() {
log.Infof("failed to initialize stackdriver exporter: %+v", err) log.Infof("failed to initialize stackdriver exporter: %+v", err)
} else { } else {
trace.RegisterExporter(exporter) trace.RegisterExporter(exporter)
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
log.Info("registered stackdriver tracing") log.Info("registered stackdriver tracing")
// Register the views to collect server stats. // Register the views to collect server stats.
@ -130,6 +153,11 @@ func initTracing() {
log.Warn("could not initialize stackdriver exporter after retrying, giving up") log.Warn("could not initialize stackdriver exporter after retrying, giving up")
} }
func initTracing() {
initJaegerTracing()
initStackDriverTracing()
}
func initProfiling(service, version string) { func initProfiling(service, version string) {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.

View file

@ -1,10 +1,32 @@
FROM node:8 FROM node:8-alpine as base
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \
FROM base as builder
# Some packages (e.g. @google-cloud/profiler) require additional
# deps for post-install scripts
RUN apk add --update --no-cache \
python \
make \
g++
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
FROM base
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe chmod +x /bin/grpc_health_probe
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY . . COPY . .
EXPOSE 7000 EXPOSE 7000
CMD [ "node", "server.js" ]
ENTRYPOINT [ "node", "server.js" ]

View file

@ -20,6 +20,7 @@ require('@google-cloud/trace-agent').start();
const path = require('path'); const path = require('path');
const grpc = require('grpc'); const grpc = require('grpc');
const leftPad = require('left-pad'); const leftPad = require('left-pad');
const pino = require('pino');
const PROTO_PATH = path.join(__dirname, './proto/demo.proto'); const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
const PORT = 7000; const PORT = 7000;
@ -28,6 +29,13 @@ const shopProto = grpc.load(PROTO_PATH).hipstershop;
const client = new shopProto.CurrencyService(`localhost:${PORT}`, const client = new shopProto.CurrencyService(`localhost:${PORT}`,
grpc.credentials.createInsecure()); grpc.credentials.createInsecure());
const logger = pino({
name: 'currencyservice-client',
messageKey: 'message',
changeLevelName: 'severity',
useLevelLabels: true
});
const request = { const request = {
from: { from: {
currency_code: 'CHF', currency_code: 'CHF',
@ -43,16 +51,16 @@ function _moneyToString (m) {
client.getSupportedCurrencies({}, (err, response) => { client.getSupportedCurrencies({}, (err, response) => {
if (err) { if (err) {
console.error(`Error in getSupportedCurrencies: ${err}`); logger.error(`Error in getSupportedCurrencies: ${err}`);
} else { } else {
console.log(`Currency codes: ${response.currency_codes}`); logger.info(`Currency codes: ${response.currency_codes}`);
} }
}); });
client.convert(request, (err, response) => { client.convert(request, (err, response) => {
if (err) { if (err) {
console.error(`Error in convert: ${err}`); logger.error(`Error in convert: ${err}`);
} else { } else {
console.log(`Convert: ${_moneyToString(request.from)} to ${_moneyToString(response)}`); logger.log(`Convert: ${_moneyToString(request.from)} to ${_moneyToString(response)}`);
} }
}); });

View file

@ -0,0 +1,35 @@
{
"EUR": "1.0",
"USD": "1.1305",
"JPY": "126.40",
"BGN": "1.9558",
"CZK": "25.592",
"DKK": "7.4609",
"GBP": "0.85970",
"HUF": "315.51",
"PLN": "4.2996",
"RON": "4.7463",
"SEK": "10.5375",
"CHF": "1.1360",
"ISK": "136.80",
"NOK": "9.8040",
"HRK": "7.4210",
"RUB": "74.4208",
"TRY": "6.1247",
"AUD": "1.6072",
"BRL": "4.2682",
"CAD": "1.5128",
"CNY": "7.5857",
"HKD": "8.8743",
"IDR": "15999.40",
"ILS": "4.0875",
"INR": "79.4320",
"KRW": "1275.05",
"MXN": "21.7999",
"MYR": "4.6289",
"NZD": "1.6679",
"PHP": "59.083",
"SGD": "1.5349",
"THB": "36.012",
"ZAR": "16.0583"
}

View file

@ -663,16 +663,36 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
}, },
"fast-json-parse": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz",
"integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw=="
},
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
}, },
"fast-redact": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-1.2.0.tgz",
"integrity": "sha512-k/uSk9PtFmvYx0m7bRk5B2gZChQk4euWhrn7Mf3vYSmwZBLh7cGNuMuc/vhH1MKMPyVJMMtl9oMwPnwlKqs7CQ=="
},
"fast-safe-stringify": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
"integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg=="
},
"findit2": { "findit2": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
"integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY="
}, },
"flatstr": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.8.tgz",
"integrity": "sha512-YXblbv/vc1zuVVUtnKl1hPqqk7TalZCppnKE7Pr8FI/Rp48vzckS/4SJ4Y9O9RNiI82Vcw/FydmtqdQOg1Dpqw=="
},
"follow-redirects": { "follow-redirects": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.1.tgz",
@ -1570,6 +1590,26 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
}, },
"pino": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/pino/-/pino-5.6.2.tgz",
"integrity": "sha512-JVMYqJkE58b2u5+t85zJLbUDOhoWDjEQqrRY1eQzuR4Ub9RIyUSQJms4deT8Joy+C/QIdrrie7NffgCm+ao9xw==",
"requires": {
"fast-json-parse": "^1.0.3",
"fast-redact": "^1.2.0",
"fast-safe-stringify": "^2.0.6",
"flatstr": "^1.0.5",
"pino-std-serializers": "^2.2.1",
"pump": "^3.0.0",
"quick-format-unescaped": "^3.0.0",
"sonic-boom": "^0.6.1"
}
},
"pino-std-serializers": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.2.1.tgz",
"integrity": "sha512-QqL7kkF7eMCpFG4hpZD8UPQga/kxkkh3E62HzMzTIL4OQyijyisAnBL8msBEAml8xcb/ioGhH7UUzGxuHqczhQ=="
},
"pretty-ms": { "pretty-ms": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz",
@ -1599,6 +1639,15 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
}, },
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"punycode": { "punycode": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@ -1609,6 +1658,11 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
}, },
"quick-format-unescaped": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.0.tgz",
"integrity": "sha512-XmIOc07VM2kPm6m3j/U6jgxyUgDm2Rgh2c1PPy0JUHoQRdoh86hOym0bHyF6G1T6sn+N5lildhvl/T59H5KVyA=="
},
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@ -1706,6 +1760,14 @@
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz",
"integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag==" "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag=="
}, },
"sonic-boom": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.6.1.tgz",
"integrity": "sha512-3qx6XXDeG+hPNa+jla1H6BMBLcjLl8L8NRERLVeIf/EuPqoqmq4K8owG29Xu7OypT/7/YT/0uKW6YitsKA+nLQ==",
"requires": {
"flatstr": "^1.0.5"
}
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",

View file

@ -17,6 +17,7 @@
"google-protobuf": "^3.0.0", "google-protobuf": "^3.0.0",
"grpc": "^1.0.0", "grpc": "^1.0.0",
"left-pad": "^1.3.0", "left-pad": "^1.3.0",
"pino": "^5.6.2",
"request": "^2.87.0", "request": "^2.87.0",
"xml2js": "^0.4.19" "xml2js": "^0.4.19"
}, },

View file

@ -64,6 +64,10 @@ message Product {
string description = 3; string description = 3;
string picture = 4; string picture = 4;
Money price_usd = 5; Money price_usd = 5;
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
repeated string categories = 6;
} }
message ListProductsResponse { message ListProductsResponse {
@ -218,18 +222,18 @@ message PlaceOrderResponse {
OrderResult order = 1; OrderResult order = 1;
} }
// ------------Ads service------------------ // ------------Ad service------------------
service AdsService { service AdService {
rpc GetAds(AdsRequest) returns (AdsResponse) {} rpc GetAds(AdRequest) returns (AdResponse) {}
} }
message AdsRequest { message AdRequest {
// List of important key words from the current page describing the context. // List of important key words from the current page describing the context.
repeated string context_keys = 1; repeated string context_keys = 1;
} }
message AdsResponse { message AdResponse {
repeated Ad ads = 1; repeated Ad ads = 1;
} }

View file

@ -30,19 +30,24 @@ require('@google-cloud/debug-agent').start({
const path = require('path'); const path = require('path');
const grpc = require('grpc'); const grpc = require('grpc');
const request = require('request'); const pino = require('pino');
const xml2js = require('xml2js');
const protoLoader = require('@grpc/proto-loader'); const protoLoader = require('@grpc/proto-loader');
const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto'); const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto');
const HEALTH_PROTO_PATH = path.join(__dirname, './proto/grpc/health/v1/health.proto'); const HEALTH_PROTO_PATH = path.join(__dirname, './proto/grpc/health/v1/health.proto');
const PORT = 7000; const PORT = 7000;
const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
const shopProto = _loadProto(MAIN_PROTO_PATH).hipstershop; const shopProto = _loadProto(MAIN_PROTO_PATH).hipstershop;
const healthProto = _loadProto(HEALTH_PROTO_PATH).grpc.health.v1; const healthProto = _loadProto(HEALTH_PROTO_PATH).grpc.health.v1;
const logger = pino({
name: 'currencyservice-server',
messageKey: 'message',
changeLevelName: 'severity',
useLevelLabels: true
});
/** /**
* Helper function that loads a protobuf file. * Helper function that loads a protobuf file.
*/ */
@ -61,36 +66,12 @@ function _loadProto (path) {
} }
/** /**
* Helper function that gets currency data from an XML webpage * Helper function that gets currency data from a stored JSON file
* Uses public data from European Central Bank * Uses public data from European Central Bank
*/ */
let _data;
function _getCurrencyData (callback) { function _getCurrencyData (callback) {
if (!_data) { const data = require('./data/currency_conversion.json');
console.log('Fetching currency data...'); callback(data);
request(DATA_URL, (err, res) => {
if (err) {
throw new Error(`Error getting data: ${err}`);
}
const body = res.body.split('\n').slice(7, -2).join('\n');
xml2js.parseString(body, (err, resJs) => {
if (err) {
throw new Error(`Error parsing HTML: ${err}`);
}
const array = resJs['Cube']['Cube'].map(x => x['$']);
const results = array.reduce((acc, x) => {
acc[x['currency']] = x['rate'];
return acc;
}, { 'EUR': '1.0' });
_data = results;
callback(_data);
});
});
} else {
callback(_data);
}
} }
/** /**
@ -108,7 +89,7 @@ function _carry (amount) {
* Lists the supported currencies * Lists the supported currencies
*/ */
function getSupportedCurrencies (call, callback) { function getSupportedCurrencies (call, callback) {
console.log('Getting supported currencies...'); logger.info('Getting supported currencies...');
_getCurrencyData((data) => { _getCurrencyData((data) => {
callback(null, {currency_codes: Object.keys(data)}); callback(null, {currency_codes: Object.keys(data)});
}); });
@ -118,7 +99,7 @@ function getSupportedCurrencies (call, callback) {
* Converts between currencies * Converts between currencies
*/ */
function convert (call, callback) { function convert (call, callback) {
console.log('received conversion request'); logger.info('received conversion request');
try { try {
_getCurrencyData((data) => { _getCurrencyData((data) => {
const request = call.request; const request = call.request;
@ -142,12 +123,11 @@ function convert (call, callback) {
result.nanos = Math.floor(result.nanos); result.nanos = Math.floor(result.nanos);
result.currency_code = request.to_code; result.currency_code = request.to_code;
console.log(`conversion request successful`); logger.info(`conversion request successful`);
callback(null, result); callback(null, result);
}); });
} catch (err) { } catch (err) {
console.error('conversion request failed.'); logger.error(`conversion request failed: ${err}`);
console.error(err);
callback(err.message); callback(err.message);
} }
} }
@ -164,7 +144,7 @@ function check (call, callback) {
* CurrencyConverter service at the sample server port * CurrencyConverter service at the sample server port
*/ */
function main () { function main () {
console.log(`Starting gRPC server on port ${PORT}...`); logger.info(`Starting gRPC server on port ${PORT}...`);
const server = new grpc.Server(); const server = new grpc.Server();
server.addService(shopProto.CurrencyService.service, {getSupportedCurrencies, convert}); server.addService(shopProto.CurrencyService.service, {getSupportedCurrencies, convert});
server.addService(healthProto.Health.service, {check}); server.addService(healthProto.Health.service, {check});

View file

@ -1,27 +1,36 @@
FROM python:3-slim as base
FROM base as builder
# Use the grpc.io provided Python image as the base image RUN apt-get -qq update \
FROM grpc/python:1.0 && apt-get install -y --no-install-recommends \
g++ \
&& rm -rf /var/lib/apt/lists/*
# download the grpc health probe # get packages
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ COPY requirements.txt .
RUN pip install -r requirements.txt
FROM base as final
# Enable unbuffered logging
ENV PYTHONUNBUFFERED=1
RUN apt-get -qq update \
&& apt-get install -y --no-install-recommends \
wget
# Download the grpc health probe
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe chmod +x /bin/grpc_health_probe
# show python logs as they occur
ENV PYTHONUNBUFFERED=0
# install pip for python3
RUN apt-get -qqy update && \
apt-get -qqy install python3-pip
# get packages
WORKDIR /email_server WORKDIR /email_server
COPY requirements.txt .
RUN pip3 install -r requirements.txt # Grab packages from builder
COPY --from=builder /usr/local/lib/python3.7/ /usr/local/lib/python3.7/
# Add the application # Add the application
COPY . . COPY . .
EXPOSE 8080 EXPOSE 8080
ENTRYPOINT [ "python3", "email_server.py" ] ENTRYPOINT [ "python", "email_server.py" ]

View file

@ -19,30 +19,33 @@ import grpc
import demo_pb2 import demo_pb2
import demo_pb2_grpc import demo_pb2_grpc
# from opencensus.trace.tracer import Tracer from logger import getJSONLogger
# from opencensus.trace.exporters import stackdriver_exporter logger = getJSONLogger('emailservice-client')
# from opencensus.trace.ext.grpc import client_interceptor
# try: from opencensus.trace.tracer import Tracer
# exporter = stackdriver_exporter.StackdriverExporter() from opencensus.trace.exporters import stackdriver_exporter
# tracer = Tracer(exporter=exporter) from opencensus.trace.ext.grpc import client_interceptor
# tracer_interceptor = client_interceptor.OpenCensusClientInterceptor(tracer, host_port='0.0.0.0:8080')
# except: try:
# tracer_interceptor = client_interceptor.OpenCensusClientInterceptor() exporter = stackdriver_exporter.StackdriverExporter()
tracer = Tracer(exporter=exporter)
tracer_interceptor = client_interceptor.OpenCensusClientInterceptor(tracer, host_port='0.0.0.0:8080')
except:
tracer_interceptor = client_interceptor.OpenCensusClientInterceptor()
def send_confirmation_email(email, order): def send_confirmation_email(email, order):
channel = grpc.insecure_channel('0.0.0.0:8080') channel = grpc.insecure_channel('0.0.0.0:8080')
# channel = grpc.intercept_channel(channel, tracer_interceptor) channel = grpc.intercept_channel(channel, tracer_interceptor)
stub = demo_pb2_grpc.EmailServiceStub(channel) stub = demo_pb2_grpc.EmailServiceStub(channel)
try: try:
response = stub.SendOrderConfirmation(demo_pb2.SendOrderConfirmationRequest( response = stub.SendOrderConfirmation(demo_pb2.SendOrderConfirmationRequest(
email = email, email = email,
order = order order = order
)) ))
print('Request sent.') logger.info('Request sent.')
except grpc.RpcError as err: except grpc.RpcError as err:
print(err.details()) logger.error(err.details())
print('{}, {}'.format(err.code().name, err.code().value)) logger.error('{}, {}'.format(err.code().name, err.code().value))
if __name__ == '__main__': if __name__ == '__main__':
print('Client for email service.') logger.info('Client for email service.')

View file

@ -28,19 +28,19 @@ import demo_pb2_grpc
from grpc_health.v1 import health_pb2 from grpc_health.v1 import health_pb2
from grpc_health.v1 import health_pb2_grpc from grpc_health.v1 import health_pb2_grpc
# from opencensus.trace.ext.grpc import server_interceptor from opencensus.trace.exporters import stackdriver_exporter
# from opencensus.trace.samplers import always_on from opencensus.trace.ext.grpc import server_interceptor
# from opencensus.trace.exporters import stackdriver_exporter from opencensus.trace.samplers import always_on
# from opencensus.trace.exporters import print_exporter
# import googleclouddebugger # import googleclouddebugger
import googlecloudprofiler
# try: try:
# sampler = always_on.AlwaysOnSampler() sampler = always_on.AlwaysOnSampler()
# exporter = stackdriver_exporter.StackdriverExporter() exporter = stackdriver_exporter.StackdriverExporter()
# tracer_interceptor = server_interceptor.OpenCensusServerInterceptor(sampler, exporter) tracer_interceptor = server_interceptor.OpenCensusServerInterceptor(sampler, exporter)
# except: except:
# tracer_interceptor = server_interceptor.OpenCensusServerInterceptor() tracer_interceptor = server_interceptor.OpenCensusServerInterceptor()
# try: # try:
# googleclouddebugger.enable( # googleclouddebugger.enable(
@ -50,6 +50,9 @@ from grpc_health.v1 import health_pb2_grpc
# except: # except:
# pass # pass
from logger import getJSONLogger
logger = getJSONLogger('emailservice-server')
# Loads confirmation email template from file # Loads confirmation email template from file
env = Environment( env = Environment(
loader=FileSystemLoader('templates'), loader=FileSystemLoader('templates'),
@ -78,14 +81,14 @@ class EmailService(BaseEmailService):
"from": { "from": {
"address_spec": from_address, "address_spec": from_address,
}, },
"to": [{ "to": [{
"address_spec": email_address "address_spec": email_address
}], }],
"subject": "Your Confirmation Email", "subject": "Your Confirmation Email",
"html_body": content "html_body": content
} }
) )
print("Message sent: {}".format(response.rfc822_message_id)) logger.info("Message sent: {}".format(response.rfc822_message_id))
def SendOrderConfirmation(self, request, context): def SendOrderConfirmation(self, request, context):
email = request.email email = request.email
@ -95,7 +98,7 @@ class EmailService(BaseEmailService):
confirmation = template.render(order = order) confirmation = template.render(order = order)
except TemplateError as err: except TemplateError as err:
context.set_details("An error occurred when preparing the confirmation mail.") context.set_details("An error occurred when preparing the confirmation mail.")
print(err.message) logger.error(err.message)
context.set_code(grpc.StatusCode.INTERNAL) context.set_code(grpc.StatusCode.INTERNAL)
return demo_pb2.Empty() return demo_pb2.Empty()
@ -111,7 +114,7 @@ class EmailService(BaseEmailService):
class DummyEmailService(BaseEmailService): class DummyEmailService(BaseEmailService):
def SendOrderConfirmation(self, request, context): def SendOrderConfirmation(self, request, context):
print('A request to send order confirmation email to {} has been received.'.format(request.email)) logger.info('A request to send order confirmation email to {} has been received.'.format(request.email))
return demo_pb2.Empty() return demo_pb2.Empty()
class HealthCheck(): class HealthCheck():
@ -120,7 +123,8 @@ class HealthCheck():
status=health_pb2.HealthCheckResponse.SERVING) status=health_pb2.HealthCheckResponse.SERVING)
def start(dummy_mode): def start(dummy_mode):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))#, interceptors=(tracer_interceptor,)) server = grpc.server(futures.ThreadPoolExecutor(max_workers=10),
interceptors=(tracer_interceptor,))
service = None service = None
if dummy_mode: if dummy_mode:
service = DummyEmailService() service = DummyEmailService()
@ -131,7 +135,7 @@ def start(dummy_mode):
health_pb2_grpc.add_HealthServicer_to_server(service, server) health_pb2_grpc.add_HealthServicer_to_server(service, server)
port = os.environ.get('PORT', "8080") port = os.environ.get('PORT', "8080")
print("listening on port: "+port) logger.info("listening on port: "+port)
server.add_insecure_port('[::]:'+port) server.add_insecure_port('[::]:'+port)
server.start() server.start()
try: try:
@ -140,7 +144,41 @@ def start(dummy_mode):
except KeyboardInterrupt: except KeyboardInterrupt:
server.stop(0) server.stop(0)
def initStackdriverProfiling():
project_id = None
try:
project_id = os.environ["GCP_PROJECT_ID"]
except KeyError:
# Environment variable not set
pass
for retry in range(1,4):
try:
if project_id:
googlecloudprofiler.start(service='email_server', service_version='1.0.0', verbose=0, project_id=project_id)
else:
googlecloudprofiler.start(service='email_server', service_version='1.0.0', verbose=0)
logger.info("Successfully started Stackdriver Profiler.")
return
except (BaseException) as exc:
logger.info("Unable to start Stackdriver Profiler Python agent. " + str(exc))
if (retry < 4):
logger.info("Sleeping %d to retry initializing Stackdriver Profiler"%(retry*10))
time.sleep (1)
else:
logger.warning("Could not initialize Stackdriver Profiler after retrying, giving up")
return
if __name__ == '__main__': if __name__ == '__main__':
print('starting the email service in dummy mode.') logger.info('starting the email service in dummy mode.')
try:
enable_profiler = os.environ["ENABLE_PROFILER"]
if enable_profiler != "1":
raise KeyError()
else:
initStackdriverProfiling()
except KeyError:
logger.info("Skipping Stackdriver Profiler Python agent initialization. Set environment variable ENABLE_PROFILER=1 to enable.")
start(dummy_mode = True) start(dummy_mode = True)

View file

@ -0,0 +1,40 @@
#!/usr/bin/python
#
# Copyright 2018 Google LLC
#
# 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.
import logging
import sys
from pythonjsonlogger import jsonlogger
# TODO(yoshifumi) this class is duplicated since other Python services are
# not sharing the modules for logging.
class CustomJsonFormatter(jsonlogger.JsonFormatter):
def add_fields(self, log_record, record, message_dict):
super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict)
if not log_record.get('timestamp'):
log_record['timestamp'] = record.created
if log_record.get('severity'):
log_record['severity'] = log_record['severity'].upper()
else:
log_record['severity'] = record.levelname
def getJSONLogger(name):
logger = logging.getLogger(name)
handler = logging.StreamHandler(sys.stdout)
formatter = CustomJsonFormatter('(timestamp) (severity) (name) (message)')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return logger

View file

@ -0,0 +1,7 @@
google-api-core==1.6.0
grpcio-health-checking==1.12.1
grpcio==1.16.1
jinja2==2.10
opencensus[stackdriver]==0.1.10
python-json-logger==0.1.9
google-cloud-profiler==1.0.8

View file

@ -1,31 +1,34 @@
cachetools==2.1.0 #
certifi==2018.4.16 # This file is autogenerated by pip-compile
chardet==3.0.4 # To update, run:
cryptography==1.7.1 #
google-api-core==1.2.1 # pip-compile --output-file requirements.txt requirements.in
google-auth==1.5.0 #
google-cloud-core==0.28.1 cachetools==3.0.0 # via google-auth
google-cloud-trace==0.19.0 certifi==2018.11.29 # via requests
googleapis-common-protos==1.5.3 chardet==3.0.4 # via requests
grpc-google-iam-v1==0.11.4 google-api-core[grpc]==1.6.0
grpcio==1.12.1 google-api-python-client==1.7.8 # via google-cloud-profiler
grpcio-health-checking==1.14.1 google-auth-httplib2==0.0.3 # via google-api-python-client, google-cloud-profiler
grpcio-tools==1.12.1 google-auth==1.6.2 # via google-api-core, google-api-python-client, google-auth-httplib2, google-cloud-profiler
idna==2.7 google-cloud-core==0.29.1 # via google-cloud-trace
Jinja2==2.10 google-cloud-profiler==1.0.8
keyring==10.1 google-cloud-trace==0.20.2 # via opencensus
keyrings.alt==1.3 googleapis-common-protos==1.5.5 # via google-api-core
MarkupSafe==1.0 grpcio-health-checking==1.12.1
opencensus==0.1.5 grpcio==1.16.1
protobuf==3.6.0 httplib2==0.12.1 # via google-api-python-client, google-auth-httplib2
pyasn1==0.4.3 idna==2.8 # via requests
pyasn1-modules==0.2.2 jinja2==2.10
pycrypto==2.6.1 markupsafe==1.1.0 # via jinja2
pygobject==3.22.0 opencensus[stackdriver]==0.1.10
pytz==2018.5 protobuf==3.6.1 # via google-api-core, google-cloud-profiler, googleapis-common-protos, grpcio-health-checking
pyxdg==0.25 pyasn1-modules==0.2.3 # via google-auth
requests==2.19.1 pyasn1==0.4.5 # via pyasn1-modules, rsa
rsa==3.4.2 python-json-logger==0.1.9
SecretStorage==2.3.1 pytz==2018.9 # via google-api-core
six==1.11.0 requests==2.21.0 # via google-api-core, google-cloud-profiler
urllib3==1.23 rsa==4.0 # via google-auth
six==1.12.0 # via google-api-core, google-api-python-client, google-auth, grpcio, protobuf
uritemplate==3.0.0 # via google-api-python-client
urllib3==1.24.1 # via requests

View file

@ -1,4 +1,4 @@
FROM golang:1.10-alpine as builder FROM golang:1.12-alpine as builder
RUN apk add --no-cache ca-certificates git && \ RUN apk add --no-cache ca-certificates git && \
wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \ wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \
chmod +x /go/bin/dep chmod +x /go/bin/dep

104
src/frontend/Gopkg.lock generated
View file

@ -16,26 +16,32 @@
version = "v0.27.0" version = "v0.27.0"
[[projects]] [[projects]]
digest = "1:9fe70def8f0ceb3d455a0acad9dadd6632287cdbf9c8c2ea50e8dabe2ade40c4" digest = "1:4b96dcd8534bc6450a922bd16a76360ba3381f0d1daf40abbaec91c053fbfeb5"
name = "contrib.go.opencensus.io/exporter/stackdriver" name = "contrib.go.opencensus.io/exporter/stackdriver"
packages = [ packages = ["."]
".",
"propagation",
]
pruneopts = "UT" pruneopts = "UT"
revision = "37aa2801fbf0205003e15636096ebf0373510288" revision = "37aa2801fbf0205003e15636096ebf0373510288"
version = "v0.5.0" version = "v0.5.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3ef905a7059a17712b7b27315692992f84f356e828d38f6ff0624e04103ec675" digest = "1:1d8f3cb93c42c5652bb509fde29ecdd1feede9334e355e8bbdc0f9f95b40c254"
name = "git.apache.org/thrift.git"
packages = ["lib/go/thrift"]
pruneopts = "UT"
revision = "6503043bc42ab96da14c25f3aee2bb4add719774"
source = "github.com/apache/thrift"
[[projects]]
branch = "master"
digest = "1:6cbe7676244a1429f4c22601f799d377a70449469ef636f91d992d719b559ff3"
name = "github.com/GoogleCloudPlatform/microservices-demo" name = "github.com/GoogleCloudPlatform/microservices-demo"
packages = [ packages = [
"src/frontend/genproto", "src/frontend/genproto",
"src/frontend/money", "src/frontend/money",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "6d969441585ade8c91c235115c7cdb12ac61354f" revision = "d944092100696aa4a5974ef5c2e710547a824622"
[[projects]] [[projects]]
digest = "1:72856926f8208767b837bf51e3373f49139f61889b67dc7fd3c2a0fd711e3f7a" digest = "1:72856926f8208767b837bf51e3373f49139f61889b67dc7fd3c2a0fd711e3f7a"
@ -61,23 +67,26 @@
name = "github.com/google/pprof" name = "github.com/google/pprof"
packages = ["profile"] packages = ["profile"]
pruneopts = "UT" pruneopts = "UT"
revision = "84b7d314e22c8d12334e52726f68517973b6027b" revision = "3ea8567a2e5752420fc544d2e2ad249721768934"
[[projects]] [[projects]]
digest = "1:3a26588bc48b96825977c1b3df964f8fd842cd6860cc26370588d3563433cf11" digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8"
name = "github.com/google/uuid" name = "github.com/google/uuid"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8"
version = "v1.0.0" version = "v1.1.0"
[[projects]] [[projects]]
digest = "1:e145e9710a10bc114a6d3e2738aadf8de146adaa031854ffdf7bbfe15da85e63" digest = "1:cd9864c6366515827a759931746738ede6079faa08df9c584596370d6add135c"
name = "github.com/googleapis/gax-go" name = "github.com/googleapis/gax-go"
packages = ["."] packages = [
".",
"v2",
]
pruneopts = "UT" pruneopts = "UT"
revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" revision = "c8a15bac9b9fe955bd9f900272f9a306465d28cf"
version = "v2.0.0" version = "v2.0.3"
[[projects]] [[projects]]
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
@ -95,6 +104,14 @@
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2" version = "v1.6.2"
[[projects]]
digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8"
name = "github.com/konsorten/go-windows-terminal-sequences"
packages = ["."]
pruneopts = "UT"
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
version = "v1.0.1"
[[projects]] [[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors" name = "github.com/pkg/errors"
@ -104,18 +121,20 @@
version = "v0.8.0" version = "v0.8.0"
[[projects]] [[projects]]
digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc" digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70"
name = "github.com/sirupsen/logrus" name = "github.com/sirupsen/logrus"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2" revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95"
version = "v1.0.6" version = "v1.2.0"
[[projects]] [[projects]]
digest = "1:1bb914cfb78f68f488a91cd7872d3d06a5f83c5bbacf0296dbef44e120b00a91" digest = "1:a5154dfd6b37bef5a3eab759e13296348e639dc8c7604f538368167782b08ccd"
name = "go.opencensus.io" name = "go.opencensus.io"
packages = [ packages = [
".", ".",
"exporter/jaeger",
"exporter/jaeger/internal/gen-go/jaeger",
"internal", "internal",
"internal/tagencoding", "internal/tagencoding",
"plugin/ocgrpc", "plugin/ocgrpc",
@ -136,15 +155,15 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ssh/terminal"] packages = ["ssh/terminal"]
pruneopts = "UT" pruneopts = "UT"
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:1c14517b2f106c61d75006199b46a46576058661d469658cb0f90739919641d2" digest = "1:8ecb828bb550a8c6b7d75b8261a42c369461311616ebe5451966d067f5f993bf"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"context", "context",
@ -157,11 +176,11 @@
"trace", "trace",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" revision = "927f97764cc334a6575f4b7a1584a147864d5723"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:f645667d687fc8bf228865a2c5455824ef05bad08841e673673ef2bb89ac5b90" digest = "1:23443edff0740e348959763085df98400dcfd85528d7771bb0ce9f5f2754ff4a"
name = "golang.org/x/oauth2" name = "golang.org/x/oauth2"
packages = [ packages = [
".", ".",
@ -171,26 +190,26 @@
"jwt", "jwt",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" revision = "d668ce993890a79bda886613ee587a69dd5da7a6"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:e0140c0c868c6e0f01c0380865194592c011fe521d6e12d78bfd33e756fe018a" digest = "1:75515eedc0dc2cb0b40372008b616fa2841d831c63eedd403285ff286c593295"
name = "golang.org/x/sync" name = "golang.org/x/sync"
packages = ["semaphore"] packages = ["semaphore"]
pruneopts = "UT" pruneopts = "UT"
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" revision = "37e7f081c4d4c64e13b10787722085407fe5d15f"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:374fc90fcb026e9a367e3fad29e988e5dd944b68ca3f24a184d77abc5307dda4" digest = "1:191cccd950a4aeadb60306062f2bdc2f924d750d0156ec6c691b17211bfd7349"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = [ packages = [
"unix", "unix",
"windows", "windows",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "d0be0721c37eeb5299f245a996a483160fc36940" revision = "82a175fd1598e8a172e58ebdf5ed262bb29129e5"
[[projects]] [[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -217,7 +236,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:e9e388241f9f0f02000dddaeeb91153d53f0cd09dcec33879cc7e043a2e65d75" digest = "1:2e81813e8e072aa700e101369890e55539729d817d32dbc3fab228d6b40c4d83"
name = "google.golang.org/api" name = "google.golang.org/api"
packages = [ packages = [
"googleapi/transport", "googleapi/transport",
@ -228,12 +247,13 @@
"transport", "transport",
"transport/grpc", "transport/grpc",
"transport/http", "transport/http",
"transport/http/internal/propagation",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "7ca32eb868bf53ea2fc406698eb98583a8073d19" revision = "19e022d8cf43ce81f046bae8cc18c5397cc7732f"
[[projects]] [[projects]]
digest = "1:26619fcd2452b4044174d26acd8b09c09dffee9a1c3a22d2383b873aa9a0131f" digest = "1:c4eaa5f79d36f76ef4bd0c4f96e36bc1b7b5a359528d1267f0cb7a5d58b7b5bb"
name = "google.golang.org/appengine" name = "google.golang.org/appengine"
packages = [ packages = [
".", ".",
@ -250,12 +270,12 @@
"urlfetch", "urlfetch",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.1.0" version = "v1.4.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:7a3da01a8f840fbbef1f027dc091ae52a29c6ab9374e126b6bdc5bf3b0ff2687" digest = "1:3552e7267a98c605e6cbfe6b03c34589265ab72e6078b95fb2e10e0cb44f5cc8"
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
packages = [ packages = [
"googleapis/api/annotations", "googleapis/api/annotations",
@ -271,28 +291,33 @@
"protobuf/field_mask", "protobuf/field_mask",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "36d5787dc5356b711fe8f3040271aaf51c35955b" revision = "bd9b4fb69e2ffd37621a6caa54dcbead29b546f2"
[[projects]] [[projects]]
digest = "1:4ad047d772a7d4841687df9534a767a9e243885ed6d7d2ced6af5995a5dc44b3" digest = "1:3fc54ad826c0183f803bb97e0927dfc05fcb7b7a6ddabed646ee8184d861fa9b"
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
packages = [ packages = [
".", ".",
"balancer", "balancer",
"balancer/base", "balancer/base",
"balancer/roundrobin", "balancer/roundrobin",
"binarylog/grpc_binarylog_v1",
"codes", "codes",
"connectivity", "connectivity",
"credentials", "credentials",
"credentials/internal",
"credentials/oauth", "credentials/oauth",
"encoding", "encoding",
"encoding/proto", "encoding/proto",
"grpclog", "grpclog",
"internal", "internal",
"internal/backoff", "internal/backoff",
"internal/binarylog",
"internal/channelz", "internal/channelz",
"internal/envconfig", "internal/envconfig",
"internal/grpcrand", "internal/grpcrand",
"internal/grpcsync",
"internal/syscall",
"internal/transport", "internal/transport",
"keepalive", "keepalive",
"metadata", "metadata",
@ -306,8 +331,8 @@
"tap", "tap",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1" revision = "df014850f6dee74ba2fc94874043a9f3f75fbfd8"
version = "v1.15.0" version = "v1.17.0"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
@ -322,6 +347,7 @@
"github.com/gorilla/mux", "github.com/gorilla/mux",
"github.com/pkg/errors", "github.com/pkg/errors",
"github.com/sirupsen/logrus", "github.com/sirupsen/logrus",
"go.opencensus.io/exporter/jaeger",
"go.opencensus.io/plugin/ocgrpc", "go.opencensus.io/plugin/ocgrpc",
"go.opencensus.io/plugin/ochttp", "go.opencensus.io/plugin/ochttp",
"go.opencensus.io/plugin/ochttp/propagation/b3", "go.opencensus.io/plugin/ochttp/propagation/b3",

View file

@ -33,10 +33,6 @@
name = "contrib.go.opencensus.io/exporter/stackdriver" name = "contrib.go.opencensus.io/exporter/stackdriver"
version = "0.5.0" version = "0.5.0"
[[constraint]]
branch = "master"
name = "github.com/GoogleCloudPlatform/microservices-demo"
[[constraint]] [[constraint]]
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
version = "1.2.0" version = "1.2.0"

View file

@ -3,9 +3,11 @@
package hipstershop package hipstershop
import proto "github.com/golang/protobuf/proto" import (
import fmt "fmt" fmt "fmt"
import math "math" proto "github.com/golang/protobuf/proto"
math "math"
)
import ( import (
context "golang.org/x/net/context" context "golang.org/x/net/context"
@ -35,16 +37,17 @@ func (m *CartItem) Reset() { *m = CartItem{} }
func (m *CartItem) String() string { return proto.CompactTextString(m) } func (m *CartItem) String() string { return proto.CompactTextString(m) }
func (*CartItem) ProtoMessage() {} func (*CartItem) ProtoMessage() {}
func (*CartItem) Descriptor() ([]byte, []int) { func (*CartItem) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{0} return fileDescriptor_ca53982754088a9d, []int{0}
} }
func (m *CartItem) XXX_Unmarshal(b []byte) error { func (m *CartItem) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CartItem.Unmarshal(m, b) return xxx_messageInfo_CartItem.Unmarshal(m, b)
} }
func (m *CartItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *CartItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CartItem.Marshal(b, m, deterministic) return xxx_messageInfo_CartItem.Marshal(b, m, deterministic)
} }
func (dst *CartItem) XXX_Merge(src proto.Message) { func (m *CartItem) XXX_Merge(src proto.Message) {
xxx_messageInfo_CartItem.Merge(dst, src) xxx_messageInfo_CartItem.Merge(m, src)
} }
func (m *CartItem) XXX_Size() int { func (m *CartItem) XXX_Size() int {
return xxx_messageInfo_CartItem.Size(m) return xxx_messageInfo_CartItem.Size(m)
@ -81,16 +84,17 @@ func (m *AddItemRequest) Reset() { *m = AddItemRequest{} }
func (m *AddItemRequest) String() string { return proto.CompactTextString(m) } func (m *AddItemRequest) String() string { return proto.CompactTextString(m) }
func (*AddItemRequest) ProtoMessage() {} func (*AddItemRequest) ProtoMessage() {}
func (*AddItemRequest) Descriptor() ([]byte, []int) { func (*AddItemRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{1} return fileDescriptor_ca53982754088a9d, []int{1}
} }
func (m *AddItemRequest) XXX_Unmarshal(b []byte) error { func (m *AddItemRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddItemRequest.Unmarshal(m, b) return xxx_messageInfo_AddItemRequest.Unmarshal(m, b)
} }
func (m *AddItemRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *AddItemRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AddItemRequest.Marshal(b, m, deterministic) return xxx_messageInfo_AddItemRequest.Marshal(b, m, deterministic)
} }
func (dst *AddItemRequest) XXX_Merge(src proto.Message) { func (m *AddItemRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AddItemRequest.Merge(dst, src) xxx_messageInfo_AddItemRequest.Merge(m, src)
} }
func (m *AddItemRequest) XXX_Size() int { func (m *AddItemRequest) XXX_Size() int {
return xxx_messageInfo_AddItemRequest.Size(m) return xxx_messageInfo_AddItemRequest.Size(m)
@ -126,16 +130,17 @@ func (m *EmptyCartRequest) Reset() { *m = EmptyCartRequest{} }
func (m *EmptyCartRequest) String() string { return proto.CompactTextString(m) } func (m *EmptyCartRequest) String() string { return proto.CompactTextString(m) }
func (*EmptyCartRequest) ProtoMessage() {} func (*EmptyCartRequest) ProtoMessage() {}
func (*EmptyCartRequest) Descriptor() ([]byte, []int) { func (*EmptyCartRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{2} return fileDescriptor_ca53982754088a9d, []int{2}
} }
func (m *EmptyCartRequest) XXX_Unmarshal(b []byte) error { func (m *EmptyCartRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EmptyCartRequest.Unmarshal(m, b) return xxx_messageInfo_EmptyCartRequest.Unmarshal(m, b)
} }
func (m *EmptyCartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *EmptyCartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EmptyCartRequest.Marshal(b, m, deterministic) return xxx_messageInfo_EmptyCartRequest.Marshal(b, m, deterministic)
} }
func (dst *EmptyCartRequest) XXX_Merge(src proto.Message) { func (m *EmptyCartRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyCartRequest.Merge(dst, src) xxx_messageInfo_EmptyCartRequest.Merge(m, src)
} }
func (m *EmptyCartRequest) XXX_Size() int { func (m *EmptyCartRequest) XXX_Size() int {
return xxx_messageInfo_EmptyCartRequest.Size(m) return xxx_messageInfo_EmptyCartRequest.Size(m)
@ -164,16 +169,17 @@ func (m *GetCartRequest) Reset() { *m = GetCartRequest{} }
func (m *GetCartRequest) String() string { return proto.CompactTextString(m) } func (m *GetCartRequest) String() string { return proto.CompactTextString(m) }
func (*GetCartRequest) ProtoMessage() {} func (*GetCartRequest) ProtoMessage() {}
func (*GetCartRequest) Descriptor() ([]byte, []int) { func (*GetCartRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{3} return fileDescriptor_ca53982754088a9d, []int{3}
} }
func (m *GetCartRequest) XXX_Unmarshal(b []byte) error { func (m *GetCartRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetCartRequest.Unmarshal(m, b) return xxx_messageInfo_GetCartRequest.Unmarshal(m, b)
} }
func (m *GetCartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *GetCartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetCartRequest.Marshal(b, m, deterministic) return xxx_messageInfo_GetCartRequest.Marshal(b, m, deterministic)
} }
func (dst *GetCartRequest) XXX_Merge(src proto.Message) { func (m *GetCartRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetCartRequest.Merge(dst, src) xxx_messageInfo_GetCartRequest.Merge(m, src)
} }
func (m *GetCartRequest) XXX_Size() int { func (m *GetCartRequest) XXX_Size() int {
return xxx_messageInfo_GetCartRequest.Size(m) return xxx_messageInfo_GetCartRequest.Size(m)
@ -203,16 +209,17 @@ func (m *Cart) Reset() { *m = Cart{} }
func (m *Cart) String() string { return proto.CompactTextString(m) } func (m *Cart) String() string { return proto.CompactTextString(m) }
func (*Cart) ProtoMessage() {} func (*Cart) ProtoMessage() {}
func (*Cart) Descriptor() ([]byte, []int) { func (*Cart) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{4} return fileDescriptor_ca53982754088a9d, []int{4}
} }
func (m *Cart) XXX_Unmarshal(b []byte) error { func (m *Cart) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Cart.Unmarshal(m, b) return xxx_messageInfo_Cart.Unmarshal(m, b)
} }
func (m *Cart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Cart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Cart.Marshal(b, m, deterministic) return xxx_messageInfo_Cart.Marshal(b, m, deterministic)
} }
func (dst *Cart) XXX_Merge(src proto.Message) { func (m *Cart) XXX_Merge(src proto.Message) {
xxx_messageInfo_Cart.Merge(dst, src) xxx_messageInfo_Cart.Merge(m, src)
} }
func (m *Cart) XXX_Size() int { func (m *Cart) XXX_Size() int {
return xxx_messageInfo_Cart.Size(m) return xxx_messageInfo_Cart.Size(m)
@ -247,16 +254,17 @@ func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) } func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {} func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) { func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{5} return fileDescriptor_ca53982754088a9d, []int{5}
} }
func (m *Empty) XXX_Unmarshal(b []byte) error { func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b) return xxx_messageInfo_Empty.Unmarshal(m, b)
} }
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic) return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
} }
func (dst *Empty) XXX_Merge(src proto.Message) { func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(dst, src) xxx_messageInfo_Empty.Merge(m, src)
} }
func (m *Empty) XXX_Size() int { func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m) return xxx_messageInfo_Empty.Size(m)
@ -279,16 +287,17 @@ func (m *ListRecommendationsRequest) Reset() { *m = ListRecommendationsR
func (m *ListRecommendationsRequest) String() string { return proto.CompactTextString(m) } func (m *ListRecommendationsRequest) String() string { return proto.CompactTextString(m) }
func (*ListRecommendationsRequest) ProtoMessage() {} func (*ListRecommendationsRequest) ProtoMessage() {}
func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) { func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{6} return fileDescriptor_ca53982754088a9d, []int{6}
} }
func (m *ListRecommendationsRequest) XXX_Unmarshal(b []byte) error { func (m *ListRecommendationsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRecommendationsRequest.Unmarshal(m, b) return xxx_messageInfo_ListRecommendationsRequest.Unmarshal(m, b)
} }
func (m *ListRecommendationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ListRecommendationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRecommendationsRequest.Marshal(b, m, deterministic) return xxx_messageInfo_ListRecommendationsRequest.Marshal(b, m, deterministic)
} }
func (dst *ListRecommendationsRequest) XXX_Merge(src proto.Message) { func (m *ListRecommendationsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRecommendationsRequest.Merge(dst, src) xxx_messageInfo_ListRecommendationsRequest.Merge(m, src)
} }
func (m *ListRecommendationsRequest) XXX_Size() int { func (m *ListRecommendationsRequest) XXX_Size() int {
return xxx_messageInfo_ListRecommendationsRequest.Size(m) return xxx_messageInfo_ListRecommendationsRequest.Size(m)
@ -324,16 +333,17 @@ func (m *ListRecommendationsResponse) Reset() { *m = ListRecommendations
func (m *ListRecommendationsResponse) String() string { return proto.CompactTextString(m) } func (m *ListRecommendationsResponse) String() string { return proto.CompactTextString(m) }
func (*ListRecommendationsResponse) ProtoMessage() {} func (*ListRecommendationsResponse) ProtoMessage() {}
func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) { func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{7} return fileDescriptor_ca53982754088a9d, []int{7}
} }
func (m *ListRecommendationsResponse) XXX_Unmarshal(b []byte) error { func (m *ListRecommendationsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRecommendationsResponse.Unmarshal(m, b) return xxx_messageInfo_ListRecommendationsResponse.Unmarshal(m, b)
} }
func (m *ListRecommendationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ListRecommendationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRecommendationsResponse.Marshal(b, m, deterministic) return xxx_messageInfo_ListRecommendationsResponse.Marshal(b, m, deterministic)
} }
func (dst *ListRecommendationsResponse) XXX_Merge(src proto.Message) { func (m *ListRecommendationsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRecommendationsResponse.Merge(dst, src) xxx_messageInfo_ListRecommendationsResponse.Merge(m, src)
} }
func (m *ListRecommendationsResponse) XXX_Size() int { func (m *ListRecommendationsResponse) XXX_Size() int {
return xxx_messageInfo_ListRecommendationsResponse.Size(m) return xxx_messageInfo_ListRecommendationsResponse.Size(m)
@ -352,11 +362,14 @@ func (m *ListRecommendationsResponse) GetProductIds() []string {
} }
type Product struct { type Product struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Picture string `protobuf:"bytes,4,opt,name=picture,proto3" json:"picture,omitempty"` Picture string `protobuf:"bytes,4,opt,name=picture,proto3" json:"picture,omitempty"`
PriceUsd *Money `protobuf:"bytes,5,opt,name=price_usd,json=priceUsd,proto3" json:"price_usd,omitempty"` PriceUsd *Money `protobuf:"bytes,5,opt,name=price_usd,json=priceUsd,proto3" json:"price_usd,omitempty"`
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
Categories []string `protobuf:"bytes,6,rep,name=categories,proto3" json:"categories,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -366,16 +379,17 @@ func (m *Product) Reset() { *m = Product{} }
func (m *Product) String() string { return proto.CompactTextString(m) } func (m *Product) String() string { return proto.CompactTextString(m) }
func (*Product) ProtoMessage() {} func (*Product) ProtoMessage() {}
func (*Product) Descriptor() ([]byte, []int) { func (*Product) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{8} return fileDescriptor_ca53982754088a9d, []int{8}
} }
func (m *Product) XXX_Unmarshal(b []byte) error { func (m *Product) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Product.Unmarshal(m, b) return xxx_messageInfo_Product.Unmarshal(m, b)
} }
func (m *Product) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Product) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Product.Marshal(b, m, deterministic) return xxx_messageInfo_Product.Marshal(b, m, deterministic)
} }
func (dst *Product) XXX_Merge(src proto.Message) { func (m *Product) XXX_Merge(src proto.Message) {
xxx_messageInfo_Product.Merge(dst, src) xxx_messageInfo_Product.Merge(m, src)
} }
func (m *Product) XXX_Size() int { func (m *Product) XXX_Size() int {
return xxx_messageInfo_Product.Size(m) return xxx_messageInfo_Product.Size(m)
@ -421,6 +435,13 @@ func (m *Product) GetPriceUsd() *Money {
return nil return nil
} }
func (m *Product) GetCategories() []string {
if m != nil {
return m.Categories
}
return nil
}
type ListProductsResponse struct { type ListProductsResponse struct {
Products []*Product `protobuf:"bytes,1,rep,name=products,proto3" json:"products,omitempty"` Products []*Product `protobuf:"bytes,1,rep,name=products,proto3" json:"products,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -432,16 +453,17 @@ func (m *ListProductsResponse) Reset() { *m = ListProductsResponse{} }
func (m *ListProductsResponse) String() string { return proto.CompactTextString(m) } func (m *ListProductsResponse) String() string { return proto.CompactTextString(m) }
func (*ListProductsResponse) ProtoMessage() {} func (*ListProductsResponse) ProtoMessage() {}
func (*ListProductsResponse) Descriptor() ([]byte, []int) { func (*ListProductsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{9} return fileDescriptor_ca53982754088a9d, []int{9}
} }
func (m *ListProductsResponse) XXX_Unmarshal(b []byte) error { func (m *ListProductsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListProductsResponse.Unmarshal(m, b) return xxx_messageInfo_ListProductsResponse.Unmarshal(m, b)
} }
func (m *ListProductsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ListProductsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListProductsResponse.Marshal(b, m, deterministic) return xxx_messageInfo_ListProductsResponse.Marshal(b, m, deterministic)
} }
func (dst *ListProductsResponse) XXX_Merge(src proto.Message) { func (m *ListProductsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListProductsResponse.Merge(dst, src) xxx_messageInfo_ListProductsResponse.Merge(m, src)
} }
func (m *ListProductsResponse) XXX_Size() int { func (m *ListProductsResponse) XXX_Size() int {
return xxx_messageInfo_ListProductsResponse.Size(m) return xxx_messageInfo_ListProductsResponse.Size(m)
@ -470,16 +492,17 @@ func (m *GetProductRequest) Reset() { *m = GetProductRequest{} }
func (m *GetProductRequest) String() string { return proto.CompactTextString(m) } func (m *GetProductRequest) String() string { return proto.CompactTextString(m) }
func (*GetProductRequest) ProtoMessage() {} func (*GetProductRequest) ProtoMessage() {}
func (*GetProductRequest) Descriptor() ([]byte, []int) { func (*GetProductRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{10} return fileDescriptor_ca53982754088a9d, []int{10}
} }
func (m *GetProductRequest) XXX_Unmarshal(b []byte) error { func (m *GetProductRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetProductRequest.Unmarshal(m, b) return xxx_messageInfo_GetProductRequest.Unmarshal(m, b)
} }
func (m *GetProductRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *GetProductRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetProductRequest.Marshal(b, m, deterministic) return xxx_messageInfo_GetProductRequest.Marshal(b, m, deterministic)
} }
func (dst *GetProductRequest) XXX_Merge(src proto.Message) { func (m *GetProductRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetProductRequest.Merge(dst, src) xxx_messageInfo_GetProductRequest.Merge(m, src)
} }
func (m *GetProductRequest) XXX_Size() int { func (m *GetProductRequest) XXX_Size() int {
return xxx_messageInfo_GetProductRequest.Size(m) return xxx_messageInfo_GetProductRequest.Size(m)
@ -508,16 +531,17 @@ func (m *SearchProductsRequest) Reset() { *m = SearchProductsRequest{} }
func (m *SearchProductsRequest) String() string { return proto.CompactTextString(m) } func (m *SearchProductsRequest) String() string { return proto.CompactTextString(m) }
func (*SearchProductsRequest) ProtoMessage() {} func (*SearchProductsRequest) ProtoMessage() {}
func (*SearchProductsRequest) Descriptor() ([]byte, []int) { func (*SearchProductsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{11} return fileDescriptor_ca53982754088a9d, []int{11}
} }
func (m *SearchProductsRequest) XXX_Unmarshal(b []byte) error { func (m *SearchProductsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SearchProductsRequest.Unmarshal(m, b) return xxx_messageInfo_SearchProductsRequest.Unmarshal(m, b)
} }
func (m *SearchProductsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *SearchProductsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SearchProductsRequest.Marshal(b, m, deterministic) return xxx_messageInfo_SearchProductsRequest.Marshal(b, m, deterministic)
} }
func (dst *SearchProductsRequest) XXX_Merge(src proto.Message) { func (m *SearchProductsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SearchProductsRequest.Merge(dst, src) xxx_messageInfo_SearchProductsRequest.Merge(m, src)
} }
func (m *SearchProductsRequest) XXX_Size() int { func (m *SearchProductsRequest) XXX_Size() int {
return xxx_messageInfo_SearchProductsRequest.Size(m) return xxx_messageInfo_SearchProductsRequest.Size(m)
@ -546,16 +570,17 @@ func (m *SearchProductsResponse) Reset() { *m = SearchProductsResponse{}
func (m *SearchProductsResponse) String() string { return proto.CompactTextString(m) } func (m *SearchProductsResponse) String() string { return proto.CompactTextString(m) }
func (*SearchProductsResponse) ProtoMessage() {} func (*SearchProductsResponse) ProtoMessage() {}
func (*SearchProductsResponse) Descriptor() ([]byte, []int) { func (*SearchProductsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{12} return fileDescriptor_ca53982754088a9d, []int{12}
} }
func (m *SearchProductsResponse) XXX_Unmarshal(b []byte) error { func (m *SearchProductsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SearchProductsResponse.Unmarshal(m, b) return xxx_messageInfo_SearchProductsResponse.Unmarshal(m, b)
} }
func (m *SearchProductsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *SearchProductsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SearchProductsResponse.Marshal(b, m, deterministic) return xxx_messageInfo_SearchProductsResponse.Marshal(b, m, deterministic)
} }
func (dst *SearchProductsResponse) XXX_Merge(src proto.Message) { func (m *SearchProductsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SearchProductsResponse.Merge(dst, src) xxx_messageInfo_SearchProductsResponse.Merge(m, src)
} }
func (m *SearchProductsResponse) XXX_Size() int { func (m *SearchProductsResponse) XXX_Size() int {
return xxx_messageInfo_SearchProductsResponse.Size(m) return xxx_messageInfo_SearchProductsResponse.Size(m)
@ -585,16 +610,17 @@ func (m *GetQuoteRequest) Reset() { *m = GetQuoteRequest{} }
func (m *GetQuoteRequest) String() string { return proto.CompactTextString(m) } func (m *GetQuoteRequest) String() string { return proto.CompactTextString(m) }
func (*GetQuoteRequest) ProtoMessage() {} func (*GetQuoteRequest) ProtoMessage() {}
func (*GetQuoteRequest) Descriptor() ([]byte, []int) { func (*GetQuoteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{13} return fileDescriptor_ca53982754088a9d, []int{13}
} }
func (m *GetQuoteRequest) XXX_Unmarshal(b []byte) error { func (m *GetQuoteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetQuoteRequest.Unmarshal(m, b) return xxx_messageInfo_GetQuoteRequest.Unmarshal(m, b)
} }
func (m *GetQuoteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *GetQuoteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetQuoteRequest.Marshal(b, m, deterministic) return xxx_messageInfo_GetQuoteRequest.Marshal(b, m, deterministic)
} }
func (dst *GetQuoteRequest) XXX_Merge(src proto.Message) { func (m *GetQuoteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetQuoteRequest.Merge(dst, src) xxx_messageInfo_GetQuoteRequest.Merge(m, src)
} }
func (m *GetQuoteRequest) XXX_Size() int { func (m *GetQuoteRequest) XXX_Size() int {
return xxx_messageInfo_GetQuoteRequest.Size(m) return xxx_messageInfo_GetQuoteRequest.Size(m)
@ -630,16 +656,17 @@ func (m *GetQuoteResponse) Reset() { *m = GetQuoteResponse{} }
func (m *GetQuoteResponse) String() string { return proto.CompactTextString(m) } func (m *GetQuoteResponse) String() string { return proto.CompactTextString(m) }
func (*GetQuoteResponse) ProtoMessage() {} func (*GetQuoteResponse) ProtoMessage() {}
func (*GetQuoteResponse) Descriptor() ([]byte, []int) { func (*GetQuoteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{14} return fileDescriptor_ca53982754088a9d, []int{14}
} }
func (m *GetQuoteResponse) XXX_Unmarshal(b []byte) error { func (m *GetQuoteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetQuoteResponse.Unmarshal(m, b) return xxx_messageInfo_GetQuoteResponse.Unmarshal(m, b)
} }
func (m *GetQuoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *GetQuoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetQuoteResponse.Marshal(b, m, deterministic) return xxx_messageInfo_GetQuoteResponse.Marshal(b, m, deterministic)
} }
func (dst *GetQuoteResponse) XXX_Merge(src proto.Message) { func (m *GetQuoteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetQuoteResponse.Merge(dst, src) xxx_messageInfo_GetQuoteResponse.Merge(m, src)
} }
func (m *GetQuoteResponse) XXX_Size() int { func (m *GetQuoteResponse) XXX_Size() int {
return xxx_messageInfo_GetQuoteResponse.Size(m) return xxx_messageInfo_GetQuoteResponse.Size(m)
@ -669,16 +696,17 @@ func (m *ShipOrderRequest) Reset() { *m = ShipOrderRequest{} }
func (m *ShipOrderRequest) String() string { return proto.CompactTextString(m) } func (m *ShipOrderRequest) String() string { return proto.CompactTextString(m) }
func (*ShipOrderRequest) ProtoMessage() {} func (*ShipOrderRequest) ProtoMessage() {}
func (*ShipOrderRequest) Descriptor() ([]byte, []int) { func (*ShipOrderRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{15} return fileDescriptor_ca53982754088a9d, []int{15}
} }
func (m *ShipOrderRequest) XXX_Unmarshal(b []byte) error { func (m *ShipOrderRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ShipOrderRequest.Unmarshal(m, b) return xxx_messageInfo_ShipOrderRequest.Unmarshal(m, b)
} }
func (m *ShipOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ShipOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ShipOrderRequest.Marshal(b, m, deterministic) return xxx_messageInfo_ShipOrderRequest.Marshal(b, m, deterministic)
} }
func (dst *ShipOrderRequest) XXX_Merge(src proto.Message) { func (m *ShipOrderRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ShipOrderRequest.Merge(dst, src) xxx_messageInfo_ShipOrderRequest.Merge(m, src)
} }
func (m *ShipOrderRequest) XXX_Size() int { func (m *ShipOrderRequest) XXX_Size() int {
return xxx_messageInfo_ShipOrderRequest.Size(m) return xxx_messageInfo_ShipOrderRequest.Size(m)
@ -714,16 +742,17 @@ func (m *ShipOrderResponse) Reset() { *m = ShipOrderResponse{} }
func (m *ShipOrderResponse) String() string { return proto.CompactTextString(m) } func (m *ShipOrderResponse) String() string { return proto.CompactTextString(m) }
func (*ShipOrderResponse) ProtoMessage() {} func (*ShipOrderResponse) ProtoMessage() {}
func (*ShipOrderResponse) Descriptor() ([]byte, []int) { func (*ShipOrderResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{16} return fileDescriptor_ca53982754088a9d, []int{16}
} }
func (m *ShipOrderResponse) XXX_Unmarshal(b []byte) error { func (m *ShipOrderResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ShipOrderResponse.Unmarshal(m, b) return xxx_messageInfo_ShipOrderResponse.Unmarshal(m, b)
} }
func (m *ShipOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ShipOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ShipOrderResponse.Marshal(b, m, deterministic) return xxx_messageInfo_ShipOrderResponse.Marshal(b, m, deterministic)
} }
func (dst *ShipOrderResponse) XXX_Merge(src proto.Message) { func (m *ShipOrderResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ShipOrderResponse.Merge(dst, src) xxx_messageInfo_ShipOrderResponse.Merge(m, src)
} }
func (m *ShipOrderResponse) XXX_Size() int { func (m *ShipOrderResponse) XXX_Size() int {
return xxx_messageInfo_ShipOrderResponse.Size(m) return xxx_messageInfo_ShipOrderResponse.Size(m)
@ -756,16 +785,17 @@ func (m *Address) Reset() { *m = Address{} }
func (m *Address) String() string { return proto.CompactTextString(m) } func (m *Address) String() string { return proto.CompactTextString(m) }
func (*Address) ProtoMessage() {} func (*Address) ProtoMessage() {}
func (*Address) Descriptor() ([]byte, []int) { func (*Address) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{17} return fileDescriptor_ca53982754088a9d, []int{17}
} }
func (m *Address) XXX_Unmarshal(b []byte) error { func (m *Address) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Address.Unmarshal(m, b) return xxx_messageInfo_Address.Unmarshal(m, b)
} }
func (m *Address) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Address) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Address.Marshal(b, m, deterministic) return xxx_messageInfo_Address.Marshal(b, m, deterministic)
} }
func (dst *Address) XXX_Merge(src proto.Message) { func (m *Address) XXX_Merge(src proto.Message) {
xxx_messageInfo_Address.Merge(dst, src) xxx_messageInfo_Address.Merge(m, src)
} }
func (m *Address) XXX_Size() int { func (m *Address) XXX_Size() int {
return xxx_messageInfo_Address.Size(m) return xxx_messageInfo_Address.Size(m)
@ -834,16 +864,17 @@ func (m *Money) Reset() { *m = Money{} }
func (m *Money) String() string { return proto.CompactTextString(m) } func (m *Money) String() string { return proto.CompactTextString(m) }
func (*Money) ProtoMessage() {} func (*Money) ProtoMessage() {}
func (*Money) Descriptor() ([]byte, []int) { func (*Money) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{18} return fileDescriptor_ca53982754088a9d, []int{18}
} }
func (m *Money) XXX_Unmarshal(b []byte) error { func (m *Money) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Money.Unmarshal(m, b) return xxx_messageInfo_Money.Unmarshal(m, b)
} }
func (m *Money) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Money) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Money.Marshal(b, m, deterministic) return xxx_messageInfo_Money.Marshal(b, m, deterministic)
} }
func (dst *Money) XXX_Merge(src proto.Message) { func (m *Money) XXX_Merge(src proto.Message) {
xxx_messageInfo_Money.Merge(dst, src) xxx_messageInfo_Money.Merge(m, src)
} }
func (m *Money) XXX_Size() int { func (m *Money) XXX_Size() int {
return xxx_messageInfo_Money.Size(m) return xxx_messageInfo_Money.Size(m)
@ -887,16 +918,17 @@ func (m *GetSupportedCurrenciesResponse) Reset() { *m = GetSupportedCurr
func (m *GetSupportedCurrenciesResponse) String() string { return proto.CompactTextString(m) } func (m *GetSupportedCurrenciesResponse) String() string { return proto.CompactTextString(m) }
func (*GetSupportedCurrenciesResponse) ProtoMessage() {} func (*GetSupportedCurrenciesResponse) ProtoMessage() {}
func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) { func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{19} return fileDescriptor_ca53982754088a9d, []int{19}
} }
func (m *GetSupportedCurrenciesResponse) XXX_Unmarshal(b []byte) error { func (m *GetSupportedCurrenciesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetSupportedCurrenciesResponse.Unmarshal(m, b) return xxx_messageInfo_GetSupportedCurrenciesResponse.Unmarshal(m, b)
} }
func (m *GetSupportedCurrenciesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *GetSupportedCurrenciesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetSupportedCurrenciesResponse.Marshal(b, m, deterministic) return xxx_messageInfo_GetSupportedCurrenciesResponse.Marshal(b, m, deterministic)
} }
func (dst *GetSupportedCurrenciesResponse) XXX_Merge(src proto.Message) { func (m *GetSupportedCurrenciesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetSupportedCurrenciesResponse.Merge(dst, src) xxx_messageInfo_GetSupportedCurrenciesResponse.Merge(m, src)
} }
func (m *GetSupportedCurrenciesResponse) XXX_Size() int { func (m *GetSupportedCurrenciesResponse) XXX_Size() int {
return xxx_messageInfo_GetSupportedCurrenciesResponse.Size(m) return xxx_messageInfo_GetSupportedCurrenciesResponse.Size(m)
@ -927,16 +959,17 @@ func (m *CurrencyConversionRequest) Reset() { *m = CurrencyConversionReq
func (m *CurrencyConversionRequest) String() string { return proto.CompactTextString(m) } func (m *CurrencyConversionRequest) String() string { return proto.CompactTextString(m) }
func (*CurrencyConversionRequest) ProtoMessage() {} func (*CurrencyConversionRequest) ProtoMessage() {}
func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) { func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{20} return fileDescriptor_ca53982754088a9d, []int{20}
} }
func (m *CurrencyConversionRequest) XXX_Unmarshal(b []byte) error { func (m *CurrencyConversionRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CurrencyConversionRequest.Unmarshal(m, b) return xxx_messageInfo_CurrencyConversionRequest.Unmarshal(m, b)
} }
func (m *CurrencyConversionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *CurrencyConversionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CurrencyConversionRequest.Marshal(b, m, deterministic) return xxx_messageInfo_CurrencyConversionRequest.Marshal(b, m, deterministic)
} }
func (dst *CurrencyConversionRequest) XXX_Merge(src proto.Message) { func (m *CurrencyConversionRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CurrencyConversionRequest.Merge(dst, src) xxx_messageInfo_CurrencyConversionRequest.Merge(m, src)
} }
func (m *CurrencyConversionRequest) XXX_Size() int { func (m *CurrencyConversionRequest) XXX_Size() int {
return xxx_messageInfo_CurrencyConversionRequest.Size(m) return xxx_messageInfo_CurrencyConversionRequest.Size(m)
@ -975,16 +1008,17 @@ func (m *CreditCardInfo) Reset() { *m = CreditCardInfo{} }
func (m *CreditCardInfo) String() string { return proto.CompactTextString(m) } func (m *CreditCardInfo) String() string { return proto.CompactTextString(m) }
func (*CreditCardInfo) ProtoMessage() {} func (*CreditCardInfo) ProtoMessage() {}
func (*CreditCardInfo) Descriptor() ([]byte, []int) { func (*CreditCardInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{21} return fileDescriptor_ca53982754088a9d, []int{21}
} }
func (m *CreditCardInfo) XXX_Unmarshal(b []byte) error { func (m *CreditCardInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreditCardInfo.Unmarshal(m, b) return xxx_messageInfo_CreditCardInfo.Unmarshal(m, b)
} }
func (m *CreditCardInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *CreditCardInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreditCardInfo.Marshal(b, m, deterministic) return xxx_messageInfo_CreditCardInfo.Marshal(b, m, deterministic)
} }
func (dst *CreditCardInfo) XXX_Merge(src proto.Message) { func (m *CreditCardInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreditCardInfo.Merge(dst, src) xxx_messageInfo_CreditCardInfo.Merge(m, src)
} }
func (m *CreditCardInfo) XXX_Size() int { func (m *CreditCardInfo) XXX_Size() int {
return xxx_messageInfo_CreditCardInfo.Size(m) return xxx_messageInfo_CreditCardInfo.Size(m)
@ -1035,16 +1069,17 @@ func (m *ChargeRequest) Reset() { *m = ChargeRequest{} }
func (m *ChargeRequest) String() string { return proto.CompactTextString(m) } func (m *ChargeRequest) String() string { return proto.CompactTextString(m) }
func (*ChargeRequest) ProtoMessage() {} func (*ChargeRequest) ProtoMessage() {}
func (*ChargeRequest) Descriptor() ([]byte, []int) { func (*ChargeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{22} return fileDescriptor_ca53982754088a9d, []int{22}
} }
func (m *ChargeRequest) XXX_Unmarshal(b []byte) error { func (m *ChargeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChargeRequest.Unmarshal(m, b) return xxx_messageInfo_ChargeRequest.Unmarshal(m, b)
} }
func (m *ChargeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ChargeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ChargeRequest.Marshal(b, m, deterministic) return xxx_messageInfo_ChargeRequest.Marshal(b, m, deterministic)
} }
func (dst *ChargeRequest) XXX_Merge(src proto.Message) { func (m *ChargeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ChargeRequest.Merge(dst, src) xxx_messageInfo_ChargeRequest.Merge(m, src)
} }
func (m *ChargeRequest) XXX_Size() int { func (m *ChargeRequest) XXX_Size() int {
return xxx_messageInfo_ChargeRequest.Size(m) return xxx_messageInfo_ChargeRequest.Size(m)
@ -1080,16 +1115,17 @@ func (m *ChargeResponse) Reset() { *m = ChargeResponse{} }
func (m *ChargeResponse) String() string { return proto.CompactTextString(m) } func (m *ChargeResponse) String() string { return proto.CompactTextString(m) }
func (*ChargeResponse) ProtoMessage() {} func (*ChargeResponse) ProtoMessage() {}
func (*ChargeResponse) Descriptor() ([]byte, []int) { func (*ChargeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{23} return fileDescriptor_ca53982754088a9d, []int{23}
} }
func (m *ChargeResponse) XXX_Unmarshal(b []byte) error { func (m *ChargeResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChargeResponse.Unmarshal(m, b) return xxx_messageInfo_ChargeResponse.Unmarshal(m, b)
} }
func (m *ChargeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *ChargeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ChargeResponse.Marshal(b, m, deterministic) return xxx_messageInfo_ChargeResponse.Marshal(b, m, deterministic)
} }
func (dst *ChargeResponse) XXX_Merge(src proto.Message) { func (m *ChargeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ChargeResponse.Merge(dst, src) xxx_messageInfo_ChargeResponse.Merge(m, src)
} }
func (m *ChargeResponse) XXX_Size() int { func (m *ChargeResponse) XXX_Size() int {
return xxx_messageInfo_ChargeResponse.Size(m) return xxx_messageInfo_ChargeResponse.Size(m)
@ -1119,16 +1155,17 @@ func (m *OrderItem) Reset() { *m = OrderItem{} }
func (m *OrderItem) String() string { return proto.CompactTextString(m) } func (m *OrderItem) String() string { return proto.CompactTextString(m) }
func (*OrderItem) ProtoMessage() {} func (*OrderItem) ProtoMessage() {}
func (*OrderItem) Descriptor() ([]byte, []int) { func (*OrderItem) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{24} return fileDescriptor_ca53982754088a9d, []int{24}
} }
func (m *OrderItem) XXX_Unmarshal(b []byte) error { func (m *OrderItem) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OrderItem.Unmarshal(m, b) return xxx_messageInfo_OrderItem.Unmarshal(m, b)
} }
func (m *OrderItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *OrderItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OrderItem.Marshal(b, m, deterministic) return xxx_messageInfo_OrderItem.Marshal(b, m, deterministic)
} }
func (dst *OrderItem) XXX_Merge(src proto.Message) { func (m *OrderItem) XXX_Merge(src proto.Message) {
xxx_messageInfo_OrderItem.Merge(dst, src) xxx_messageInfo_OrderItem.Merge(m, src)
} }
func (m *OrderItem) XXX_Size() int { func (m *OrderItem) XXX_Size() int {
return xxx_messageInfo_OrderItem.Size(m) return xxx_messageInfo_OrderItem.Size(m)
@ -1168,16 +1205,17 @@ func (m *OrderResult) Reset() { *m = OrderResult{} }
func (m *OrderResult) String() string { return proto.CompactTextString(m) } func (m *OrderResult) String() string { return proto.CompactTextString(m) }
func (*OrderResult) ProtoMessage() {} func (*OrderResult) ProtoMessage() {}
func (*OrderResult) Descriptor() ([]byte, []int) { func (*OrderResult) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{25} return fileDescriptor_ca53982754088a9d, []int{25}
} }
func (m *OrderResult) XXX_Unmarshal(b []byte) error { func (m *OrderResult) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OrderResult.Unmarshal(m, b) return xxx_messageInfo_OrderResult.Unmarshal(m, b)
} }
func (m *OrderResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *OrderResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OrderResult.Marshal(b, m, deterministic) return xxx_messageInfo_OrderResult.Marshal(b, m, deterministic)
} }
func (dst *OrderResult) XXX_Merge(src proto.Message) { func (m *OrderResult) XXX_Merge(src proto.Message) {
xxx_messageInfo_OrderResult.Merge(dst, src) xxx_messageInfo_OrderResult.Merge(m, src)
} }
func (m *OrderResult) XXX_Size() int { func (m *OrderResult) XXX_Size() int {
return xxx_messageInfo_OrderResult.Size(m) return xxx_messageInfo_OrderResult.Size(m)
@ -1235,16 +1273,17 @@ func (m *SendOrderConfirmationRequest) Reset() { *m = SendOrderConfirmat
func (m *SendOrderConfirmationRequest) String() string { return proto.CompactTextString(m) } func (m *SendOrderConfirmationRequest) String() string { return proto.CompactTextString(m) }
func (*SendOrderConfirmationRequest) ProtoMessage() {} func (*SendOrderConfirmationRequest) ProtoMessage() {}
func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) { func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{26} return fileDescriptor_ca53982754088a9d, []int{26}
} }
func (m *SendOrderConfirmationRequest) XXX_Unmarshal(b []byte) error { func (m *SendOrderConfirmationRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendOrderConfirmationRequest.Unmarshal(m, b) return xxx_messageInfo_SendOrderConfirmationRequest.Unmarshal(m, b)
} }
func (m *SendOrderConfirmationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *SendOrderConfirmationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SendOrderConfirmationRequest.Marshal(b, m, deterministic) return xxx_messageInfo_SendOrderConfirmationRequest.Marshal(b, m, deterministic)
} }
func (dst *SendOrderConfirmationRequest) XXX_Merge(src proto.Message) { func (m *SendOrderConfirmationRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SendOrderConfirmationRequest.Merge(dst, src) xxx_messageInfo_SendOrderConfirmationRequest.Merge(m, src)
} }
func (m *SendOrderConfirmationRequest) XXX_Size() int { func (m *SendOrderConfirmationRequest) XXX_Size() int {
return xxx_messageInfo_SendOrderConfirmationRequest.Size(m) return xxx_messageInfo_SendOrderConfirmationRequest.Size(m)
@ -1284,16 +1323,17 @@ func (m *PlaceOrderRequest) Reset() { *m = PlaceOrderRequest{} }
func (m *PlaceOrderRequest) String() string { return proto.CompactTextString(m) } func (m *PlaceOrderRequest) String() string { return proto.CompactTextString(m) }
func (*PlaceOrderRequest) ProtoMessage() {} func (*PlaceOrderRequest) ProtoMessage() {}
func (*PlaceOrderRequest) Descriptor() ([]byte, []int) { func (*PlaceOrderRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{27} return fileDescriptor_ca53982754088a9d, []int{27}
} }
func (m *PlaceOrderRequest) XXX_Unmarshal(b []byte) error { func (m *PlaceOrderRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PlaceOrderRequest.Unmarshal(m, b) return xxx_messageInfo_PlaceOrderRequest.Unmarshal(m, b)
} }
func (m *PlaceOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlaceOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PlaceOrderRequest.Marshal(b, m, deterministic) return xxx_messageInfo_PlaceOrderRequest.Marshal(b, m, deterministic)
} }
func (dst *PlaceOrderRequest) XXX_Merge(src proto.Message) { func (m *PlaceOrderRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlaceOrderRequest.Merge(dst, src) xxx_messageInfo_PlaceOrderRequest.Merge(m, src)
} }
func (m *PlaceOrderRequest) XXX_Size() int { func (m *PlaceOrderRequest) XXX_Size() int {
return xxx_messageInfo_PlaceOrderRequest.Size(m) return xxx_messageInfo_PlaceOrderRequest.Size(m)
@ -1350,16 +1390,17 @@ func (m *PlaceOrderResponse) Reset() { *m = PlaceOrderResponse{} }
func (m *PlaceOrderResponse) String() string { return proto.CompactTextString(m) } func (m *PlaceOrderResponse) String() string { return proto.CompactTextString(m) }
func (*PlaceOrderResponse) ProtoMessage() {} func (*PlaceOrderResponse) ProtoMessage() {}
func (*PlaceOrderResponse) Descriptor() ([]byte, []int) { func (*PlaceOrderResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{28} return fileDescriptor_ca53982754088a9d, []int{28}
} }
func (m *PlaceOrderResponse) XXX_Unmarshal(b []byte) error { func (m *PlaceOrderResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PlaceOrderResponse.Unmarshal(m, b) return xxx_messageInfo_PlaceOrderResponse.Unmarshal(m, b)
} }
func (m *PlaceOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlaceOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PlaceOrderResponse.Marshal(b, m, deterministic) return xxx_messageInfo_PlaceOrderResponse.Marshal(b, m, deterministic)
} }
func (dst *PlaceOrderResponse) XXX_Merge(src proto.Message) { func (m *PlaceOrderResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlaceOrderResponse.Merge(dst, src) xxx_messageInfo_PlaceOrderResponse.Merge(m, src)
} }
func (m *PlaceOrderResponse) XXX_Size() int { func (m *PlaceOrderResponse) XXX_Size() int {
return xxx_messageInfo_PlaceOrderResponse.Size(m) return xxx_messageInfo_PlaceOrderResponse.Size(m)
@ -1389,16 +1430,17 @@ func (m *AdRequest) Reset() { *m = AdRequest{} }
func (m *AdRequest) String() string { return proto.CompactTextString(m) } func (m *AdRequest) String() string { return proto.CompactTextString(m) }
func (*AdRequest) ProtoMessage() {} func (*AdRequest) ProtoMessage() {}
func (*AdRequest) Descriptor() ([]byte, []int) { func (*AdRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{29} return fileDescriptor_ca53982754088a9d, []int{29}
} }
func (m *AdRequest) XXX_Unmarshal(b []byte) error { func (m *AdRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AdRequest.Unmarshal(m, b) return xxx_messageInfo_AdRequest.Unmarshal(m, b)
} }
func (m *AdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *AdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AdRequest.Marshal(b, m, deterministic) return xxx_messageInfo_AdRequest.Marshal(b, m, deterministic)
} }
func (dst *AdRequest) XXX_Merge(src proto.Message) { func (m *AdRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AdRequest.Merge(dst, src) xxx_messageInfo_AdRequest.Merge(m, src)
} }
func (m *AdRequest) XXX_Size() int { func (m *AdRequest) XXX_Size() int {
return xxx_messageInfo_AdRequest.Size(m) return xxx_messageInfo_AdRequest.Size(m)
@ -1427,16 +1469,17 @@ func (m *AdResponse) Reset() { *m = AdResponse{} }
func (m *AdResponse) String() string { return proto.CompactTextString(m) } func (m *AdResponse) String() string { return proto.CompactTextString(m) }
func (*AdResponse) ProtoMessage() {} func (*AdResponse) ProtoMessage() {}
func (*AdResponse) Descriptor() ([]byte, []int) { func (*AdResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{30} return fileDescriptor_ca53982754088a9d, []int{30}
} }
func (m *AdResponse) XXX_Unmarshal(b []byte) error { func (m *AdResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AdResponse.Unmarshal(m, b) return xxx_messageInfo_AdResponse.Unmarshal(m, b)
} }
func (m *AdResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *AdResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AdResponse.Marshal(b, m, deterministic) return xxx_messageInfo_AdResponse.Marshal(b, m, deterministic)
} }
func (dst *AdResponse) XXX_Merge(src proto.Message) { func (m *AdResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AdResponse.Merge(dst, src) xxx_messageInfo_AdResponse.Merge(m, src)
} }
func (m *AdResponse) XXX_Size() int { func (m *AdResponse) XXX_Size() int {
return xxx_messageInfo_AdResponse.Size(m) return xxx_messageInfo_AdResponse.Size(m)
@ -1468,16 +1511,17 @@ func (m *Ad) Reset() { *m = Ad{} }
func (m *Ad) String() string { return proto.CompactTextString(m) } func (m *Ad) String() string { return proto.CompactTextString(m) }
func (*Ad) ProtoMessage() {} func (*Ad) ProtoMessage() {}
func (*Ad) Descriptor() ([]byte, []int) { func (*Ad) Descriptor() ([]byte, []int) {
return fileDescriptor_demo_88bb8fdac9cd6be5, []int{31} return fileDescriptor_ca53982754088a9d, []int{31}
} }
func (m *Ad) XXX_Unmarshal(b []byte) error { func (m *Ad) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Ad.Unmarshal(m, b) return xxx_messageInfo_Ad.Unmarshal(m, b)
} }
func (m *Ad) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Ad) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Ad.Marshal(b, m, deterministic) return xxx_messageInfo_Ad.Marshal(b, m, deterministic)
} }
func (dst *Ad) XXX_Merge(src proto.Message) { func (m *Ad) XXX_Merge(src proto.Message) {
xxx_messageInfo_Ad.Merge(dst, src) xxx_messageInfo_Ad.Merge(m, src)
} }
func (m *Ad) XXX_Size() int { func (m *Ad) XXX_Size() int {
return xxx_messageInfo_Ad.Size(m) return xxx_messageInfo_Ad.Size(m)
@ -2319,101 +2363,102 @@ var _AdService_serviceDesc = grpc.ServiceDesc{
Metadata: "demo.proto", Metadata: "demo.proto",
} }
func init() { proto.RegisterFile("demo.proto", fileDescriptor_demo_88bb8fdac9cd6be5) } func init() { proto.RegisterFile("demo.proto", fileDescriptor_ca53982754088a9d) }
var fileDescriptor_demo_88bb8fdac9cd6be5 = []byte{ var fileDescriptor_ca53982754088a9d = []byte{
// 1483 bytes of a gzipped FileDescriptorProto // 1500 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x72, 0xd3, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xef, 0x72, 0x13, 0xb7,
0x17, 0x8f, 0x92, 0x38, 0x8e, 0x8f, 0x63, 0x27, 0xd9, 0x7f, 0x12, 0x8c, 0xc2, 0x47, 0xd8, 0x0c, 0x16, 0xcf, 0x26, 0xb1, 0x1d, 0x1f, 0xc7, 0x4e, 0xa2, 0x9b, 0x04, 0xb3, 0x81, 0x10, 0x94, 0x81,
0xfc, 0xa1, 0x80, 0x61, 0xd2, 0xce, 0x70, 0x01, 0x2d, 0xcd, 0x98, 0x8c, 0xf1, 0x14, 0x0a, 0x55, 0x0b, 0x17, 0x08, 0x4c, 0xee, 0x9d, 0xe1, 0x03, 0xdc, 0xd2, 0x8c, 0xc9, 0x18, 0x4f, 0xa1, 0xd0,
0xa0, 0x43, 0x87, 0x4e, 0x3d, 0x42, 0xbb, 0x60, 0x95, 0x48, 0x2b, 0x76, 0x57, 0x19, 0xcc, 0x65, 0x0d, 0xe9, 0xd0, 0xa1, 0x53, 0xcf, 0xb2, 0x12, 0xf1, 0x96, 0xec, 0x6a, 0x91, 0xb4, 0x19, 0xcc,
0xfb, 0x00, 0xbd, 0xef, 0x23, 0xf4, 0x05, 0xfa, 0x0e, 0xbd, 0xef, 0x2b, 0xf4, 0x39, 0x3a, 0xbb, 0xc7, 0xf6, 0x01, 0xfa, 0x1e, 0x7d, 0x81, 0xce, 0xf4, 0x11, 0xfa, 0xbd, 0xaf, 0xd0, 0xe7, 0xe8,
0xd2, 0xea, 0xcb, 0x76, 0x02, 0x37, 0xbd, 0xf3, 0x9e, 0xfd, 0xe9, 0x9c, 0xdf, 0x39, 0x7b, 0xbe, 0x48, 0xbb, 0xda, 0x7f, 0xb1, 0x13, 0xf8, 0xd2, 0x6f, 0xab, 0xa3, 0x9f, 0xce, 0xf9, 0xe9, 0xe8,
0x12, 0x00, 0x42, 0x03, 0xd6, 0x8d, 0x38, 0x93, 0x0c, 0x35, 0x47, 0x7e, 0x24, 0x24, 0xe5, 0x62, 0xfc, 0xb3, 0x01, 0x08, 0x0d, 0xd8, 0x4e, 0xc4, 0x99, 0x64, 0xa8, 0x35, 0xf2, 0x23, 0x21, 0x29,
0xc4, 0x22, 0x7c, 0x00, 0xcb, 0x3d, 0x97, 0xcb, 0x81, 0xa4, 0x01, 0x3a, 0x0f, 0x10, 0x71, 0x46, 0x17, 0x23, 0x16, 0xe1, 0x7d, 0x58, 0xe8, 0xb9, 0x5c, 0x0e, 0x24, 0x0d, 0xd0, 0x65, 0x80, 0x88,
0x62, 0x4f, 0x0e, 0x7d, 0xd2, 0xb1, 0x76, 0xac, 0xab, 0x0d, 0xa7, 0x91, 0x4a, 0x06, 0x04, 0xd9, 0x33, 0x12, 0x7b, 0x72, 0xe8, 0x93, 0xae, 0xb5, 0x65, 0xdd, 0x68, 0x3a, 0xcd, 0x54, 0x32, 0x20,
0xb0, 0xfc, 0x2e, 0x76, 0x43, 0xe9, 0xcb, 0x71, 0x67, 0x7e, 0xc7, 0xba, 0x5a, 0x73, 0xb2, 0x33, 0xc8, 0x86, 0x85, 0xf7, 0xb1, 0x1b, 0x4a, 0x5f, 0x8e, 0xbb, 0xb3, 0x5b, 0xd6, 0x8d, 0x9a, 0x93,
0x7e, 0x06, 0xed, 0x7d, 0x42, 0x94, 0x16, 0x87, 0xbe, 0x8b, 0xa9, 0x90, 0xe8, 0x0c, 0xd4, 0x63, 0xad, 0xf1, 0x4b, 0xe8, 0xec, 0x11, 0xa2, 0xb4, 0x38, 0xf4, 0x7d, 0x4c, 0x85, 0x44, 0x17, 0xa0,
0x41, 0x79, 0xae, 0x69, 0x49, 0x1d, 0x07, 0x04, 0x5d, 0x83, 0x45, 0x5f, 0xd2, 0x40, 0xab, 0x68, 0x11, 0x0b, 0xca, 0x73, 0x4d, 0x75, 0xb5, 0x1c, 0x10, 0x74, 0x13, 0xe6, 0x7d, 0x49, 0x03, 0xad,
0xee, 0x6d, 0x76, 0x0b, 0x6c, 0xba, 0x86, 0x8a, 0xa3, 0x21, 0xf8, 0x3a, 0xac, 0x1d, 0x04, 0x91, 0xa2, 0xb5, 0xbb, 0xb6, 0x53, 0x60, 0xb3, 0x63, 0xa8, 0x38, 0x1a, 0x82, 0x6f, 0xc1, 0xf2, 0x7e,
0x1c, 0x2b, 0xf1, 0x69, 0x7a, 0xf1, 0x35, 0x68, 0xf7, 0xa9, 0xfc, 0x28, 0xe8, 0x23, 0x58, 0x54, 0x10, 0xc9, 0xb1, 0x12, 0x9f, 0xa7, 0x17, 0xdf, 0x84, 0x4e, 0x9f, 0xca, 0x4f, 0x82, 0x3e, 0x85,
0xb8, 0xd9, 0x1c, 0xaf, 0x43, 0x4d, 0x11, 0x10, 0x9d, 0xf9, 0x9d, 0x85, 0xd9, 0x24, 0x13, 0x0c, 0x79, 0x85, 0x9b, 0xce, 0xf1, 0x16, 0xd4, 0x14, 0x01, 0xd1, 0x9d, 0xdd, 0x9a, 0x9b, 0x4e, 0x32,
0xae, 0x43, 0x4d, 0xb3, 0xc4, 0xdf, 0x83, 0xfd, 0xc8, 0x17, 0xd2, 0xa1, 0x1e, 0x0b, 0x02, 0x1a, 0xc1, 0xe0, 0x06, 0xd4, 0x34, 0x4b, 0xfc, 0x2d, 0xd8, 0x4f, 0x7d, 0x21, 0x1d, 0xea, 0xb1, 0x20,
0x12, 0x57, 0xfa, 0x2c, 0x14, 0xa7, 0x06, 0xe4, 0x22, 0x34, 0xf3, 0xb0, 0x27, 0x26, 0x1b, 0x0e, 0xa0, 0x21, 0x71, 0xa5, 0xcf, 0x42, 0x71, 0xae, 0x43, 0xae, 0x40, 0x2b, 0x77, 0x7b, 0x62, 0xb2,
0x64, 0x71, 0x17, 0xf8, 0x2b, 0xd8, 0x9e, 0xaa, 0x57, 0x44, 0x2c, 0x14, 0xb4, 0xfa, 0xbd, 0x35, 0xe9, 0x40, 0xe6, 0x77, 0x81, 0xbf, 0x80, 0x8d, 0x89, 0x7a, 0x45, 0xc4, 0x42, 0x41, 0xab, 0xe7,
0xf1, 0xfd, 0xef, 0x16, 0xd4, 0x9f, 0x26, 0x47, 0xd4, 0x86, 0xf9, 0x8c, 0xc0, 0xbc, 0x4f, 0x10, 0xad, 0x53, 0xe7, 0x7f, 0xb7, 0xa0, 0xf1, 0x22, 0x59, 0xa2, 0x0e, 0xcc, 0x66, 0x04, 0x66, 0x7d,
0x82, 0xc5, 0xd0, 0x0d, 0xa8, 0x7e, 0x8d, 0x86, 0xa3, 0x7f, 0xa3, 0x1d, 0x68, 0x12, 0x2a, 0x3c, 0x82, 0x10, 0xcc, 0x87, 0x6e, 0x40, 0xf5, 0x6b, 0x34, 0x1d, 0xfd, 0x8d, 0xb6, 0xa0, 0x45, 0xa8,
0xee, 0x47, 0xca, 0x50, 0x67, 0x41, 0x5f, 0x15, 0x45, 0xa8, 0x03, 0xf5, 0xc8, 0xf7, 0x64, 0xcc, 0xf0, 0xb8, 0x1f, 0x29, 0x43, 0xdd, 0x39, 0xbd, 0x55, 0x14, 0xa1, 0x2e, 0x34, 0x22, 0xdf, 0x93,
0x69, 0x67, 0x51, 0xdf, 0x9a, 0x23, 0xba, 0x05, 0x8d, 0x88, 0xfb, 0x1e, 0x1d, 0xc6, 0x82, 0x74, 0x31, 0xa7, 0xdd, 0x79, 0xbd, 0x6b, 0x96, 0xe8, 0x2e, 0x34, 0x23, 0xee, 0x7b, 0x74, 0x18, 0x0b,
0x6a, 0xfa, 0x89, 0x51, 0x29, 0x7a, 0x8f, 0x59, 0x48, 0xc7, 0xce, 0xb2, 0x06, 0x3d, 0x17, 0x04, 0xd2, 0xad, 0xe9, 0x27, 0x46, 0x25, 0xef, 0x3d, 0x63, 0x21, 0x1d, 0x3b, 0x0b, 0x1a, 0x74, 0x28,
0x3f, 0x84, 0x0d, 0xe5, 0x5c, 0xca, 0x2f, 0xf7, 0xea, 0x36, 0x2c, 0xa7, 0x2e, 0x24, 0x2e, 0x35, 0x08, 0xda, 0x04, 0xf0, 0x5c, 0x49, 0x8f, 0x18, 0xf7, 0xa9, 0xe8, 0xd6, 0x13, 0xf2, 0xb9, 0x04,
0xf7, 0x36, 0x4a, 0x7a, 0xd2, 0x0f, 0x9c, 0x0c, 0x85, 0x77, 0x61, 0xbd, 0x4f, 0x8d, 0x22, 0x13, 0x3f, 0x81, 0x55, 0x75, 0xf9, 0x94, 0x7f, 0x7e, 0xeb, 0x7b, 0xb0, 0x90, 0x5e, 0x31, 0xb9, 0x72,
0xf5, 0x8a, 0xbf, 0xf8, 0x26, 0x6c, 0x1e, 0x52, 0x97, 0x7b, 0xa3, 0xdc, 0x60, 0x02, 0xdc, 0x80, 0x6b, 0x77, 0xb5, 0x64, 0x27, 0x3d, 0xe0, 0x64, 0x28, 0xbc, 0x0d, 0x2b, 0x7d, 0x6a, 0x14, 0x99,
0xda, 0xbb, 0x98, 0xf2, 0x71, 0x8a, 0x4d, 0x0e, 0xf8, 0x21, 0x6c, 0x55, 0xe1, 0x29, 0xbf, 0x2e, 0x57, 0xa9, 0xf8, 0x03, 0xdf, 0x81, 0xb5, 0x03, 0xea, 0x72, 0x6f, 0x94, 0x1b, 0x4c, 0x80, 0xab,
0xd4, 0x39, 0x15, 0xf1, 0xd1, 0x29, 0xf4, 0x0c, 0x08, 0x87, 0xb0, 0xda, 0xa7, 0xf2, 0xbb, 0x98, 0x50, 0x7b, 0x1f, 0x53, 0x3e, 0x4e, 0xb1, 0xc9, 0x02, 0x3f, 0x81, 0xf5, 0x2a, 0x3c, 0xe5, 0xb7,
0x49, 0x6a, 0x4c, 0x76, 0xa1, 0xee, 0x12, 0xc2, 0xa9, 0x10, 0xda, 0x68, 0x55, 0xc5, 0x7e, 0x72, 0x03, 0x0d, 0x4e, 0x45, 0x7c, 0x7c, 0x0e, 0x3d, 0x03, 0xc2, 0x21, 0x2c, 0xf5, 0xa9, 0xfc, 0x26,
0xe7, 0x18, 0xd0, 0xa7, 0x65, 0xe5, 0x3e, 0xac, 0xe5, 0xf6, 0x52, 0xce, 0x37, 0x61, 0xd9, 0x63, 0x66, 0x92, 0x1a, 0x93, 0x3b, 0xd0, 0x70, 0x09, 0xe1, 0x54, 0x08, 0x6d, 0xb4, 0xaa, 0x62, 0x2f,
0x42, 0xea, 0xb7, 0xb1, 0x66, 0xbe, 0x4d, 0x5d, 0x61, 0xd4, 0xd3, 0x30, 0x58, 0x3b, 0x1c, 0xf9, 0xd9, 0x73, 0x0c, 0xe8, 0xf3, 0xa2, 0x76, 0x0f, 0x96, 0x73, 0x7b, 0x29, 0xe7, 0x3b, 0xb0, 0xe0,
0xd1, 0x13, 0x4e, 0x28, 0xff, 0x4f, 0x38, 0x7f, 0x01, 0xeb, 0x05, 0x83, 0x79, 0x7a, 0x4b, 0xee, 0x31, 0x21, 0xf5, 0xdb, 0x59, 0x53, 0xdf, 0xae, 0xa1, 0x30, 0x87, 0x82, 0x60, 0x06, 0xcb, 0x07,
0x7a, 0x6f, 0xfd, 0xf0, 0x4d, 0x5e, 0x3b, 0x60, 0x44, 0x03, 0x82, 0x7f, 0xb3, 0xa0, 0x9e, 0xda, 0x23, 0x3f, 0x7a, 0xce, 0x09, 0xe5, 0xff, 0x08, 0xe7, 0xff, 0xc1, 0x4a, 0xc1, 0x60, 0x1e, 0xfe,
0x45, 0x97, 0xa1, 0x2d, 0x24, 0xa7, 0x54, 0x0e, 0x8b, 0x2c, 0x1b, 0x4e, 0x2b, 0x91, 0x1a, 0x18, 0x92, 0xbb, 0xde, 0x3b, 0x3f, 0x3c, 0xca, 0x73, 0x0b, 0x8c, 0x68, 0x40, 0xf0, 0x2f, 0x16, 0x34,
0x82, 0x45, 0xcf, 0xb4, 0xb1, 0x86, 0xa3, 0x7f, 0xab, 0x04, 0x10, 0xd2, 0x95, 0x34, 0xcd, 0xf7, 0x52, 0xbb, 0xe8, 0x1a, 0x74, 0x84, 0xe4, 0x94, 0xca, 0x61, 0x91, 0x65, 0xd3, 0x69, 0x27, 0x52,
0xe4, 0xa0, 0x32, 0xdd, 0x63, 0x71, 0x28, 0xf9, 0xd8, 0x64, 0x7a, 0x7a, 0x44, 0x67, 0x61, 0xf9, 0x03, 0x43, 0x30, 0xef, 0x99, 0x32, 0xd7, 0x74, 0xf4, 0xb7, 0x0a, 0x00, 0x21, 0x5d, 0x49, 0xd3,
0x83, 0x1f, 0x0d, 0x3d, 0x46, 0xa8, 0x4e, 0xf4, 0x9a, 0x53, 0xff, 0xe0, 0x47, 0x3d, 0x46, 0x28, 0x7c, 0x48, 0x16, 0x2a, 0x13, 0x3c, 0x16, 0x87, 0x92, 0x8f, 0x4d, 0x26, 0xa4, 0x4b, 0x74, 0x11,
0x7e, 0x01, 0x35, 0x1d, 0x4a, 0xb4, 0x0b, 0x2d, 0x2f, 0xe6, 0x9c, 0x86, 0xde, 0x38, 0x01, 0x26, 0x16, 0x3e, 0xfa, 0xd1, 0xd0, 0x63, 0x84, 0xea, 0x44, 0xa8, 0x39, 0x8d, 0x8f, 0x7e, 0xd4, 0x63,
0x6c, 0x56, 0x8c, 0x50, 0xa1, 0x95, 0xe1, 0x38, 0xf4, 0xa5, 0xd0, 0x6c, 0x16, 0x9c, 0xe4, 0xa0, 0x84, 0xe2, 0x57, 0x50, 0xd3, 0xae, 0x44, 0xdb, 0xd0, 0xf6, 0x62, 0xce, 0x69, 0xe8, 0x8d, 0x13,
0xa4, 0xa1, 0x1b, 0x32, 0xa1, 0xe9, 0xd4, 0x9c, 0xe4, 0x80, 0xfb, 0x70, 0xa1, 0x4f, 0xe5, 0x61, 0x60, 0xc2, 0x66, 0xd1, 0x08, 0x15, 0x5a, 0x19, 0x8e, 0x43, 0x5f, 0x0a, 0xcd, 0x66, 0xce, 0x49,
0x1c, 0x45, 0x8c, 0x4b, 0x4a, 0x7a, 0x89, 0x1e, 0x9f, 0xe6, 0x79, 0x79, 0x19, 0xda, 0x25, 0x93, 0x16, 0x4a, 0x1a, 0xba, 0x21, 0x13, 0x9a, 0x4e, 0xcd, 0x49, 0x16, 0xb8, 0x0f, 0x9b, 0x7d, 0x2a,
0xa6, 0x21, 0xb4, 0x8a, 0x36, 0x05, 0xfe, 0x11, 0xce, 0xf6, 0x32, 0x41, 0x78, 0x4c, 0xb9, 0xf0, 0x0f, 0xe2, 0x28, 0x62, 0x5c, 0x52, 0xd2, 0x4b, 0xf4, 0xf8, 0x34, 0x8f, 0xcb, 0x6b, 0xd0, 0x29,
0x59, 0x68, 0x1e, 0xf9, 0x0a, 0x2c, 0xbe, 0xe6, 0x2c, 0x38, 0x21, 0x47, 0xf4, 0xbd, 0x6a, 0x69, 0x99, 0x34, 0x05, 0xa3, 0x5d, 0xb4, 0x29, 0xf0, 0xf7, 0x70, 0xb1, 0x97, 0x09, 0xc2, 0x13, 0xca,
0x92, 0x25, 0x8e, 0x25, 0x91, 0x5c, 0x92, 0x4c, 0x07, 0xe0, 0x1f, 0x0b, 0xda, 0x3d, 0x4e, 0x89, 0x85, 0xcf, 0x42, 0xf3, 0xc8, 0xd7, 0x61, 0xfe, 0x2d, 0x67, 0xc1, 0x19, 0x31, 0xa2, 0xf7, 0x55,
0xaf, 0xfa, 0x31, 0x19, 0x84, 0xaf, 0x19, 0xba, 0x01, 0xc8, 0xd3, 0x92, 0xa1, 0xe7, 0x72, 0x32, 0xc9, 0x93, 0x2c, 0xb9, 0x58, 0xe2, 0xc9, 0xba, 0x64, 0xda, 0x01, 0x7f, 0x59, 0xd0, 0xe9, 0x71,
0x0c, 0xe3, 0xe0, 0x15, 0xe5, 0x69, 0x3c, 0xd6, 0xbc, 0x0c, 0xfb, 0xad, 0x96, 0xa3, 0x2b, 0xb0, 0x4a, 0x7c, 0x55, 0xaf, 0xc9, 0x20, 0x7c, 0xcb, 0xd0, 0x6d, 0x40, 0x9e, 0x96, 0x0c, 0x3d, 0x97,
0x5a, 0x44, 0x7b, 0xc7, 0xc7, 0xe9, 0xc8, 0x69, 0xe5, 0xd0, 0xde, 0xf1, 0x31, 0xfa, 0x12, 0xb6, 0x93, 0x61, 0x18, 0x07, 0x6f, 0x28, 0x4f, 0xfd, 0xb1, 0xec, 0x65, 0xd8, 0xaf, 0xb5, 0x1c, 0x5d,
0x8b, 0x38, 0xfa, 0x3e, 0xf2, 0xb9, 0x6e, 0x8f, 0xc3, 0x31, 0x75, 0x79, 0x1a, 0xbb, 0x4e, 0xfe, 0x87, 0xa5, 0x22, 0xda, 0x3b, 0x39, 0x49, 0x5b, 0x52, 0x3b, 0x87, 0xf6, 0x4e, 0x4e, 0xd0, 0xff,
0xcd, 0x41, 0x06, 0xf8, 0x81, 0xba, 0x1c, 0xdd, 0x87, 0x73, 0x33, 0x3e, 0x0f, 0x58, 0x28, 0x47, 0x61, 0xa3, 0x88, 0xa3, 0x1f, 0x22, 0x9f, 0xeb, 0xf2, 0x39, 0x1c, 0x53, 0x97, 0xa7, 0xbe, 0xeb,
0xfa, 0xc9, 0x6b, 0xce, 0xd9, 0x69, 0xdf, 0x3f, 0x56, 0x00, 0x3c, 0x86, 0x56, 0x6f, 0xe4, 0xf2, 0xe6, 0x67, 0xf6, 0x33, 0xc0, 0x77, 0xd4, 0xe5, 0xe8, 0x11, 0x5c, 0x9a, 0x72, 0x3c, 0x60, 0xa1,
0x37, 0x59, 0x4d, 0x7f, 0x06, 0x4b, 0x6e, 0xa0, 0x32, 0xe4, 0x84, 0xe0, 0xa5, 0x08, 0x74, 0x0f, 0x1c, 0xe9, 0x27, 0xaf, 0x39, 0x17, 0x27, 0x9d, 0x7f, 0xa6, 0x00, 0x78, 0x0c, 0xed, 0xde, 0xc8,
0x9a, 0x05, 0xeb, 0xe9, 0x40, 0xdc, 0x2e, 0x57, 0x48, 0x29, 0x88, 0x0e, 0xe4, 0x4c, 0xf0, 0x1d, 0xe5, 0x47, 0x59, 0x4e, 0xff, 0x07, 0xea, 0x6e, 0xa0, 0x22, 0xe4, 0x0c, 0xe7, 0xa5, 0x08, 0xf4,
0x68, 0x1b, 0xd3, 0xf9, 0xd3, 0x4b, 0xee, 0x86, 0xc2, 0xf5, 0xb4, 0x0b, 0x59, 0xb1, 0xb4, 0x0a, 0x10, 0x5a, 0x05, 0xeb, 0x69, 0xc3, 0xdc, 0x28, 0x67, 0x48, 0xc9, 0x89, 0x0e, 0xe4, 0x4c, 0xf0,
0xd2, 0x01, 0xc1, 0x3f, 0x41, 0x43, 0x57, 0x98, 0x9e, 0xf9, 0x66, 0x1a, 0x5b, 0xa7, 0x4e, 0x63, 0x7d, 0xe8, 0x18, 0xd3, 0xf9, 0xd3, 0x4b, 0xee, 0x86, 0xc2, 0xf5, 0xf4, 0x15, 0xb2, 0x64, 0x69,
0x95, 0x15, 0xaa, 0x33, 0xa4, 0x3c, 0xa7, 0x66, 0x85, 0xba, 0xc7, 0xbf, 0xcc, 0x43, 0xd3, 0x94, 0x17, 0xa4, 0x03, 0x82, 0x7f, 0x80, 0xa6, 0xce, 0x30, 0x3d, 0x13, 0x98, 0x6e, 0x6d, 0x9d, 0xdb,
0x70, 0x7c, 0x24, 0x55, 0xa1, 0x30, 0x75, 0xcc, 0x09, 0xd5, 0xf5, 0x79, 0x40, 0xd0, 0x6d, 0xd8, 0xad, 0x55, 0x54, 0xa8, 0xca, 0x90, 0xf2, 0x9c, 0x18, 0x15, 0x6a, 0x1f, 0xff, 0x34, 0x0b, 0x2d,
0x10, 0x23, 0x3f, 0x8a, 0x54, 0x6d, 0x17, 0x8b, 0x3c, 0xc9, 0x26, 0x64, 0xee, 0x9e, 0x65, 0xc5, 0x93, 0xc2, 0xf1, 0xb1, 0x54, 0x89, 0xc2, 0xd4, 0x32, 0x27, 0xd4, 0xd0, 0xeb, 0x01, 0x41, 0xf7,
0x8e, 0xee, 0x40, 0x2b, 0xfb, 0x42, 0xb3, 0x59, 0x98, 0xc9, 0x66, 0xc5, 0x00, 0x7b, 0x4c, 0x48, 0x60, 0x55, 0x8c, 0xfc, 0x28, 0x52, 0xb9, 0x5d, 0x4c, 0xf2, 0x24, 0x9a, 0x90, 0xd9, 0x7b, 0x99,
0x74, 0x1f, 0xd6, 0xb2, 0x0f, 0x4d, 0x6f, 0x58, 0x3c, 0xa1, 0x83, 0xad, 0x1a, 0xb4, 0xe9, 0x19, 0x25, 0x3b, 0xba, 0x0f, 0xed, 0xec, 0x84, 0x66, 0x33, 0x37, 0x95, 0xcd, 0xa2, 0x01, 0xf6, 0x98,
0x37, 0x4c, 0x27, 0xab, 0xe9, 0x4e, 0xb6, 0x55, 0xfa, 0x2a, 0x0b, 0xa8, 0x69, 0x65, 0x04, 0xce, 0x90, 0xe8, 0x11, 0x2c, 0x67, 0x07, 0x4d, 0x6d, 0x98, 0x3f, 0xa3, 0x82, 0x2d, 0x19, 0xb4, 0xa9,
0x1d, 0xd2, 0x90, 0x68, 0x79, 0x8f, 0x85, 0xaf, 0x7d, 0x1e, 0xe8, 0xb4, 0x29, 0x8c, 0x1b, 0x1a, 0x19, 0xb7, 0x4d, 0x25, 0xab, 0xe9, 0x4a, 0xb6, 0x5e, 0x3a, 0x95, 0x39, 0xd4, 0x94, 0x32, 0x02,
0xb8, 0xfe, 0x91, 0x19, 0x37, 0xfa, 0x80, 0xba, 0x50, 0xd3, 0xa1, 0x49, 0x63, 0xdc, 0x99, 0xb4, 0x97, 0x0e, 0x68, 0x48, 0xb4, 0xbc, 0xc7, 0xc2, 0xb7, 0x3e, 0x0f, 0x74, 0xd8, 0x14, 0xda, 0x0d,
0x91, 0xc4, 0xd4, 0x49, 0x60, 0xf8, 0x6f, 0x0b, 0xd6, 0x9f, 0x1e, 0xb9, 0x1e, 0x2d, 0xf5, 0xe8, 0x0d, 0x5c, 0xff, 0xd8, 0xb4, 0x1b, 0xbd, 0x40, 0x3b, 0x50, 0xd3, 0xae, 0x49, 0x7d, 0xdc, 0x3d,
0x99, 0x9b, 0xc6, 0x2e, 0xb4, 0xf4, 0x85, 0x69, 0x05, 0x69, 0x9c, 0x57, 0x94, 0xd0, 0x74, 0x83, 0x6d, 0x23, 0xf1, 0xa9, 0x93, 0xc0, 0xf0, 0x9f, 0x16, 0xac, 0xbc, 0x38, 0x76, 0x3d, 0x5a, 0xaa,
0x62, 0x87, 0x5f, 0xf8, 0x98, 0x0e, 0x9f, 0x79, 0x52, 0x2b, 0x7a, 0x52, 0xc9, 0xed, 0xa5, 0x4f, 0xd1, 0x53, 0x27, 0x91, 0x6d, 0x68, 0xeb, 0x0d, 0x53, 0x0a, 0x52, 0x3f, 0x2f, 0x2a, 0xa1, 0xa9,
0xcb, 0xed, 0x07, 0x80, 0x8a, 0x6e, 0x65, 0x23, 0x37, 0x8d, 0x8e, 0xf5, 0x71, 0xd1, 0xe9, 0x42, 0x06, 0xc5, 0x0a, 0x3f, 0xf7, 0x29, 0x15, 0x3e, 0xbb, 0x49, 0xad, 0x78, 0x93, 0x4a, 0x6c, 0xd7,
0x63, 0x9f, 0x98, 0xa0, 0x5c, 0x82, 0x15, 0x8f, 0x85, 0x92, 0xbe, 0x97, 0xc3, 0xb7, 0x74, 0x6c, 0x3f, 0x2f, 0xb6, 0x1f, 0x03, 0x2a, 0x5e, 0x2b, 0x6b, 0xb9, 0xa9, 0x77, 0xac, 0x4f, 0xf3, 0xce,
0xba, 0x62, 0x33, 0x95, 0x7d, 0x43, 0xc7, 0x02, 0xdf, 0x02, 0x50, 0xf8, 0xd4, 0xda, 0x25, 0x58, 0x0e, 0x34, 0xf7, 0x88, 0x71, 0xca, 0x55, 0x58, 0xf4, 0x58, 0x28, 0xe9, 0x07, 0x39, 0x7c, 0x47,
0x70, 0x89, 0x19, 0xee, 0xab, 0x95, 0x18, 0x38, 0xea, 0x0e, 0xdf, 0x85, 0xf9, 0x7d, 0xa2, 0x34, 0xc7, 0xa6, 0x2a, 0xb6, 0x52, 0xd9, 0x57, 0x74, 0x2c, 0xf0, 0x5d, 0x00, 0x85, 0x4f, 0xad, 0x5d,
0x2b, 0xe6, 0x9c, 0x7a, 0x72, 0x18, 0x73, 0xf3, 0xa2, 0x4d, 0x23, 0x7b, 0xce, 0x8f, 0xd4, 0xbc, 0x85, 0x39, 0x97, 0x98, 0xe6, 0xbe, 0x54, 0xf1, 0x81, 0xa3, 0xf6, 0xf0, 0x03, 0x98, 0xdd, 0x23,
0x51, 0x56, 0xcc, 0xbc, 0x51, 0xbf, 0xf7, 0xfe, 0xb2, 0xa0, 0xa9, 0x2a, 0xec, 0x90, 0xf2, 0x63, 0x4a, 0xb3, 0x62, 0xce, 0xa9, 0x27, 0x87, 0x31, 0x37, 0x2f, 0xda, 0x32, 0xb2, 0x43, 0x7e, 0xac,
0xdf, 0xa3, 0xe8, 0x9e, 0x9e, 0x62, 0xba, 0x28, 0xb7, 0xab, 0x11, 0x2f, 0x2c, 0xd6, 0x76, 0x39, 0xfa, 0x8d, 0xb2, 0x62, 0xfa, 0x8d, 0xfa, 0xde, 0xfd, 0xc3, 0x82, 0x96, 0xca, 0xb0, 0x03, 0xca,
0xd5, 0x93, 0xcd, 0x73, 0x0e, 0xdd, 0x85, 0x7a, 0xba, 0xfd, 0x56, 0xbe, 0x2e, 0xef, 0xc4, 0xf6, 0x4f, 0x7c, 0x8f, 0xa2, 0x87, 0xba, 0x8b, 0xe9, 0xa4, 0xdc, 0xa8, 0x7a, 0xbc, 0x30, 0x78, 0xdb,
0xfa, 0x44, 0x85, 0xe3, 0x39, 0xf4, 0x35, 0x34, 0xb2, 0x3d, 0x1b, 0x9d, 0x9f, 0xd4, 0x5f, 0x54, 0xe5, 0x50, 0x4f, 0x26, 0xd3, 0x19, 0xf4, 0x00, 0x1a, 0xe9, 0x74, 0x5c, 0x39, 0x5d, 0x9e, 0x99,
0x30, 0xd5, 0xfc, 0xde, 0xaf, 0x16, 0x6c, 0x96, 0xf7, 0x53, 0xe3, 0xd6, 0xcf, 0xf0, 0xbf, 0x29, 0xed, 0x95, 0x53, 0x19, 0x8e, 0x67, 0xd0, 0x97, 0xd0, 0xcc, 0xe6, 0x70, 0x74, 0xf9, 0xb4, 0xfe,
0xcb, 0x2b, 0xfa, 0x7f, 0x49, 0xcd, 0xec, 0xb5, 0xd9, 0xbe, 0x7a, 0x3a, 0x30, 0x79, 0x30, 0xc5, 0xa2, 0x82, 0x89, 0xe6, 0x77, 0x7f, 0xb6, 0x60, 0xad, 0x3c, 0xbf, 0x9a, 0x6b, 0xfd, 0x08, 0xff,
0x62, 0x1e, 0x36, 0xd3, 0xc5, 0xab, 0xe7, 0x4a, 0xf7, 0x88, 0xbd, 0x31, 0x2c, 0xfa, 0xb0, 0x52, 0x9a, 0x30, 0xdc, 0xa2, 0x7f, 0x97, 0xd4, 0x4c, 0x1f, 0xab, 0xed, 0x1b, 0xe7, 0x03, 0x93, 0x07,
0xdc, 0x32, 0xd1, 0x14, 0x2f, 0xec, 0x4b, 0x13, 0x96, 0xaa, 0x4b, 0x1f, 0x9e, 0x43, 0x0f, 0x00, 0x53, 0x2c, 0x66, 0x61, 0x2d, 0x1d, 0xbc, 0x7a, 0xae, 0x74, 0x8f, 0xd9, 0x91, 0x61, 0xd1, 0x87,
0xf2, 0x25, 0x13, 0x5d, 0xa8, 0x86, 0xba, 0xbc, 0x7d, 0xda, 0x53, 0x77, 0x42, 0x3c, 0x87, 0x5e, 0xc5, 0xe2, 0x94, 0x89, 0x26, 0xdc, 0xc2, 0xbe, 0x7a, 0xca, 0x52, 0x75, 0xe8, 0xc3, 0x33, 0xe8,
0x42, 0xbb, 0xbc, 0x56, 0x22, 0x5c, 0x42, 0x4e, 0x5d, 0x51, 0xed, 0xdd, 0x13, 0x31, 0x59, 0x14, 0x31, 0x40, 0x3e, 0x64, 0xa2, 0xcd, 0xaa, 0xab, 0xcb, 0xd3, 0xa7, 0x3d, 0x71, 0x26, 0xc4, 0x33,
0xfe, 0xb0, 0x60, 0xf5, 0x30, 0x6d, 0x5e, 0xc6, 0xff, 0x01, 0x2c, 0x9b, 0x6d, 0x10, 0x9d, 0xab, 0xe8, 0x35, 0x74, 0xca, 0x63, 0x25, 0xc2, 0x25, 0xe4, 0xc4, 0x11, 0xd5, 0xde, 0x3e, 0x13, 0x93,
0x92, 0x2e, 0x2e, 0xa5, 0xf6, 0xf9, 0x19, 0xb7, 0x59, 0x04, 0x1e, 0x41, 0x23, 0x5b, 0xd2, 0x2a, 0x79, 0xe1, 0x57, 0x0b, 0x96, 0x0e, 0xd2, 0xe2, 0x65, 0xee, 0x3f, 0x80, 0x05, 0x33, 0x0d, 0xa2,
0xc9, 0x52, 0xdd, 0x16, 0xed, 0x0b, 0xb3, 0xae, 0x33, 0xb2, 0x7f, 0x5a, 0xb0, 0x6a, 0x5a, 0x8f, 0x4b, 0x55, 0xd2, 0xc5, 0xa1, 0xd4, 0xbe, 0x3c, 0x65, 0x37, 0xf3, 0xc0, 0x53, 0x68, 0x66, 0x43,
0x21, 0xfb, 0x12, 0xb6, 0xa6, 0x2f, 0x39, 0x53, 0x9f, 0xed, 0x7a, 0x95, 0xf0, 0x09, 0xdb, 0x11, 0x5a, 0x25, 0x58, 0xaa, 0xd3, 0xa2, 0xbd, 0x39, 0x6d, 0x3b, 0x23, 0xfb, 0x9b, 0x05, 0x4b, 0xa6,
0x9e, 0x43, 0x7d, 0xa8, 0x27, 0x0b, 0x8f, 0x44, 0x57, 0xca, 0xb5, 0x30, 0x6b, 0x1d, 0xb2, 0xa7, 0xf4, 0x18, 0xb2, 0xaf, 0x61, 0x7d, 0xf2, 0x90, 0x33, 0xf1, 0xd9, 0x6e, 0x55, 0x09, 0x9f, 0x31,
0x0c, 0x17, 0x3c, 0xb7, 0xf7, 0x1c, 0xda, 0x4f, 0xdd, 0x71, 0x40, 0xc3, 0xac, 0x82, 0x7b, 0xb0, 0x1d, 0xe1, 0x19, 0xd4, 0x87, 0x46, 0x32, 0xf0, 0x48, 0x74, 0xbd, 0x9c, 0x0b, 0xd3, 0xc6, 0x21,
0x94, 0x4c, 0x64, 0x64, 0x97, 0x35, 0x17, 0x37, 0x04, 0x7b, 0x7b, 0xea, 0x5d, 0x16, 0x90, 0x11, 0x7b, 0x42, 0x73, 0xc1, 0x33, 0xbb, 0x87, 0xd0, 0x79, 0xe1, 0x8e, 0x03, 0x1a, 0x66, 0x19, 0xdc,
0xac, 0x1c, 0xa8, 0x0e, 0x6a, 0x94, 0xbe, 0x50, 0x7f, 0xb0, 0x4c, 0x19, 0x24, 0xe8, 0x5a, 0x25, 0x83, 0x7a, 0xd2, 0x91, 0x91, 0x5d, 0xd6, 0x5c, 0x9c, 0x10, 0xec, 0x8d, 0x89, 0x7b, 0x99, 0x43,
0x1b, 0x66, 0x0f, 0x9b, 0x19, 0x35, 0xfb, 0x0a, 0x56, 0x7b, 0x23, 0xea, 0xbd, 0x65, 0x71, 0xe6, 0x46, 0xb0, 0xb8, 0xaf, 0x2a, 0xa8, 0x51, 0xfa, 0x4a, 0xfd, 0x60, 0x99, 0xd0, 0x48, 0xd0, 0xcd,
0xc1, 0x13, 0x80, 0xbc, 0xef, 0x56, 0xb2, 0x7b, 0x62, 0xce, 0xd8, 0x17, 0x67, 0xde, 0x67, 0xde, 0x4a, 0x34, 0x4c, 0x6f, 0x36, 0x53, 0x72, 0xf6, 0x0d, 0x2c, 0xf5, 0x46, 0xd4, 0x7b, 0xc7, 0xe2,
0x3c, 0x54, 0x2d, 0xd8, 0x68, 0xbf, 0x0b, 0x4b, 0x7d, 0xb5, 0x83, 0x0b, 0xb4, 0x55, 0x6d, 0xa7, 0xec, 0x06, 0xcf, 0x01, 0xf2, 0xba, 0x5b, 0x89, 0xee, 0x53, 0x7d, 0xc6, 0xbe, 0x32, 0x75, 0x3f,
0xa9, 0xc6, 0x33, 0x13, 0x72, 0xa3, 0xe9, 0xd5, 0x92, 0xfe, 0xe7, 0xc5, 0xe7, 0xff, 0x06, 0x00, 0xbb, 0xcd, 0x13, 0x55, 0x82, 0x8d, 0xf6, 0x07, 0x50, 0xef, 0xab, 0x19, 0x5c, 0xa0, 0xf5, 0x6a,
0x00, 0xff, 0xff, 0x22, 0xc9, 0xfe, 0x20, 0xca, 0x10, 0x00, 0x00, 0x39, 0x4d, 0x35, 0x5e, 0x38, 0x25, 0x37, 0x9a, 0xde, 0xd4, 0xf5, 0x9f, 0x1b, 0xff, 0xfd, 0x3b,
0x00, 0x00, 0xff, 0xff, 0xb2, 0xa0, 0x6e, 0x6c, 0xea, 0x10, 0x00, 0x00,
} }

View file

@ -80,7 +80,7 @@ func (fe *frontendServer) homeHandler(w http.ResponseWriter, r *http.Request) {
"products": ps, "products": ps,
"cart_size": len(cart), "cart_size": len(cart),
"banner_color": os.Getenv("BANNER_COLOR"), // illustrates canary deployments "banner_color": os.Getenv("BANNER_COLOR"), // illustrates canary deployments
"ad": fe.chooseAd(r.Context(), log), "ad": fe.chooseAd(r.Context(), []string{}, log),
}); err != nil { }); err != nil {
log.Error(err) log.Error(err)
} }
@ -133,7 +133,7 @@ func (fe *frontendServer) productHandler(w http.ResponseWriter, r *http.Request)
if err := templates.ExecuteTemplate(w, "product", map[string]interface{}{ if err := templates.ExecuteTemplate(w, "product", map[string]interface{}{
"session_id": sessionID(r), "session_id": sessionID(r),
"request_id": r.Context().Value(ctxKeyRequestID{}), "request_id": r.Context().Value(ctxKeyRequestID{}),
"ad": fe.chooseAd(r.Context(), log), "ad": fe.chooseAd(r.Context(), p.Categories, log),
"user_currency": currentCurrency(r), "user_currency": currentCurrency(r),
"currencies": currencies, "currencies": currencies,
"product": product, "product": product,
@ -346,8 +346,8 @@ func (fe *frontendServer) setCurrencyHandler(w http.ResponseWriter, r *http.Requ
// chooseAd queries for advertisements available and randomly chooses one, if // chooseAd queries for advertisements available and randomly chooses one, if
// available. It ignores the error retrieving the ad since it is not critical. // available. It ignores the error retrieving the ad since it is not critical.
func (fe *frontendServer) chooseAd(ctx context.Context, log logrus.FieldLogger) *pb.Ad { func (fe *frontendServer) chooseAd(ctx context.Context, ctxKeys []string, log logrus.FieldLogger) *pb.Ad {
ads, err := fe.getAd(ctx) ads, err := fe.getAd(ctx, ctxKeys)
if err != nil { if err != nil {
log.WithField("error", err).Warn("failed to retrieve ads") log.WithField("error", err).Warn("failed to retrieve ads")
return nil return nil

View file

@ -26,6 +26,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.opencensus.io/exporter/jaeger"
"go.opencensus.io/plugin/ocgrpc" "go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/plugin/ochttp" "go.opencensus.io/plugin/ochttp"
"go.opencensus.io/plugin/ochttp/propagation/b3" "go.opencensus.io/plugin/ochttp/propagation/b3"
@ -142,7 +143,31 @@ func main() {
log.Fatal(http.ListenAndServe(addr+":"+srvPort, handler)) log.Fatal(http.ListenAndServe(addr+":"+srvPort, handler))
} }
func initJaegerTracing(log logrus.FieldLogger) {
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
if svcAddr == "" {
log.Info("jaeger initialization disabled.")
return
}
// Register the Jaeger exporter to be able to retrieve
// the collected spans.
exporter, err := jaeger.NewExporter(jaeger.Options{
Endpoint: fmt.Sprintf("http://%s", svcAddr),
Process: jaeger.Process{
ServiceName: "frontend",
},
})
if err != nil {
log.Fatal(err)
}
trace.RegisterExporter(exporter)
log.Info("jaeger initialization completed.")
}
func initStats(log logrus.FieldLogger, exporter *stackdriver.Exporter) { func initStats(log logrus.FieldLogger, exporter *stackdriver.Exporter) {
view.SetReportingPeriod(60 * time.Second)
view.RegisterExporter(exporter) view.RegisterExporter(exporter)
if err := view.Register(ochttp.DefaultServerViews...); err != nil { if err := view.Register(ochttp.DefaultServerViews...); err != nil {
log.Warn("Error registering http default server views") log.Warn("Error registering http default server views")
@ -156,17 +181,19 @@ func initStats(log logrus.FieldLogger, exporter *stackdriver.Exporter) {
} }
} }
func initTracing(log logrus.FieldLogger) { func initStackdriverTracing(log logrus.FieldLogger) {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.
for i := 1; i <= 3; i++ { for i := 1; i <= 3; i++ {
log = log.WithField("retry", i) log = log.WithField("retry", i)
exporter, err := stackdriver.NewExporter(stackdriver.Options{}) exporter, err := stackdriver.NewExporter(stackdriver.Options{})
if err != nil { if err != nil {
// log.Warnf is used since there are multiple backends (stackdriver & jaeger)
// to store the traces. In production setup most likely you would use only one backend.
// In that case you should use log.Fatalf.
log.Warnf("failed to initialize stackdriver exporter: %+v", err) log.Warnf("failed to initialize stackdriver exporter: %+v", err)
} else { } else {
trace.RegisterExporter(exporter) trace.RegisterExporter(exporter)
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
log.Info("registered stackdriver tracing") log.Info("registered stackdriver tracing")
// Register the views to collect server stats. // Register the views to collect server stats.
@ -180,6 +207,18 @@ func initTracing(log logrus.FieldLogger) {
log.Warn("could not initialize stackdriver exporter after retrying, giving up") log.Warn("could not initialize stackdriver exporter after retrying, giving up")
} }
func initTracing(log logrus.FieldLogger) {
// This is a demo app with low QPS. trace.AlwaysSample() is used here
// to make sure traces are available for observation and analysis.
// In a production environment or high QPS setup please use
// trace.ProbabilitySampler set at the desired probability.
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
initJaegerTracing(log)
initStackdriverTracing(log)
}
func initProfiling(log logrus.FieldLogger, service, version string) { func initProfiling(log logrus.FieldLogger, service, version string) {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.

View file

@ -116,12 +116,12 @@ func (fe *frontendServer) getRecommendations(ctx context.Context, userID string,
return out, err return out, err
} }
func (fe *frontendServer) getAd(ctx context.Context) ([]*pb.Ad, error) { func (fe *frontendServer) getAd(ctx context.Context, ctxKeys []string) ([]*pb.Ad, error) {
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*100) ctx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
defer cancel() defer cancel()
resp, err := pb.NewAdServiceClient(fe.adSvcConn).GetAds(ctx, &pb.AdRequest{ resp, err := pb.NewAdServiceClient(fe.adSvcConn).GetAds(ctx, &pb.AdRequest{
ContextKeys: nil, ContextKeys: ctxKeys,
}) })
return resp.GetAds(), errors.Wrap(err, "failed to get ads") return resp.GetAds(), errors.Wrap(err, "failed to get ads")
} }

View file

@ -132,7 +132,8 @@
</div> </div>
<div class="col-md-2 mb-3"> <div class="col-md-2 mb-3">
<label for="credit_card_cvv">CVV</label> <label for="credit_card_cvv">CVV</label>
<input type="text" class="form-control" id="credit_card_cvv" <input type="password" class="form-control" id="credit_card_cvv"
autocomplete="off"
name="credit_card_cvv" value="672" required pattern="\d{3}"> name="credit_card_cvv" value="672" required pattern="\d{3}">
</div> </div>
</div> </div>

View file

@ -1,7 +1,17 @@
FROM python:3.6 FROM python:3-slim as base
FROM base as builder
RUN apt-get -qq update \
&& apt-get install -y --no-install-recommends \
g++
COPY requirements.txt . COPY requirements.txt .
RUN pip install -r requirements.txt
RUN pip install --install-option="--prefix=/install" -r requirements.txt
FROM base
COPY --from=builder /install /usr/local
COPY . . COPY . .
ENTRYPOINT ./loadgen.sh ENTRYPOINT ./loadgen.sh

View file

@ -1,4 +1,4 @@
#!/bin/bash -eu #!/bin/sh -eu
# #
# Copyright 2018 Google LLC # Copyright 2018 Google LLC
# #

View file

@ -60,7 +60,7 @@ def checkout(l):
'country': 'United States', 'country': 'United States',
'credit_card_number': '4432-8015-6152-0454', 'credit_card_number': '4432-8015-6152-0454',
'credit_card_expiration_month': '1', 'credit_card_expiration_month': '1',
'credit_card_expiration_year': '2019', 'credit_card_expiration_year': '2039',
'credit_card_cvv': '672', 'credit_card_cvv': '672',
}) })

View file

@ -0,0 +1 @@
locustio==0.8.1

View file

@ -1,2 +1,23 @@
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt requirements.in
#
certifi==2018.11.29 # via requests
chardet==3.0.4 # via requests
click==7.0 # via flask
flask==1.0.2 # via locustio
gevent==1.4.0 # via locustio
greenlet==0.4.15 # via gevent
idna==2.8 # via requests
itsdangerous==1.1.0 # via flask
jinja2==2.10 # via flask
locustio==0.8.1 locustio==0.8.1
pyzmq==17.0.0 markupsafe==1.1.0 # via jinja2
msgpack-python==0.5.6 # via locustio
pyzmq==17.0.0 # via locustio
requests==2.21.0 # via locustio
six==1.12.0 # via locustio
urllib3==1.24.1 # via requests
werkzeug==0.14.1 # via flask

View file

@ -1,7 +1,13 @@
FROM node:8 FROM node:8-alpine as base
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ FROM base as builder
chmod +x /bin/grpc_health_probe
# Some packages (e.g. @google-cloud/profiler) require additional
# deps for post-install scripts
RUN apk add --update --no-cache \
python \
make \
g++
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -9,8 +15,18 @@ COPY package*.json ./
RUN npm install --only=production RUN npm install --only=production
FROM base
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY . . COPY . .
EXPOSE 50051 EXPOSE 50051
CMD [ "node", "index.js" ] ENTRYPOINT [ "node", "index.js" ]

View file

@ -14,6 +14,15 @@
const cardValidator = require('simple-card-validator'); const cardValidator = require('simple-card-validator');
const uuid = require('uuid/v4'); const uuid = require('uuid/v4');
const pino = require('pino');
const logger = pino({
name: 'paymentservice-charge',
messageKey: 'message',
changeLevelName: 'severity',
useLevelLabels: true
});
class CreditCardError extends Error { class CreditCardError extends Error {
constructor (message) { constructor (message) {
@ -67,7 +76,7 @@ module.exports = function charge (request) {
const { credit_card_expiration_year: year, credit_card_expiration_month: month } = creditCard; const { credit_card_expiration_year: year, credit_card_expiration_month: month } = creditCard;
if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); } if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); }
console.log(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \ logger.info(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \
Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`); Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`);
return { transaction_id: uuid() }; return { transaction_id: uuid() };

View file

@ -700,16 +700,36 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
}, },
"fast-json-parse": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz",
"integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw=="
},
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
}, },
"fast-redact": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-1.2.0.tgz",
"integrity": "sha512-k/uSk9PtFmvYx0m7bRk5B2gZChQk4euWhrn7Mf3vYSmwZBLh7cGNuMuc/vhH1MKMPyVJMMtl9oMwPnwlKqs7CQ=="
},
"fast-safe-stringify": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
"integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg=="
},
"findit2": { "findit2": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
"integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY="
}, },
"flatstr": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.8.tgz",
"integrity": "sha512-YXblbv/vc1zuVVUtnKl1hPqqk7TalZCppnKE7Pr8FI/Rp48vzckS/4SJ4Y9O9RNiI82Vcw/FydmtqdQOg1Dpqw=="
},
"follow-redirects": { "follow-redirects": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.1.tgz",
@ -1592,6 +1612,26 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
}, },
"pino": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/pino/-/pino-5.6.2.tgz",
"integrity": "sha512-JVMYqJkE58b2u5+t85zJLbUDOhoWDjEQqrRY1eQzuR4Ub9RIyUSQJms4deT8Joy+C/QIdrrie7NffgCm+ao9xw==",
"requires": {
"fast-json-parse": "^1.0.3",
"fast-redact": "^1.2.0",
"fast-safe-stringify": "^2.0.6",
"flatstr": "^1.0.5",
"pino-std-serializers": "^2.2.1",
"pump": "^3.0.0",
"quick-format-unescaped": "^3.0.0",
"sonic-boom": "^0.6.1"
}
},
"pino-std-serializers": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.2.1.tgz",
"integrity": "sha512-QqL7kkF7eMCpFG4hpZD8UPQga/kxkkh3E62HzMzTIL4OQyijyisAnBL8msBEAml8xcb/ioGhH7UUzGxuHqczhQ=="
},
"pretty-ms": { "pretty-ms": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz",
@ -1621,6 +1661,15 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
}, },
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"punycode": { "punycode": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@ -1631,6 +1680,11 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
}, },
"quick-format-unescaped": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.0.tgz",
"integrity": "sha512-XmIOc07VM2kPm6m3j/U6jgxyUgDm2Rgh2c1PPy0JUHoQRdoh86hOym0bHyF6G1T6sn+N5lildhvl/T59H5KVyA=="
},
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@ -1728,6 +1782,14 @@
"resolved": "https://registry.npmjs.org/simple-card-validator/-/simple-card-validator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/simple-card-validator/-/simple-card-validator-1.1.0.tgz",
"integrity": "sha1-675uRp/q7Cy7SBX4Qyu+BMccNvk=" "integrity": "sha1-675uRp/q7Cy7SBX4Qyu+BMccNvk="
}, },
"sonic-boom": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.6.1.tgz",
"integrity": "sha512-3qx6XXDeG+hPNa+jla1H6BMBLcjLl8L8NRERLVeIf/EuPqoqmq4K8owG29Xu7OypT/7/YT/0uKW6YitsKA+nLQ==",
"requires": {
"flatstr": "^1.0.5"
}
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",

View file

@ -16,6 +16,7 @@
"@google-cloud/trace-agent": "^2.11.0", "@google-cloud/trace-agent": "^2.11.0",
"@grpc/proto-loader": "^0.1.0", "@grpc/proto-loader": "^0.1.0",
"grpc": "^1.12.3", "grpc": "^1.12.3",
"pino": "^5.6.2",
"simple-card-validator": "^1.1.0", "simple-card-validator": "^1.1.0",
"uuid": "^3.2.1" "uuid": "^3.2.1"
}, },

View file

@ -64,6 +64,10 @@ message Product {
string description = 3; string description = 3;
string picture = 4; string picture = 4;
Money price_usd = 5; Money price_usd = 5;
// Categories such as "vintage" or "gardening" that can be used to look up
// other related products.
repeated string categories = 6;
} }
message ListProductsResponse { message ListProductsResponse {
@ -218,18 +222,18 @@ message PlaceOrderResponse {
OrderResult order = 1; OrderResult order = 1;
} }
// ------------Ads service------------------ // ------------Ad service------------------
service AdsService { service AdService {
rpc GetAds(AdsRequest) returns (AdsResponse) {} rpc GetAds(AdRequest) returns (AdResponse) {}
} }
message AdsRequest { message AdRequest {
// List of important key words from the current page describing the context. // List of important key words from the current page describing the context.
repeated string context_keys = 1; repeated string context_keys = 1;
} }
message AdsResponse { message AdResponse {
repeated Ad ads = 1; repeated Ad ads = 1;
} }

View file

@ -14,10 +14,18 @@
const path = require('path'); const path = require('path');
const grpc = require('grpc'); const grpc = require('grpc');
const pino = require('pino');
const protoLoader = require('@grpc/proto-loader'); const protoLoader = require('@grpc/proto-loader');
const charge = require('./charge'); const charge = require('./charge');
const logger = pino({
name: 'paymentservice-server',
messageKey: 'message',
changeLevelName: 'severity',
useLevelLabels: true
});
class HipsterShopServer { class HipsterShopServer {
constructor (protoRoot, port = HipsterShopServer.DEFAULT_PORT) { constructor (protoRoot, port = HipsterShopServer.DEFAULT_PORT) {
this.port = port; this.port = port;
@ -38,7 +46,7 @@ class HipsterShopServer {
*/ */
static ChargeServiceHandler (call, callback) { static ChargeServiceHandler (call, callback) {
try { try {
console.log(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`); logger.info(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`);
const response = charge(call.request); const response = charge(call.request);
callback(null, response); callback(null, response);
} catch (err) { } catch (err) {
@ -53,7 +61,7 @@ class HipsterShopServer {
listen () { listen () {
this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure()); this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure());
console.log(`PaymentService grpc server listening on ${this.port}`); logger.info(`PaymentService grpc server listening on ${this.port}`);
this.server.start(); this.server.start();
} }

View file

@ -1,4 +1,4 @@
FROM golang:1.10-alpine AS builder FROM golang:1.12-alpine AS builder
RUN apk add --no-cache ca-certificates git && \ RUN apk add --no-cache ca-certificates git && \
wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \ wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \
chmod +x /go/bin/dep chmod +x /go/bin/dep
@ -15,7 +15,7 @@ RUN go build -o /productcatalogservice .
FROM alpine AS release FROM alpine AS release
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe chmod +x /bin/grpc_health_probe
WORKDIR /productcatalogservice WORKDIR /productcatalogservice

View file

@ -2,27 +2,46 @@
[[projects]] [[projects]]
digest = "1:467af0aad47996b25b838d6f14c8371123a8a76ec239020a6c5894e1f8f60272"
name = "cloud.google.com/go" name = "cloud.google.com/go"
packages = [ packages = [
"compute/metadata", "compute/metadata",
"internal/version", "internal/version",
"monitoring/apiv3", "monitoring/apiv3",
"profiler", "profiler",
"trace/apiv2" "trace/apiv2",
] ]
pruneopts = "UT"
revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad" revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad"
version = "v0.27.0" version = "v0.27.0"
[[projects]] [[projects]]
digest = "1:4b96dcd8534bc6450a922bd16a76360ba3381f0d1daf40abbaec91c053fbfeb5"
name = "contrib.go.opencensus.io/exporter/stackdriver" name = "contrib.go.opencensus.io/exporter/stackdriver"
packages = [ packages = ["."]
".", pruneopts = "UT"
"propagation"
]
revision = "37aa2801fbf0205003e15636096ebf0373510288" revision = "37aa2801fbf0205003e15636096ebf0373510288"
version = "v0.5.0" version = "v0.5.0"
[[projects]] [[projects]]
branch = "master"
digest = "1:d3a57cdbaefaceca4ebe6258ed86a992bdcfc93a8442dbda5343e2d43a8f8a6a"
name = "git.apache.org/thrift.git"
packages = ["lib/go/thrift"]
pruneopts = "UT"
revision = "67df34afa782be67154034b31e4ad7cb3834fed1"
source = "github.com/apache/thrift"
[[projects]]
branch = "master"
digest = "1:14e66208d324c0ecb49934b5ac311c50a94e3a458e92b0026ef9e26919ac8d9d"
name = "github.com/GoogleCloudPlatform/microservices-demo"
packages = ["src/productcatalogservice/genproto"]
pruneopts = "UT"
revision = "10dfd04ab174cc680ed6ffef26cc4fb09ec40404"
[[projects]]
digest = "1:4fbf68bee2a60f6af6414572936edb295f6f26b73c6fb25ab0e7b03b013854f5"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = [ packages = [
"jsonpb", "jsonpb",
@ -34,46 +53,67 @@
"ptypes/empty", "ptypes/empty",
"ptypes/struct", "ptypes/struct",
"ptypes/timestamp", "ptypes/timestamp",
"ptypes/wrappers" "ptypes/wrappers",
] ]
pruneopts = "UT"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0" version = "v1.2.0"
[[projects]] [[projects]]
digest = "1:2e3c336fc7fde5c984d2841455a658a6d626450b1754a854b3b32e7a8f49a07a"
name = "github.com/google/go-cmp" name = "github.com/google/go-cmp"
packages = [ packages = [
"cmp", "cmp",
"cmp/internal/diff", "cmp/internal/diff",
"cmp/internal/function", "cmp/internal/function",
"cmp/internal/value" "cmp/internal/value",
] ]
pruneopts = "UT"
revision = "3af367b6b30c263d47e8895973edcca9a49cf029" revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
version = "v0.2.0" version = "v0.2.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:fc55304e290027108ae0cac675a171bcd854f9c657678c20ceea837718ea6819"
name = "github.com/google/pprof" name = "github.com/google/pprof"
packages = ["profile"] packages = ["profile"]
revision = "e027b505a088ac3c68c339a1d7ce7724bf34538b" pruneopts = "UT"
revision = "e84dfd68c163c45ea47aa24b3dc7eaa93f6675b1"
[[projects]] [[projects]]
digest = "1:cd9864c6366515827a759931746738ede6079faa08df9c584596370d6add135c"
name = "github.com/googleapis/gax-go" name = "github.com/googleapis/gax-go"
packages = ["."] packages = [
revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" ".",
version = "v2.0.0" "v2",
]
pruneopts = "UT"
revision = "c8a15bac9b9fe955bd9f900272f9a306465d28cf"
version = "v2.0.3"
[[projects]] [[projects]]
digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc" digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8"
name = "github.com/konsorten/go-windows-terminal-sequences"
packages = ["."]
pruneopts = "UT"
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
version = "v1.0.1"
[[projects]]
digest = "1:87c2e02fb01c27060ccc5ba7c5a407cc91147726f8f40b70cceeedbc52b1f3a8"
name = "github.com/sirupsen/logrus" name = "github.com/sirupsen/logrus"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2" revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5"
version = "v1.0.6" version = "v1.3.0"
[[projects]] [[projects]]
digest = "1:a5154dfd6b37bef5a3eab759e13296348e639dc8c7604f538368167782b08ccd"
name = "go.opencensus.io" name = "go.opencensus.io"
packages = [ packages = [
".", ".",
"exporter/jaeger",
"exporter/jaeger/internal/gen-go/jaeger",
"internal", "internal",
"internal/tagencoding", "internal/tagencoding",
"plugin/ocgrpc", "plugin/ocgrpc",
@ -86,21 +126,23 @@
"trace", "trace",
"trace/internal", "trace/internal",
"trace/propagation", "trace/propagation",
"trace/tracestate" "trace/tracestate",
] ]
pruneopts = "UT"
revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89"
version = "v0.16.0" version = "v0.16.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ssh/terminal"] packages = ["ssh/terminal"]
pruneopts = "UT" pruneopts = "UT"
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:9d2f08c64693fbe7177b5980f80c35672c80f12be79bb3bc86948b934d70e4ee"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"context", "context",
@ -110,35 +152,46 @@
"http2/hpack", "http2/hpack",
"idna", "idna",
"internal/timeseries", "internal/timeseries",
"trace" "trace",
] ]
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" pruneopts = "UT"
revision = "ed066c81e75eba56dd9bd2139ade88125b855585"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:511a6232760c10dcb1ebf1ab83ef0291e2baf801f203ca6314759c5458b73a6a"
name = "golang.org/x/oauth2" name = "golang.org/x/oauth2"
packages = [ packages = [
".", ".",
"google", "google",
"internal", "internal",
"jws", "jws",
"jwt" "jwt",
] ]
revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" pruneopts = "UT"
revision = "5dab4167f31cbd76b407f1486c86b40748bc5073"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:75515eedc0dc2cb0b40372008b616fa2841d831c63eedd403285ff286c593295"
name = "golang.org/x/sync" name = "golang.org/x/sync"
packages = ["semaphore"] packages = ["semaphore"]
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" pruneopts = "UT"
revision = "37e7f081c4d4c64e13b10787722085407fe5d15f"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:43cde116ff48f299eddb7e6515677e6d0a2c915854bb05a333877f07c3bb3033"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix"] packages = [
revision = "1561086e645b2809fb9f8a1e2a38160bf8d53bf4" "unix",
"windows",
]
pruneopts = "UT"
revision = "11f53e03133963fb11ae0588e08b5e0b85be8be5"
[[projects]] [[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text" name = "golang.org/x/text"
packages = [ packages = [
"collate", "collate",
@ -154,13 +207,15 @@
"unicode/bidi", "unicode/bidi",
"unicode/cldr", "unicode/cldr",
"unicode/norm", "unicode/norm",
"unicode/rangetable" "unicode/rangetable",
] ]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0" version = "v0.3.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:26a71f62c83707b9952821c2a895bd041588501fa370cc267221817fcc721253"
name = "google.golang.org/api" name = "google.golang.org/api"
packages = [ packages = [
"googleapi/transport", "googleapi/transport",
@ -170,11 +225,14 @@
"support/bundler", "support/bundler",
"transport", "transport",
"transport/grpc", "transport/grpc",
"transport/http" "transport/http",
"transport/http/internal/propagation",
] ]
revision = "19ff8768a5c0b8e46ea281065664787eefc24121" pruneopts = "UT"
revision = "43037ff31f6958582e5d3c19d9ac1a4d2819669c"
[[projects]] [[projects]]
digest = "1:c4eaa5f79d36f76ef4bd0c4f96e36bc1b7b5a359528d1267f0cb7a5d58b7b5bb"
name = "google.golang.org/appengine" name = "google.golang.org/appengine"
packages = [ packages = [
".", ".",
@ -188,13 +246,15 @@
"internal/socket", "internal/socket",
"internal/urlfetch", "internal/urlfetch",
"socket", "socket",
"urlfetch" "urlfetch",
] ]
revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06" pruneopts = "UT"
version = "v1.2.0" revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.4.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3552e7267a98c605e6cbfe6b03c34589265ab72e6078b95fb2e10e0cb44f5cc8"
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
packages = [ packages = [
"googleapis/api/annotations", "googleapis/api/annotations",
@ -207,11 +267,13 @@
"googleapis/monitoring/v3", "googleapis/monitoring/v3",
"googleapis/rpc/errdetails", "googleapis/rpc/errdetails",
"googleapis/rpc/status", "googleapis/rpc/status",
"protobuf/field_mask" "protobuf/field_mask",
] ]
revision = "c3f76f3b92d1ffa4c58a9ff842a58b8877655e0f" pruneopts = "UT"
revision = "db91494dd46c1fdcbbde05e5ff5eb56df8f7d79a"
[[projects]] [[projects]]
digest = "1:6497ab07ec89179db8d5a563d33635be04ceffaa29007a3ae74b9f15f4d3068e"
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
packages = [ packages = [
".", ".",
@ -241,14 +303,32 @@
"resolver/passthrough", "resolver/passthrough",
"stats", "stats",
"status", "status",
"tap" "tap",
] ]
pruneopts = "UT"
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
version = "v1.14.0" version = "v1.14.0"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "b68fd9438c2eb711d9fc51c1f23c5ca0d5169bf4022351dfc400cd35ba39dfaa" input-imports = [
"cloud.google.com/go/profiler",
"contrib.go.opencensus.io/exporter/stackdriver",
"github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto",
"github.com/golang/protobuf/jsonpb",
"github.com/golang/protobuf/proto",
"github.com/google/go-cmp/cmp",
"github.com/sirupsen/logrus",
"go.opencensus.io/exporter/jaeger",
"go.opencensus.io/plugin/ocgrpc",
"go.opencensus.io/stats/view",
"go.opencensus.io/trace",
"golang.org/x/net/context",
"google.golang.org/grpc",
"google.golang.org/grpc/codes",
"google.golang.org/grpc/health/grpc_health_v1",
"google.golang.org/grpc/status",
]
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -3,3 +3,36 @@
Run the following command to restore dependencies to `vendor/` directory: Run the following command to restore dependencies to `vendor/` directory:
dep ensure --vendor-only dep ensure --vendor-only
## Dynamic catalog reloading / artificial delay
This service has a "dynamic catalog reloading" feature that is purposefully
not well implemented. The goal of this feature is to allow you to modify the
`products.json` file and have the changes be picked up without having to
restart the service.
However, this feature is bugged: the catalog is actually reloaded on each
request, introducing a noticeable delay in the frontend. This delay will also
show up in profiling tools: the `parseCatalog` function will take more than 80%
of the CPU time.
You can trigger this feature (and the delay) by sending a `USR1` signal and
remove it (if needed) by sending a `USR2` signal:
```
# Trigger bug
kubectl exec \
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
-c server -- kill -USR1 1
# Remove bug
kubectl exec \
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
-c server -- kill -USR2 1
```
## Latency injection
This service has an `EXTRA_LATENCY` environment variable. This will inject a sleep for the specified [time.Duration](https://golang.org/pkg/time/#ParseDuration) on every call to
to the server.
For example, use `EXTRA_LATENCY="5.5s"` to sleep for 5.5 seconds on every request.

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 67, "units": 67,
"nanos": 990000000 "nanos": 990000000
} },
"categories": ["vintage"]
}, },
{ {
"id": "66VCHSJNUP", "id": "66VCHSJNUP",
@ -20,7 +21,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 12, "units": 12,
"nanos": 490000000 "nanos": 490000000
} },
"categories": ["photography", "vintage"]
}, },
{ {
"id": "1YMWWN1N4O", "id": "1YMWWN1N4O",
@ -30,7 +32,8 @@
"priceUsd": { "priceUsd": {
"currencyCode": "USD", "currencyCode": "USD",
"units": 124 "units": 124
} },
"categories": ["cookware"]
}, },
{ {
"id": "L9ECAV7KIM", "id": "L9ECAV7KIM",
@ -41,7 +44,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 36, "units": 36,
"nanos": 450000000 "nanos": 450000000
} },
"categories": ["gardening"]
}, },
{ {
"id": "2ZYFJ3GM2N", "id": "2ZYFJ3GM2N",
@ -51,7 +55,8 @@
"priceUsd": { "priceUsd": {
"currencyCode": "USD", "currencyCode": "USD",
"units": 2245 "units": 2245
} },
"categories": ["photography", "vintage"]
}, },
{ {
"id": "0PUK6V6EV0", "id": "0PUK6V6EV0",
@ -62,7 +67,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 65, "units": 65,
"nanos": 500000000 "nanos": 500000000
} },
"categories": ["music", "vintage"]
}, },
{ {
"id": "LS4PSXUNUM", "id": "LS4PSXUNUM",
@ -73,7 +79,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 24, "units": 24,
"nanos": 330000000 "nanos": 330000000
} },
"categories": ["cookware"]
}, },
{ {
"id": "9SIQT8TOJO", "id": "9SIQT8TOJO",
@ -84,7 +91,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 789, "units": 789,
"nanos": 500000000 "nanos": 500000000
} },
"categories": ["cycling"]
}, },
{ {
"id": "6E92ZMYYFZ", "id": "6E92ZMYYFZ",
@ -95,7 +103,8 @@
"currencyCode": "USD", "currencyCode": "USD",
"units": 12, "units": 12,
"nanos": 300000000 "nanos": 300000000
} },
"categories": ["gardening"]
} }
] ]
} }

View file

@ -22,7 +22,10 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"os/signal"
"strings" "strings"
"sync"
"syscall"
"time" "time"
pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto" pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto"
@ -32,6 +35,7 @@ import (
"contrib.go.opencensus.io/exporter/stackdriver" "contrib.go.opencensus.io/exporter/stackdriver"
"github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/jsonpb"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.opencensus.io/exporter/jaeger"
"go.opencensus.io/plugin/ocgrpc" "go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view" "go.opencensus.io/stats/view"
"go.opencensus.io/trace" "go.opencensus.io/trace"
@ -41,18 +45,17 @@ import (
) )
var ( var (
catalogJSON []byte cat pb.ListProductsResponse
log *logrus.Logger catalogMutex *sync.Mutex
log *logrus.Logger
extraLatency time.Duration
port = flag.Int("port", 3550, "port to listen at") port = flag.Int("port", 3550, "port to listen at")
reloadCatalog bool
) )
func init() { func init() {
c, err := ioutil.ReadFile("products.json")
if err != nil {
log.Fatalf("failed to open product catalog json file: %v", err)
}
catalogJSON = c
log = logrus.New() log = logrus.New()
log.Formatter = &logrus.JSONFormatter{ log.Formatter = &logrus.JSONFormatter{
FieldMap: logrus.FieldMap{ FieldMap: logrus.FieldMap{
@ -63,7 +66,11 @@ func init() {
TimestampFormat: time.RFC3339Nano, TimestampFormat: time.RFC3339Nano,
} }
log.Out = os.Stdout log.Out = os.Stdout
log.Info("successfully parsed product catalog json") catalogMutex = &sync.Mutex{}
err := readCatalogFile(&cat)
if err != nil {
log.Warnf("could not parse product catalog")
}
} }
func main() { func main() {
@ -71,6 +78,34 @@ func main() {
go initProfiling("productcatalogservice", "1.0.0") go initProfiling("productcatalogservice", "1.0.0")
flag.Parse() flag.Parse()
// set injected latency
if s := os.Getenv("EXTRA_LATENCY"); s != "" {
v, err := time.ParseDuration(s)
if err != nil {
log.Fatalf("failed to parse EXTRA_LATENCY (%s) as time.Duration: %+v", v, err)
}
extraLatency = v
log.Infof("extra latency enabled (duration: %v)", extraLatency)
} else {
extraLatency = time.Duration(0)
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
go func() {
for {
sig := <-sigs
log.Printf("Received signal: %s", sig)
if sig == syscall.SIGUSR1 {
reloadCatalog = true
log.Infof("Enable catalog reloading")
} else {
reloadCatalog = false
log.Infof("Disable catalog reloading")
}
}
}()
log.Infof("starting grpc server at :%d", *port) log.Infof("starting grpc server at :%d", *port)
run(*port) run(*port)
select {} select {}
@ -89,7 +124,29 @@ func run(port int) string {
return l.Addr().String() return l.Addr().String()
} }
func initJaegerTracing() {
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
if svcAddr == "" {
log.Info("jaeger initialization disabled.")
return
}
// Register the Jaeger exporter to be able to retrieve
// the collected spans.
exporter, err := jaeger.NewExporter(jaeger.Options{
Endpoint: fmt.Sprintf("http://%s", svcAddr),
Process: jaeger.Process{
ServiceName: "productcatalogservice",
},
})
if err != nil {
log.Fatal(err)
}
trace.RegisterExporter(exporter)
log.Info("jaeger initialization completed.")
}
func initStats(exporter *stackdriver.Exporter) { func initStats(exporter *stackdriver.Exporter) {
view.SetReportingPeriod(60 * time.Second)
view.RegisterExporter(exporter) view.RegisterExporter(exporter)
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil { if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
log.Info("Error registering default server views") log.Info("Error registering default server views")
@ -98,7 +155,7 @@ func initStats(exporter *stackdriver.Exporter) {
} }
} }
func initTracing() { func initStackDriverTracing() {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.
for i := 1; i <= 3; i++ { for i := 1; i <= 3; i++ {
@ -121,6 +178,11 @@ func initTracing() {
log.Warn("could not initialize stackdriver exporter after retrying, giving up") log.Warn("could not initialize stackdriver exporter after retrying, giving up")
} }
func initTracing() {
initJaegerTracing()
initStackDriverTracing()
}
func initProfiling(service, version string) { func initProfiling(service, version string) {
// TODO(ahmetb) this method is duplicated in other microservices using Go // TODO(ahmetb) this method is duplicated in other microservices using Go
// since they are not sharing packages. // since they are not sharing packages.
@ -145,12 +207,28 @@ func initProfiling(service, version string) {
type productCatalog struct{} type productCatalog struct{}
func parseCatalog() []*pb.Product { func readCatalogFile(catalog *pb.ListProductsResponse) error {
var cat pb.ListProductsResponse catalogMutex.Lock()
defer catalogMutex.Unlock()
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), &cat); err != nil { catalogJSON, err := ioutil.ReadFile("products.json")
if err != nil {
log.Fatalf("failed to open product catalog json file: %v", err)
return err
}
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), catalog); err != nil {
log.Warnf("failed to parse the catalog JSON: %v", err) log.Warnf("failed to parse the catalog JSON: %v", err)
return nil return err
}
log.Info("successfully parsed product catalog json")
return nil
}
func parseCatalog() []*pb.Product {
if reloadCatalog || len(cat.Products) == 0 {
err := readCatalogFile(&cat)
if err != nil {
return []*pb.Product{}
}
} }
return cat.Products return cat.Products
} }
@ -160,10 +238,12 @@ func (p *productCatalog) Check(ctx context.Context, req *healthpb.HealthCheckReq
} }
func (p *productCatalog) ListProducts(context.Context, *pb.Empty) (*pb.ListProductsResponse, error) { func (p *productCatalog) ListProducts(context.Context, *pb.Empty) (*pb.ListProductsResponse, error) {
time.Sleep(extraLatency)
return &pb.ListProductsResponse{Products: parseCatalog()}, nil return &pb.ListProductsResponse{Products: parseCatalog()}, nil
} }
func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) { func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) {
time.Sleep(extraLatency)
var found *pb.Product var found *pb.Product
for i := 0; i < len(parseCatalog()); i++ { for i := 0; i < len(parseCatalog()); i++ {
if req.Id == parseCatalog()[i].Id { if req.Id == parseCatalog()[i].Id {
@ -177,6 +257,7 @@ func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetProductReque
} }
func (p *productCatalog) SearchProducts(ctx context.Context, req *pb.SearchProductsRequest) (*pb.SearchProductsResponse, error) { func (p *productCatalog) SearchProducts(ctx context.Context, req *pb.SearchProductsRequest) (*pb.SearchProductsResponse, error) {
time.Sleep(extraLatency)
// Intepret query as a substring match in name or description. // Intepret query as a substring match in name or description.
var ps []*pb.Product var ps []*pb.Product
for _, p := range parseCatalog() { for _, p := range parseCatalog() {

View file

@ -1,12 +1,12 @@
FROM python:2.7-slim FROM python:2.7-slim
RUN apt-get update -qqy && \ RUN apt-get update -qqy && \
apt-get -qqy install wget && \ apt-get -qqy install wget g++ && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# show python logs as they occur # show python logs as they occur
ENV PYTHONUNBUFFERED=0 ENV PYTHONUNBUFFERED=0
# download the grpc health probe # download the grpc health probe
RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \
wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /bin/grpc_health_probe chmod +x /bin/grpc_health_probe

View file

@ -14,15 +14,18 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import sys
import grpc import grpc
import demo_pb2 import demo_pb2
import demo_pb2_grpc import demo_pb2_grpc
import sys
from opencensus.trace.tracer import Tracer from opencensus.trace.tracer import Tracer
from opencensus.trace.exporters import stackdriver_exporter from opencensus.trace.exporters import stackdriver_exporter
from opencensus.trace.ext.grpc import client_interceptor from opencensus.trace.ext.grpc import client_interceptor
from logger import getJSONLogger
logger = getJSONLogger('recommendationservice-server')
if __name__ == "__main__": if __name__ == "__main__":
# get port # get port
if len(sys.argv) > 1: if len(sys.argv) > 1:
@ -45,4 +48,4 @@ if __name__ == "__main__":
request = demo_pb2.ListRecommendationsRequest(user_id="test", product_ids=["test"]) request = demo_pb2.ListRecommendationsRequest(user_id="test", product_ids=["test"])
# make call to server # make call to server
response = stub.ListRecommendations(request) response = stub.ListRecommendations(request)
print(response) logger.info(response)

Some files were not shown because too many files have changed in this diff Show more