blob: eda45eca5cf3d62d3652c3b4ed6aa7d646ddc6a7 [file] [log] [blame]
// Copyright Istio 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.
var http = require('http')
var dispatcher = require('httpdispatcher')
var port = parseInt(process.argv[2])
var userAddedRatings = [] // used to demonstrate POST functionality
var unavailable = false
var healthy = true
if (process.env.SERVICE_VERSION === 'v-unavailable') {
// make the service unavailable once in 60 seconds
setInterval(function () {
unavailable = !unavailable
}, 60000);
}
if (process.env.SERVICE_VERSION === 'v-unhealthy') {
// make the service unavailable once in 15 minutes for 15 minutes.
// 15 minutes is chosen since the Kubernetes's exponential back-off is reset after 10 minutes
// of successful execution
// see https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy
// Kiali shows the last 10 or 30 minutes, so to show the error rate of 50%,
// it will be required to run the service for 30 minutes, 15 minutes of each state (healthy/unhealthy)
setInterval(function () {
healthy = !healthy
unavailable = !unavailable
}, 900000);
}
/**
* We default to using mongodb, if DB_TYPE is not set to mysql.
*/
if (process.env.SERVICE_VERSION === 'v2') {
if (process.env.DB_TYPE === 'mysql') {
var mysql = require('mysql')
var hostName = process.env.MYSQL_DB_HOST
var portNumber = process.env.MYSQL_DB_PORT
var username = process.env.MYSQL_DB_USER
var password = process.env.MYSQL_DB_PASSWORD
} else {
var MongoClient = require('mongodb').MongoClient
var url = process.env.MONGO_DB_URL
}
}
dispatcher.onPost(/^\/ratings\/[0-9]*/, function (req, res) {
var productIdStr = req.url.split('/').pop()
var productId = parseInt(productIdStr)
var ratings = {}
if (Number.isNaN(productId)) {
res.writeHead(400, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'please provide numeric product ID'}))
return
}
try {
ratings = JSON.parse(req.body)
} catch (error) {
res.writeHead(400, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'please provide valid ratings JSON'}))
return
}
if (process.env.SERVICE_VERSION === 'v2') { // the version that is backed by a database
res.writeHead(501, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'Post not implemented for database backed ratings'}))
} else { // the version that holds ratings in-memory
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify(putLocalReviews(productId, ratings)))
}
})
dispatcher.onGet(/^\/ratings\/[0-9]*/, function (req, res) {
var productIdStr = req.url.split('/').pop()
var productId = parseInt(productIdStr)
if (Number.isNaN(productId)) {
res.writeHead(400, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'please provide numeric product ID'}))
} else if (process.env.SERVICE_VERSION === 'v2') {
var firstRating = 0
var secondRating = 0
if (process.env.DB_TYPE === 'mysql') {
var connection = mysql.createConnection({
host: hostName,
port: portNumber,
user: username,
password: password,
database: 'test'
})
connection.connect(function(err) {
if (err) {
res.end(JSON.stringify({error: 'could not connect to ratings database'}))
console.log(err)
return
}
connection.query('SELECT Rating FROM ratings', function (err, results, fields) {
if (err) {
res.writeHead(500, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'could not perform select'}))
console.log(err)
} else {
if (results[0]) {
firstRating = results[0].Rating
}
if (results[1]) {
secondRating = results[1].Rating
}
var result = {
id: productId,
ratings: {
Reviewer1: firstRating,
Reviewer2: secondRating
}
}
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify(result))
}
})
// close the connection
connection.end()
})
} else {
MongoClient.connect(url, function (err, client) {
if (err) {
res.writeHead(500, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'could not connect to ratings database'}))
console.log(err)
} else {
const db = client.db("test")
db.collection('ratings').find({}).toArray(function (err, data) {
if (err) {
res.writeHead(500, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'could not load ratings from database'}))
console.log(err)
} else {
if (data[0]) {
firstRating = data[0].rating
}
if (data[1]) {
secondRating = data[1].rating
}
var result = {
id: productId,
ratings: {
Reviewer1: firstRating,
Reviewer2: secondRating
}
}
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify(result))
}
// close client once done:
client.close()
})
}
})
}
} else {
if (process.env.SERVICE_VERSION === 'v-faulty') {
// in half of the cases return error,
// in another half proceed as usual
var random = Math.random(); // returns [0,1]
if (random <= 0.5) {
getLocalReviewsServiceUnavailable(res)
} else {
getLocalReviewsSuccessful(res, productId)
}
}
else if (process.env.SERVICE_VERSION === 'v-delayed') {
// in half of the cases delay for 7 seconds,
// in another half proceed as usual
var random = Math.random(); // returns [0,1]
if (random <= 0.5) {
setTimeout(getLocalReviewsSuccessful, 7000, res, productId)
} else {
getLocalReviewsSuccessful(res, productId)
}
}
else if (process.env.SERVICE_VERSION === 'v-unavailable' || process.env.SERVICE_VERSION === 'v-unhealthy') {
if (unavailable) {
getLocalReviewsServiceUnavailable(res)
} else {
getLocalReviewsSuccessful(res, productId)
}
}
else {
getLocalReviewsSuccessful(res, productId)
}
}
})
dispatcher.onGet('/health', function (req, res) {
if (healthy) {
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify({status: 'Ratings is healthy'}))
} else {
res.writeHead(500, {'Content-type': 'application/json'})
res.end(JSON.stringify({status: 'Ratings is not healthy'}))
}
})
function putLocalReviews (productId, ratings) {
userAddedRatings[productId] = {
id: productId,
ratings: ratings
}
return getLocalReviews(productId)
}
function getLocalReviewsSuccessful(res, productId) {
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify(getLocalReviews(productId)))
}
function getLocalReviewsServiceUnavailable(res) {
res.writeHead(503, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'Service unavailable'}))
}
function getLocalReviews (productId) {
if (typeof userAddedRatings[productId] !== 'undefined') {
return userAddedRatings[productId]
}
return {
id: productId,
ratings: {
'Reviewer1': 5,
'Reviewer2': 4
}
}
}
function handleRequest (request, response) {
try {
console.log(request.method + ' ' + request.url)
dispatcher.dispatch(request, response)
} catch (err) {
console.log(err)
}
}
var server = http.createServer(handleRequest)
server.listen(port, function () {
console.log('Server listening on: http://0.0.0.0:%s', port)
})