Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat(payment-service): Adds new payment service written in Python #80

Merged
merged 8 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Unguard is composed of eight microservices written in different languages that t
| [like-service](./src/like-service) | PHP | default | Serves REST API for adding likes to posts using MariaDB; vulnerable to SQL injection attacks |
| [user-auth-service](./src/user-auth-service) | Node.js Express | default | Serves REST API for authenticating users with JWT tokens (vulnerable to JWT key confusion). |
| [status-service](./src/status-service) | Go | unguard-status | Serves REST API for Kubernetes deployments health, as well as a user and user role list (vulnerable to SQL injection) |
| [payment-service](./src/payment-service) | Python Flask | default | Serves REST API for adding and retrieving credit card payment information associated with a user. |
| jaeger | | default | The [Jaeger](https://www.jaegertracing.io/) stack for distributed tracing. |
| mariadb | | unguard-mariadb | Relational database that holds user and token data. |
| redis | | default | Key-value store that holds all user data (except authentication-related stuff). |
Expand Down
2 changes: 2 additions & 0 deletions chart/templates/frontend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ spec:
value: {{ quote .Values.frontend.deployment.container.env.PROFILE_SERVICE_ADDRESS }}
- name: LIKE_SERVICE_ADDRES
value: {{ quote .Values.frontend.deployment.container.env.LIKE_SERVICE_ADDRES }}
- name: PAYMENT_SERVICE_ADDRESS
value: {{ quote .Values.frontend.deployment.container.env.PAYMENT_SERVICE_ADDRESS }}
- name: FRONTEND_BASE_PATH
value: {{ quote .Values.frontend.deployment.container.env.FRONTEND_BASE_PATH }}
- name: AD_SERVICE_BASE_PATH
Expand Down
85 changes: 85 additions & 0 deletions chart/templates/payment-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{{- /*
Copyright 2024 Dynatrace 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: v1
kind: Service
metadata:
name: unguard-{{.Values.paymentService.name}}
labels:
app.kubernetes.io/name: {{.Values.paymentService.name}}
app.kubernetes.io/part-of: unguard
spec:
type: {{ .Values.paymentService.service.type }}
selector:
app.kubernetes.io/name: {{.Values.paymentService.name}}
app.kubernetes.io/part-of: unguard
ports:
- targetPort: {{ .Values.paymentService.containerPort }}
port: {{ .Values.paymentService.service.ports.port }}

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: unguard-{{.Values.paymentService.name}}
labels:
app.kubernetes.io/name: {{.Values.paymentService.name}}
app.kubernetes.io/part-of: unguard
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{.Values.paymentService.name}}
app.kubernetes.io/part-of: unguard
strategy:
type: {{.Values.paymentService.deployment.strategy.type}}
template:
metadata:
labels:
app.kubernetes.io/name: {{.Values.paymentService.name}}
app.kubernetes.io/part-of: unguard
spec:
containers:
- name: {{.Values.paymentService.name}}
image: {{.Values.paymentService.deployment.container.image.repository}}:{{.Values.envoyProxy.deployment.container.image.tag}}
imagePullPolicy: {{.Values.paymentService.deployment.container.image.pullPolicy}}
ports:
- containerPort: {{ .Values.paymentService.containerPort }}
env:
orazefabian marked this conversation as resolved.
Show resolved Hide resolved
- name: SERVER_PORT
value: {{ quote .Values.paymentService.containerPort }}
- name: API_PATH
value: {{ quote .Values.paymentService.deployment.container.env.API_PATH }}
- name: OTEL_LOGS_EXPORTER
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_LOGS_EXPORTER }}
- name: OTEL_METRICS_EXPORTER
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_METRICS_EXPORTER }}
- name: OTEL_RESOURCE_ATTRIBUTES
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_RESOURCE_ATTRIBUTES }}
- name: OTEL_TRACES_EXPORTER
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_TRACES_EXPORTER }}
- name: OTEL_EXPERIMENTAL_SDK_ENABLED
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_EXPERIMENTAL_SDK_ENABLED }}
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_EXPORTER_OTLP_ENDPOINT }}
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_EXPORTER_OTLP_PROTOCOL }}
- name: OTEL_PROPAGATORS
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_PROPAGATORS }}
- name: OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED
value: {{ quote .Values.paymentService.deployment.container.env.OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED }}
- name: PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION
value: "python"

8 changes: 8 additions & 0 deletions chart/tracing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ userAuthService:
env:
JAEGER_SAMPLER_PARAM: 1
JAEGER_DISABLED: false

paymentService:
deployment:
container:
env:
OTEL_TRACES_EXPORTER: otlp
OTEL_EXPERIMENTAL_SDK_ENABLED: true
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED: true
32 changes: 32 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,37 @@ likeService:
name: "{{ .Values.mariaDB.serviceName }}"
key: "{{ .Values.mariaDB.password }}"

# Payment Service
paymentService:
name: payment-service

containerPort: 8084

service:
type: ClusterIP
ports:
port: 80

deployment:
strategy:
type: Recreate
container:
image:
repository: ghcr.io/dynatrace-oss/unguard/unguard-payment-service
tag: 0.9.2
pullPolicy: IfNotPresent
env:
API_PATH: /payment-service
OTEL_LOGS_EXPORTER: none
OTEL_METRICS_EXPORTER: none
OTEL_RESOURCE_ATTRIBUTES: service.name=unguard-payment-service
OTEL_TRACES_EXPORTER: none
OTEL_EXPERIMENTAL_SDK_ENABLED: false
W3D3 marked this conversation as resolved.
Show resolved Hide resolved
OTEL_EXPORTER_OTLP_ENDPOINT: "http://jaeger-collector:4318"
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"
OTEL_PROPAGATORS: "jaeger"
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED: false

# Frontend
frontend:
name: frontend
Expand Down Expand Up @@ -449,6 +480,7 @@ frontend:
MEMBERSHIP_SERVICE_ADDRESS: unguard-membership-service
PROFILE_SERVICE_ADDRESS: unguard-profile-service
LIKE_SERVICE_ADDRES: unguard-like-service
PAYMENT_SERVICE_ADDRESS: unguard-payment-service
FRONTEND_BASE_PATH: /ui
AD_SERVICE_BASE_PATH: /ad-service
LIKE_SERVICE_BASE_PATH: /like-service
Expand Down
2 changes: 1 addition & 1 deletion docs/TRACING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ See in the Unguard Chart [README](../chart/README.md#tracing-and-jaeger)
1. Port-forward the Jaeger UI

```sh
kubectl port-forward -n unguard service/jaeger-query 16686:16686
kubectl port-forward -n unguard service/jaeger-query 16686:80
orazefabian marked this conversation as resolved.
Show resolved Hide resolved
```

2. Open [localhost:16686](http://localhost:16686)
Expand Down
Binary file modified docs/images/unguard-architecture.fig
Binary file not shown.
276 changes: 147 additions & 129 deletions docs/images/unguard-architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions skaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ build:
context: src/profile-service
- image: unguard-like-service
context: src/like-service
- image: unguard-payment-service
context: src/payment-service

local:
# only the docker CLI respects a manually logged-in daemon
Expand Down Expand Up @@ -105,6 +107,9 @@ deploy:
# like-service
likeService.deployment.container.image.repository: "{{.IMAGE_REPO_unguard_like_service}}"
likeService.deployment.container.image.tag: "{{.IMAGE_TAG_unguard_like_service}}@{{.IMAGE_DIGEST_unguard_like_service}}"
# payment-service
paymentService.deployment.container.image.repository: "{{.IMAGE_REPO_unguard_payment_service}}"
paymentService.deployment.container.image.tag: "{{.IMAGE_TAG_unguard_payment_service}}@{{.IMAGE_DIGEST_unguard_payment_service}}"

setValues:
# ad-service
Expand All @@ -129,6 +134,8 @@ deploy:
profileService.deployment.container.image.pullPolicy: "IfNotPresent"
# like-service
likeService.deployment.container.image.pullPolicy: "IfNotPresent"
# payment-service
paymentService.deployment.container.image.pullPolicy: "IfNotPresent"

profiles:
- name: jib
Expand Down
31 changes: 19 additions & 12 deletions src/frontend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const {extendURL} = require("./controller/utilities.js");
const {loggerFactory} = require('./controller/loggerFactory');

const site = require("./site");
const {join} = require("path");

if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
Expand All @@ -48,6 +49,7 @@ const membershipServiceApiLogger = microserviceLoggerFactory.create('MEMBERSHIP_
const statusServiceApiLogger = microserviceLoggerFactory.create('STATUS_SERVICE_API');
const profileServiceLogger = microserviceLoggerFactory.create('PROFILE_SERVICE_API');
const likeServiceLogger = microserviceLoggerFactory.create('LIKE_SERVICE_API')
const paymentServiceLogger = microserviceLoggerFactory.create('PAYMENT_SERVICE_API')

// log all environment variables
frontendLogger.info("JAEGER_SERVICE_NAME is set to " + process.env.JAEGER_SERVICE_NAME);
Expand All @@ -64,6 +66,7 @@ frontendLogger.info("AD_SERVICE_BASE_PATH is set to " + process.env.AD_SERVICE_B
frontendLogger.info("STATUS_SERVICE_BASE_PATH is set to " + process.env.STATUS_SERVICE_BASE_PATH);
frontendLogger.info("PROFILE_SERVICE_ADDRESS is set to " + process.env.PROFILE_SERVICE_ADDRESS);
frontendLogger.info("LIKE_SERVICE_ADDRESS is set to " + process.env.LIKE_SERVICE_ADDRES);
frontendLogger.info("PAYMENT_SERVICE_ADDRESS is set to " + process.env.PAYMENT_SERVICE_ADDRESS);

let app = express();

Expand All @@ -85,8 +88,8 @@ app.use(process.env.FRONTEND_BASE_PATH + "/js/jquery", express.static(path.join(
app.use(process.env.FRONTEND_BASE_PATH + "/js/bootstrap", express.static(path.join(__dirname, "node_modules/bootstrap/dist/js")));

app.use(process.env.FRONTEND_BASE_PATH, express.static(path.join(__dirname, 'public')));
// for non generated content, serve the static folder
app.use(process.env.FRONTEND_BASE_PATH, express.static(path.join(__dirname, 'static')));
// for non-generated content, serve the static folder
app.use(process.env.FRONTEND_BASE_PATH, express.static(join(__dirname, 'static')));

// Setup tracer
const tracer = initTracerFromEnv({
Expand All @@ -98,15 +101,15 @@ const tracer = initTracerFromEnv({
const applyTracingInterceptors = createAxiosTracing(tracer);

// enable express server side tracing
app.use(expressOpentracing({ tracer }));
app.use(expressOpentracing({tracer}));

// by trusting the proxy, express uses x-forwarded-for header to set the remote ip header
app.set('trust proxy', true)

// enable cookie parsing
app.use(cookieParser());
// for parsing application/xwww-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.urlencoded({extended: true}));

function createAxiosInstance(req, baseURL, logger, headers) {
const axiosInstace = axios.create({baseURL, headers});
Expand All @@ -133,26 +136,29 @@ function createAxiosInstance(req, baseURL, logger, headers) {
}

// setup 7 custom axios instances configured to talk to each microservice respectively
// (MICROBLOG_API, PROXY, USER_AUTH_API, AD_SERVICE_API, PROFILE_SERVICE_API, LIKE_SERVICE_API and STATUS_SERVICE_API). All with Jaeger tracing enabled
// (MICROBLOG_API, PROXY, USER_AUTH_API, AD_SERVICE_API, PROFILE_SERVICE_API, LIKE_SERVICE_API, STATUS_SERVICE_API and PAYMENT_SERVICE_API).
// All with Jaeger tracing enabled
app.use((req, res, next) => {
const cookieHeader = req.cookies.jwt ? {"Cookie": "jwt=" + req.cookies.jwt} : {};

const MICROBLOG_API = createAxiosInstance(req, "http://" + process.env.MICROBLOG_SERVICE_ADDRESS, microblogLogger, cookieHeader);
const PROXY = createAxiosInstance(req, "http://" + process.env.PROXY_SERVICE_ADDRESS, proxyLogger);
const USER_AUTH_API = createAxiosInstance(req, "http://" + process.env.USER_AUTH_SERVICE_ADDRESS, userAuthApiLogger, cookieHeader);
const AD_SERVICE_API = createAxiosInstance(req,"http://" + process.env.AD_SERVICE_ADDRESS + process.env.AD_SERVICE_BASE_PATH, adServiceApiLogger, cookieHeader);
const AD_SERVICE_API = createAxiosInstance(req, "http://" + process.env.AD_SERVICE_ADDRESS + process.env.AD_SERVICE_BASE_PATH, adServiceApiLogger, cookieHeader);
const MEMBERSHIP_SERVICE_API = createAxiosInstance(req, "http://" + process.env.MEMBERSHIP_SERVICE_ADDRESS + process.env.MEMBERSHIP_SERVICE_BASE_PATH, membershipServiceApiLogger, cookieHeader)
const PROFILE_SERVICE_API = createAxiosInstance(req, "http://" + process.env.PROFILE_SERVICE_ADDRESS, profileServiceLogger);
const STATUS_SERVICE_API =createAxiosInstance(req, "http://" + process.env.STATUS_SERVICE_ADDRESS + process.env.STATUS_SERVICE_BASE_PATH, statusServiceApiLogger);
const STATUS_SERVICE_API = createAxiosInstance(req, "http://" + process.env.STATUS_SERVICE_ADDRESS + process.env.STATUS_SERVICE_BASE_PATH, statusServiceApiLogger);
const LIKE_SERVICE_API = createAxiosInstance(req, "http://" + process.env.LIKE_SERVICE_ADDRES, likeServiceLogger, cookieHeader);
const PAYMENT_SERVICE_API = createAxiosInstance(req, "http://" + process.env.PAYMENT_SERVICE_ADDRESS, paymentServiceLogger, cookieHeader);

applyTracingInterceptors(MICROBLOG_API, {span: req.span});
applyTracingInterceptors(PROXY, {span: req.span});
applyTracingInterceptors(USER_AUTH_API, {span: req.span});
applyTracingInterceptors(AD_SERVICE_API, {span: req.span});
applyTracingInterceptors(STATUS_SERVICE_API, {span: req.span});
applyTracingInterceptors(MICROBLOG_API, {span: req.span});
applyTracingInterceptors(PROXY, {span: req.span});
applyTracingInterceptors(USER_AUTH_API, {span: req.span});
applyTracingInterceptors(AD_SERVICE_API, {span: req.span});
applyTracingInterceptors(STATUS_SERVICE_API, {span: req.span});
applyTracingInterceptors(PROFILE_SERVICE_API, {span: req.span});
applyTracingInterceptors(LIKE_SERVICE_API, {span: req.span});
applyTracingInterceptors(PAYMENT_SERVICE_API, {span: req.span});

req.MICROBLOG_API = MICROBLOG_API;
req.PROXY = PROXY;
Expand All @@ -162,6 +168,7 @@ app.use((req, res, next) => {
req.STATUS_SERVICE_API = STATUS_SERVICE_API;
req.PROFILE_SERVICE_API = PROFILE_SERVICE_API;
req.LIKE_SERVICE_API = LIKE_SERVICE_API;
req.PAYMENT_SERVICE_API = PAYMENT_SERVICE_API;

next();
});
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/controller/errorHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ exports.handleError = function (error) {
let outMessage = message || "No detailed message available.";

if (response) {
return { error: outMessage, message: data }
return { title: outMessage, error: outMessage, message: data }
} else if (request) {
const requestMetadata = {
message: error.message,
Expand All @@ -36,9 +36,9 @@ exports.handleError = function (error) {
path: url
}

return { error: errorTitle, message: requestMetadata };
return { title: errorTitle, error: errorTitle, message: requestMetadata };
} else {
return { error: errorTitle, message: { message: error.message } } // Lowest level error
return { title: errorTitle, error: errorTitle, message: { message: error.message } } // Lowest level error
}
}

Expand Down
Loading
Loading