Add health checks to Node.js services (#26)
* Move Node healthchecks to gRPC * gitignore proto files * Switch to standard health RPC * Fix lint * Update client.js * Add protos back + update them * node services: fix & run genproto.sh this gets currencyservice to work but paymentservice is still crashing in the docker container. Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com> * Fix docker breaking * update dockerfiles with released health probe Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
This commit is contained in:
parent
360d983512
commit
6c37a96f3a
18 changed files with 313 additions and 109 deletions
|
@ -30,13 +30,11 @@ spec:
|
||||||
- name: grpc
|
- name: grpc
|
||||||
containerPort: 7000
|
containerPort: 7000
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
periodSeconds: 5
|
exec:
|
||||||
tcpSocket:
|
command: ["/bin/grpc_health_probe", "-addr=:7000"]
|
||||||
port: 7000
|
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
periodSeconds: 5
|
exec:
|
||||||
tcpSocket:
|
command: ["/bin/grpc_health_probe", "-addr=:7000"]
|
||||||
port: 7000
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
|
|
|
@ -29,13 +29,11 @@ spec:
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 50051
|
- containerPort: 50051
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
periodSeconds: 5
|
exec:
|
||||||
tcpSocket:
|
command: ["/bin/grpc_health_probe", "-addr=:50051"]
|
||||||
port: 50051
|
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
periodSeconds: 5
|
exec:
|
||||||
tcpSocket:
|
command: ["/bin/grpc_health_probe", "-addr=:50051"]
|
||||||
port: 50051
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
|
|
43
pb/grpc/health/v1/health.proto
Normal file
43
pb/grpc/health/v1/health.proto
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2015 The gRPC Authors
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// The canonical version of this proto can be found at
|
||||||
|
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.health.v1;
|
||||||
|
|
||||||
|
option csharp_namespace = "Grpc.Health.V1";
|
||||||
|
option go_package = "google.golang.org/grpc/health/grpc_health_v1";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option java_outer_classname = "HealthProto";
|
||||||
|
option java_package = "io.grpc.health.v1";
|
||||||
|
|
||||||
|
message HealthCheckRequest {
|
||||||
|
string service = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HealthCheckResponse {
|
||||||
|
enum ServingStatus {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
SERVING = 1;
|
||||||
|
NOT_SERVING = 2;
|
||||||
|
}
|
||||||
|
ServingStatus status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Health {
|
||||||
|
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
FROM node:8
|
FROM node:8
|
||||||
|
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 /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --only=production
|
RUN npm install --only=production
|
||||||
|
|
|
@ -22,7 +22,7 @@ const grpc = require('grpc');
|
||||||
const leftPad = require('left-pad');
|
const leftPad = require('left-pad');
|
||||||
|
|
||||||
const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
|
const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
|
||||||
const PORT = 31337;
|
const PORT = 7000;
|
||||||
|
|
||||||
const shopProto = grpc.load(PROTO_PATH).hipstershop;
|
const shopProto = grpc.load(PROTO_PATH).hipstershop;
|
||||||
const client = new shopProto.CurrencyService(`localhost:${PORT}`,
|
const client = new shopProto.CurrencyService(`localhost:${PORT}`,
|
||||||
|
@ -49,7 +49,7 @@ client.getSupportedCurrencies({}, (err, response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.convert(request, function (err, response) {
|
client.convert(request, (err, response) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(`Error in convert: ${err}`);
|
console.error(`Error in convert: ${err}`);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
# 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
|
|
||||||
# protos are loaded dynamically for node, simply copies over the proto.
|
# protos are loaded dynamically for node, simply copies over the proto.
|
||||||
mkdir -p proto && \
|
mkdir -p proto
|
||||||
cp ../../pb/demo.proto proto
|
cp -r ../../pb/* ./proto
|
||||||
|
|
|
@ -2,17 +2,25 @@
|
||||||
"name": "grpc-currency-service",
|
"name": "grpc-currency-service",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "A gRPC currency conversion microservice",
|
"description": "A gRPC currency conversion microservice",
|
||||||
"repository": "TODO",
|
"repository": "https://github.com/GoogleCloudPlatform/microservices-demo",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"lint": "semistandard *.js"
|
||||||
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/debug-agent": "^2.6.0",
|
"@google-cloud/debug-agent": "^2.6.0",
|
||||||
"@google-cloud/profiler": "^0.1.14",
|
"@google-cloud/profiler": "^0.1.14",
|
||||||
"@google-cloud/trace-agent": "^2.11.0",
|
"@google-cloud/trace-agent": "^2.11.0",
|
||||||
|
"@grpc/proto-loader": "^0.3.0",
|
||||||
"async": "^1.5.2",
|
"async": "^1.5.2",
|
||||||
"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",
|
||||||
"request": "^2.87.0",
|
"request": "^2.87.0",
|
||||||
"xml2js": "^0.4.19"
|
"xml2js": "^0.4.19"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"semistandard": "^12.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,9 @@ message ShipOrderResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
message Address {
|
message Address {
|
||||||
string street_address_1 = 1;
|
string street_address = 1;
|
||||||
string street_address_2 = 2;
|
string city = 2;
|
||||||
string city= 3;
|
string state = 3;
|
||||||
string country = 4;
|
string country = 4;
|
||||||
int32 zip_code = 5;
|
int32 zip_code = 5;
|
||||||
}
|
}
|
||||||
|
@ -202,21 +202,9 @@ message SendOrderConfirmationRequest {
|
||||||
// -------------Checkout service-----------------
|
// -------------Checkout service-----------------
|
||||||
|
|
||||||
service CheckoutService {
|
service CheckoutService {
|
||||||
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {}
|
|
||||||
rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
|
rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateOrderRequest {
|
|
||||||
string user_id = 1;
|
|
||||||
string user_currency = 2;
|
|
||||||
Address address = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CreateOrderResponse {
|
|
||||||
repeated OrderItem items = 1;
|
|
||||||
Money shipping_cost = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PlaceOrderRequest {
|
message PlaceOrderRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
string user_currency = 2;
|
string user_currency = 2;
|
||||||
|
@ -229,3 +217,26 @@ message PlaceOrderRequest {
|
||||||
message PlaceOrderResponse {
|
message PlaceOrderResponse {
|
||||||
OrderResult order = 1;
|
OrderResult order = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------Ads service------------------
|
||||||
|
|
||||||
|
service AdsService {
|
||||||
|
rpc GetAds(AdsRequest) returns (AdsResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message AdsRequest {
|
||||||
|
// List of important key words from the current page describing the context.
|
||||||
|
repeated string context_keys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AdsResponse {
|
||||||
|
repeated Ad ads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ad {
|
||||||
|
// url to redirect to when an ad is clicked.
|
||||||
|
string redirect_url = 1;
|
||||||
|
|
||||||
|
// short advertisement text to display.
|
||||||
|
string text = 2;
|
||||||
|
}
|
||||||
|
|
43
src/currencyservice/proto/grpc/health/v1/health.proto
Normal file
43
src/currencyservice/proto/grpc/health/v1/health.proto
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2015 The gRPC Authors
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// The canonical version of this proto can be found at
|
||||||
|
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.health.v1;
|
||||||
|
|
||||||
|
option csharp_namespace = "Grpc.Health.V1";
|
||||||
|
option go_package = "google.golang.org/grpc/health/grpc_health_v1";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option java_outer_classname = "HealthProto";
|
||||||
|
option java_package = "io.grpc.health.v1";
|
||||||
|
|
||||||
|
message HealthCheckRequest {
|
||||||
|
string service = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HealthCheckResponse {
|
||||||
|
enum ServingStatus {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
SERVING = 1;
|
||||||
|
NOT_SERVING = 2;
|
||||||
|
}
|
||||||
|
ServingStatus status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Health {
|
||||||
|
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
|
||||||
|
}
|
|
@ -16,27 +16,49 @@
|
||||||
|
|
||||||
require('@google-cloud/profiler').start({
|
require('@google-cloud/profiler').start({
|
||||||
serviceContext: {
|
serviceContext: {
|
||||||
service: 'currencyservice',
|
service: 'currencyservice',
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
require('@google-cloud/trace-agent').start();
|
require('@google-cloud/trace-agent').start();
|
||||||
require('@google-cloud/debug-agent').start({
|
require('@google-cloud/debug-agent').start({
|
||||||
serviceContext: {
|
serviceContext: {
|
||||||
service: 'currencyservice',
|
service: 'currencyservice',
|
||||||
version: 'VERSION'
|
version: 'VERSION'
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const grpc = require('grpc');
|
const grpc = require('grpc');
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
const xml2js = require('xml2js');
|
const xml2js = require('xml2js');
|
||||||
|
const protoLoader = require('@grpc/proto-loader');
|
||||||
|
|
||||||
|
const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto');
|
||||||
|
const HEALTH_PROTO_PATH = path.join(__dirname, './proto/grpc/health/v1/health.proto');
|
||||||
|
|
||||||
const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
|
|
||||||
const PORT = 7000;
|
const PORT = 7000;
|
||||||
const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
|
const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
|
||||||
const shopProto = grpc.load(PROTO_PATH).hipstershop;
|
|
||||||
|
const shopProto = _loadProto(MAIN_PROTO_PATH).hipstershop;
|
||||||
|
const healthProto = _loadProto(HEALTH_PROTO_PATH).grpc.health.v1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that loads a protobuf file.
|
||||||
|
*/
|
||||||
|
function _loadProto (path) {
|
||||||
|
const packageDefinition = protoLoader.loadSync(
|
||||||
|
path,
|
||||||
|
{
|
||||||
|
keepCase: true,
|
||||||
|
longs: String,
|
||||||
|
enums: String,
|
||||||
|
defaults: true,
|
||||||
|
oneofs: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return grpc.loadPackageDefinition(packageDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function that gets currency data from an XML webpage
|
* Helper function that gets currency data from an XML webpage
|
||||||
|
@ -116,8 +138,8 @@ function convert (call, callback) {
|
||||||
nanos: euros.nanos * data[request.to_code]
|
nanos: euros.nanos * data[request.to_code]
|
||||||
});
|
});
|
||||||
|
|
||||||
result.units = Math.floor(result.units)
|
result.units = Math.floor(result.units);
|
||||||
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`);
|
console.log(`conversion request successful`);
|
||||||
|
@ -130,6 +152,13 @@ function convert (call, callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint for health checks
|
||||||
|
*/
|
||||||
|
function check (call, callback) {
|
||||||
|
callback(null, { status: 'SERVING' });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an RPC server that receives requests for the
|
* Starts an RPC server that receives requests for the
|
||||||
* CurrencyConverter service at the sample server port
|
* CurrencyConverter service at the sample server port
|
||||||
|
@ -138,6 +167,7 @@ function main () {
|
||||||
console.log(`Starting gRPC server on port ${PORT}...`);
|
console.log(`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.bind(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure());
|
server.bind(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure());
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
FROM node:8
|
FROM node:8
|
||||||
|
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 /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
|
|
@ -16,26 +16,26 @@ const cardValidator = require('simple-card-validator');
|
||||||
const uuid = require('uuid/v4');
|
const uuid = require('uuid/v4');
|
||||||
|
|
||||||
class CreditCardError extends Error {
|
class CreditCardError extends Error {
|
||||||
constructor(message) {
|
constructor (message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = 400; // Invalid argument error
|
this.code = 400; // Invalid argument error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidCreditCard extends CreditCardError {
|
class InvalidCreditCard extends CreditCardError {
|
||||||
constructor(cardType) {
|
constructor (cardType) {
|
||||||
super(`Credit card info is invalid`);
|
super(`Credit card info is invalid`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnacceptedCreditCard extends CreditCardError {
|
class UnacceptedCreditCard extends CreditCardError {
|
||||||
constructor(cardType) {
|
constructor (cardType) {
|
||||||
super(`Sorry, we cannot process ${cardType} credit cards. Only VISA or MasterCard is accepted.`);
|
super(`Sorry, we cannot process ${cardType} credit cards. Only VISA or MasterCard is accepted.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExpiredCreditCard extends CreditCardError {
|
class ExpiredCreditCard extends CreditCardError {
|
||||||
constructor(number, month, year) {
|
constructor (number, month, year) {
|
||||||
super(`Your credit card (ending ${number.substr(-4)}) expired on ${month}/${year}`);
|
super(`Your credit card (ending ${number.substr(-4)}) expired on ${month}/${year}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,34 +46,29 @@ class ExpiredCreditCard extends CreditCardError {
|
||||||
* @param {*} request
|
* @param {*} request
|
||||||
* @return transaction_id - a random uuid v4.
|
* @return transaction_id - a random uuid v4.
|
||||||
*/
|
*/
|
||||||
module.exports = function charge(request) {
|
module.exports = function charge (request) {
|
||||||
const { amount, credit_card: creditCard } = request;
|
const { amount, credit_card: creditCard } = request;
|
||||||
const cardNumber = creditCard.credit_card_number;
|
const cardNumber = creditCard.credit_card_number;
|
||||||
const cardInfo = cardValidator(cardNumber);
|
const cardInfo = cardValidator(cardNumber);
|
||||||
const {
|
const {
|
||||||
card_type: cardType,
|
card_type: cardType,
|
||||||
valid,
|
valid
|
||||||
cvv_length: cvvLength,
|
|
||||||
} = cardInfo.getCardDetails();
|
} = cardInfo.getCardDetails();
|
||||||
|
|
||||||
if (!valid)
|
if (!valid) { throw new InvalidCreditCard(); }
|
||||||
throw new InvalidCreditCard();
|
|
||||||
|
|
||||||
// Only VISA and mastercard is accepted, other card types (AMEX, dinersclub) will
|
// Only VISA and mastercard is accepted, other card types (AMEX, dinersclub) will
|
||||||
// throw UnacceptedCreditCard error.
|
// throw UnacceptedCreditCard error.
|
||||||
if (!(cardType === 'visa' || cardType == 'mastercard'))
|
if (!(cardType === 'visa' || cardType === 'mastercard')) { throw new UnacceptedCreditCard(cardType); }
|
||||||
throw new UnacceptedCreditCard(cardType);
|
|
||||||
|
|
||||||
// Also validate expiration is > today.
|
// Also validate expiration is > today.
|
||||||
const currentMonth = new Date().getMonth() + 1;
|
const currentMonth = new Date().getMonth() + 1;
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
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))
|
if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); }
|
||||||
throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year);
|
|
||||||
|
|
||||||
console.log(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \
|
console.log(`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() };
|
||||||
|
};
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
# protos are loaded dynamically for node, simply copies over the proto.
|
# protos are loaded dynamically for node, simply copies over the proto.
|
||||||
|
mkdir -p proto
|
||||||
mkdir -p proto && \
|
cp -r ../../pb/* ./proto
|
||||||
cp ../../pb/demo.proto proto
|
|
||||||
|
|
|
@ -17,25 +17,25 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('@google-cloud/profiler').start({
|
require('@google-cloud/profiler').start({
|
||||||
serviceContext: {
|
serviceContext: {
|
||||||
service: 'paymentservice',
|
service: 'paymentservice',
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
require('@google-cloud/trace-agent').start();
|
require('@google-cloud/trace-agent').start();
|
||||||
require('@google-cloud/debug-agent').start({
|
require('@google-cloud/debug-agent').start({
|
||||||
serviceContext: {
|
serviceContext: {
|
||||||
service: 'paymentservice',
|
service: 'paymentservice',
|
||||||
version: 'VERSION'
|
version: 'VERSION'
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
const HipsterShopServer = require('./server');
|
const HipsterShopServer = require('./server');
|
||||||
|
|
||||||
const PORT = process.env['PORT'];
|
const PORT = process.env['PORT'];
|
||||||
const PROTO_PATH = __dirname + '/proto/demo.proto';
|
const PROTO_PATH = path.join(__dirname, '/proto/');
|
||||||
|
|
||||||
const server = new HipsterShopServer(PROTO_PATH, PORT);
|
const server = new HipsterShopServer(PROTO_PATH, PORT);
|
||||||
|
|
||||||
server.listen();
|
server.listen();
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
"name": "paymentservice",
|
"name": "paymentservice",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "Payment Microservice demo",
|
"description": "Payment Microservice demo",
|
||||||
|
"repository": "https://github.com/GoogleCloudPlatform/microservices-demo",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"lint": "semistandard *.js"
|
||||||
},
|
},
|
||||||
"author": "Jonathan Lui",
|
"author": "Jonathan Lui",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -16,5 +18,8 @@
|
||||||
"grpc": "^1.12.3",
|
"grpc": "^1.12.3",
|
||||||
"simple-card-validator": "^1.1.0",
|
"simple-card-validator": "^1.1.0",
|
||||||
"uuid": "^3.2.1"
|
"uuid": "^3.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"semistandard": "^12.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,9 @@ message ShipOrderResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
message Address {
|
message Address {
|
||||||
string street_address_1 = 1;
|
string street_address = 1;
|
||||||
string street_address_2 = 2;
|
string city = 2;
|
||||||
string city= 3;
|
string state = 3;
|
||||||
string country = 4;
|
string country = 4;
|
||||||
int32 zip_code = 5;
|
int32 zip_code = 5;
|
||||||
}
|
}
|
||||||
|
@ -202,21 +202,9 @@ message SendOrderConfirmationRequest {
|
||||||
// -------------Checkout service-----------------
|
// -------------Checkout service-----------------
|
||||||
|
|
||||||
service CheckoutService {
|
service CheckoutService {
|
||||||
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {}
|
|
||||||
rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
|
rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateOrderRequest {
|
|
||||||
string user_id = 1;
|
|
||||||
string user_currency = 2;
|
|
||||||
Address address = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CreateOrderResponse {
|
|
||||||
repeated OrderItem items = 1;
|
|
||||||
Money shipping_cost = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PlaceOrderRequest {
|
message PlaceOrderRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
string user_currency = 2;
|
string user_currency = 2;
|
||||||
|
@ -229,3 +217,26 @@ message PlaceOrderRequest {
|
||||||
message PlaceOrderResponse {
|
message PlaceOrderResponse {
|
||||||
OrderResult order = 1;
|
OrderResult order = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------Ads service------------------
|
||||||
|
|
||||||
|
service AdsService {
|
||||||
|
rpc GetAds(AdsRequest) returns (AdsResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message AdsRequest {
|
||||||
|
// List of important key words from the current page describing the context.
|
||||||
|
repeated string context_keys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AdsResponse {
|
||||||
|
repeated Ad ads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ad {
|
||||||
|
// url to redirect to when an ad is clicked.
|
||||||
|
string redirect_url = 1;
|
||||||
|
|
||||||
|
// short advertisement text to display.
|
||||||
|
string text = 2;
|
||||||
|
}
|
||||||
|
|
43
src/paymentservice/proto/grpc/health/v1/health.proto
Normal file
43
src/paymentservice/proto/grpc/health/v1/health.proto
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2015 The gRPC Authors
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// The canonical version of this proto can be found at
|
||||||
|
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.health.v1;
|
||||||
|
|
||||||
|
option csharp_namespace = "Grpc.Health.V1";
|
||||||
|
option go_package = "google.golang.org/grpc/health/grpc_health_v1";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option java_outer_classname = "HealthProto";
|
||||||
|
option java_package = "io.grpc.health.v1";
|
||||||
|
|
||||||
|
message HealthCheckRequest {
|
||||||
|
string service = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HealthCheckResponse {
|
||||||
|
enum ServingStatus {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
SERVING = 1;
|
||||||
|
NOT_SERVING = 2;
|
||||||
|
}
|
||||||
|
ServingStatus status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Health {
|
||||||
|
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
|
||||||
|
}
|
|
@ -12,17 +12,23 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
const grpc = require('grpc');
|
const grpc = require('grpc');
|
||||||
const protoLoader = require('@grpc/proto-loader');
|
const protoLoader = require('@grpc/proto-loader');
|
||||||
|
|
||||||
const charge = require('./charge');
|
const charge = require('./charge');
|
||||||
|
|
||||||
class HipsterShopServer {
|
class HipsterShopServer {
|
||||||
constructor(protoFile, port = HipsterShopServer.DEFAULT_PORT) {
|
constructor (protoRoot, port = HipsterShopServer.DEFAULT_PORT) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
|
||||||
|
this.packages = {
|
||||||
|
hipsterShop: this.loadProto(path.join(protoRoot, 'demo.proto')),
|
||||||
|
health: this.loadProto(path.join(protoRoot, 'grpc/health/v1/health.proto'))
|
||||||
|
};
|
||||||
|
|
||||||
this.server = new grpc.Server();
|
this.server = new grpc.Server();
|
||||||
this.loadProto(protoFile);
|
this.loadAllProtos(protoRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,10 +36,10 @@ class HipsterShopServer {
|
||||||
* @param {*} call { ChargeRequest }
|
* @param {*} call { ChargeRequest }
|
||||||
* @param {*} callback fn(err, ChargeResponse)
|
* @param {*} callback fn(err, ChargeResponse)
|
||||||
*/
|
*/
|
||||||
static ChargeServiceHandler(call, callback) {
|
static ChargeServiceHandler (call, callback) {
|
||||||
try {
|
try {
|
||||||
console.log(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`)
|
console.log(`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) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
|
@ -41,13 +47,17 @@ class HipsterShopServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listen() {
|
static CheckHandler (call, callback) {
|
||||||
|
callback(null, { status: 'SERVING' });
|
||||||
|
}
|
||||||
|
|
||||||
|
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}`);
|
console.log(`PaymentService grpc server listening on ${this.port}`);
|
||||||
this.server.start();
|
this.server.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadProto(path) {
|
loadProto (path) {
|
||||||
const packageDefinition = protoLoader.loadSync(
|
const packageDefinition = protoLoader.loadSync(
|
||||||
path,
|
path,
|
||||||
{
|
{
|
||||||
|
@ -55,21 +65,28 @@ class HipsterShopServer {
|
||||||
longs: String,
|
longs: String,
|
||||||
enums: String,
|
enums: String,
|
||||||
defaults: true,
|
defaults: true,
|
||||||
oneofs: true,
|
oneofs: true
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
return grpc.loadPackageDefinition(packageDefinition);
|
||||||
const hipsterShopPackage = protoDescriptor.hipstershop;
|
|
||||||
|
|
||||||
this.addProtoService(hipsterShopPackage.PaymentService.service);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addProtoService(service) {
|
loadAllProtos (protoRoot) {
|
||||||
|
const hipsterShopPackage = this.packages.hipsterShop.hipstershop;
|
||||||
|
const healthPackage = this.packages.health.grpc.health.v1;
|
||||||
|
|
||||||
this.server.addService(
|
this.server.addService(
|
||||||
service,
|
hipsterShopPackage.PaymentService.service,
|
||||||
{
|
{
|
||||||
charge: HipsterShopServer.ChargeServiceHandler.bind(this),
|
charge: HipsterShopServer.ChargeServiceHandler.bind(this)
|
||||||
},
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.server.addService(
|
||||||
|
healthPackage.Health.service,
|
||||||
|
{
|
||||||
|
check: HipsterShopServer.CheckHandler.bind(this)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue