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

🐘 Add like-service (PHP) for liking/unliking posts #63

Merged
merged 11 commits into from
Nov 20, 2023
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
🧑‍💻 feat(like-service): Add Jaeger support
eliasgierlinger committed Nov 20, 2023
commit 40758dcfd29548fa17d14097bfaa9124cf24b89e
26 changes: 26 additions & 0 deletions chart/jaeger-otlp-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2023 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.

# This enables OpenTelemetry ingress for Jaeger when applied using --values with helm install
collector:
W3D3 marked this conversation as resolved.
Show resolved Hide resolved
service:
otlp:
http:
port: 4318
name: otlp-http
nodePort: 4318
grpc:
port: 4317
name: otlp-grpc
nodePort: 4317
2 changes: 2 additions & 0 deletions chart/templates/frontend.yaml
Original file line number Diff line number Diff line change
@@ -71,6 +71,8 @@ spec:
value: {{ quote .Values.frontend.deployment.container.env.MEMBERSHIP_SERVICE_ADDRESS }}
- name: PROFILE_SERVICE_ADDRESS
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: FRONTEND_BASE_PATH
value: {{ quote .Values.frontend.deployment.container.env.FRONTEND_BASE_PATH }}
- name: AD_SERVICE_BASE_PATH
8 changes: 4 additions & 4 deletions chart/templates/like-service.yaml
Original file line number Diff line number Diff line change
@@ -72,10 +72,10 @@ spec:
{{if .Values.tracing.enabled}}
- name: JAEGER_AGENT_HOST
value: {{ quote (printf "%s-%s" .Values.jaeger.name .Values.likeService.deployment.container.env.JAEGER_AGENT_HOST) }}
- name: JAEGER_SAMPLER_TYPE
value: {{ quote .Values.likeService.deployment.container.env.JAEGER_SAMPLER_TYPE }}
- name: JAEGER_SAMPLER_PARAM
value: {{ quote .Values.likeService.deployment.container.env.JAEGER_SAMPLER_PARAM }}
- name: JAEGER_PORT
value: {{ quote .Values.likeService.deployment.container.env.JAEGER_PORT }}
- name: SERVICE_NAME
value: {{ quote .Values.likeService.deployment.container.env.SERVICE_NAME }}
- name: JAEGER_DISABLED
value: {{ quote .Values.likeService.deployment.container.env.JAEGER_DISABLED }}
{{end}}
6 changes: 6 additions & 0 deletions chart/tracing.yaml
Original file line number Diff line number Diff line change
@@ -15,6 +15,12 @@ frontend:
JAEGER_SAMPLER_PARAM: 1
JAEGER_DISABLED: false

likeService:
deployment:
container:
env:
JAEGER_DISABLED: false

microblogService:
deployment:
container:
6 changes: 3 additions & 3 deletions chart/values.yaml
Original file line number Diff line number Diff line change
@@ -404,10 +404,10 @@ likeService:
ports:
containerPort: 8000
env:
JAEGER_AGENT_HOST: agent # change depending on your jaeger deployment
JAEGER_SAMPLER_TYPE: const
JAEGER_SAMPLER_PARAM: 0
JAEGER_AGENT_HOST: collector # PHP OpenTelemetry sends data to jaeger-collector instead of jaeger-agent
JAEGER_DISABLED: true
JAEGER_PORT: 4318
SERVICE_NAME: unguard-like-service
SERVER_PORT: 8000
API_PATH: /like-service
USER_AUTH_SERVICE_ADDRESS: unguard-user-auth-service
6 changes: 5 additions & 1 deletion docs/TRACING.md
Original file line number Diff line number Diff line change
@@ -28,7 +28,11 @@ This document explains how to install Jaeger tracing using Helm to the cluster.
## Install Jaeger

1. For local development
1. Install the Jaeger-Operator
1. Install Jaeger (takes a couple of minutes)
```sh
helm install jaeger jaegertracing/jaeger --version 0.71.14 --wait --namespace unguard --create-namespace --values ./chart/jaeger-otlp-values.yaml
```
2. Install the Jaeger-Operator
W3D3 marked this conversation as resolved.
Show resolved Hide resolved
```sh
helm install jaeger-operator jaegertracing/jaeger-operator --version 2.22.0 --wait --namespace unguard --create-namespace
```
4 changes: 2 additions & 2 deletions src/frontend/app.js
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ frontendLogger.info("FRONTEND_BASE_PATH is set to " + process.env.FRONTEND_BASE_
frontendLogger.info("AD_SERVICE_BASE_PATH is set to " + process.env.AD_SERVICE_BASE_PATH);
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.UNGUARD_LIKE_SERVICE_SERVICE_HOST + ":" + process.env.UNGUARD_LIKE_SERVICE_SERVICE_PORT); //TODO: find out why LIKE_SERVICE_ADDRESS does not work
frontendLogger.info("LIKE_SERVICE_ADDRESS is set to " + process.env.LIKE_SERVICE_ADDRES);

let app = express();

@@ -144,7 +144,7 @@ app.use((req, res, next) => {
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 LIKE_SERVICE_API = createAxiosInstance(req, "http://" + process.env.UNGUARD_LIKE_SERVICE_SERVICE_HOST + ":" + process.env.UNGUARD_LIKE_SERVICE_SERVICE_PORT, likeServiceLogger, cookieHeader);
const LIKE_SERVICE_API = createAxiosInstance(req, "http://" + process.env.LIKE_SERVICE_ADDRES, likeServiceLogger, cookieHeader);

applyTracingInterceptors(MICROBLOG_API, {span: req.span});
applyTracingInterceptors(PROXY, {span: req.span});
26 changes: 12 additions & 14 deletions src/frontend/site.js
AlejandroCamba marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -403,21 +403,19 @@ async function getLikeCount(req, postId) {
return response.data
}

async function getMultipleLikeCounts(req, postIds) {
let response = await fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.get(`/like-service/like-count/`, { params: { postIds: postIds } }))
return response.data
}

async function insertLikeCountIntoPostArray(req, data) {
//return new Promise(async (resolve, reject) => {
let finalData = [];
await Promise.all(
data.map(async (post) => {
let postId = post.postId;
let likeData = await getLikeCount(req, postId);
let likeCount = likeData.likeCount;
let userLiked = likeData.userLiked;
post = {...post, likeCount: likeCount, userLiked: userLiked};
finalData.push(post);
})
);
return finalData;
//})
let likeData = await getMultipleLikeCounts(req, data.map(post => post.postId));

return data.map(post => {
let likeCount = likeData.likeCounts.find(likeCount => likeCount.postId == post.postId)?.likeCount ?? 0;
let userLiked = likeData.likedPosts.some(like => like.postId == post.postId);
return {...post, likeCount: likeCount, userLiked: userLiked};
});
}


7 changes: 6 additions & 1 deletion src/like-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:7.3-cli
FROM php:8.0-cli
W3D3 marked this conversation as resolved.
Show resolved Hide resolved

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
W3D3 marked this conversation as resolved.
Show resolved Hide resolved

@@ -12,6 +12,11 @@ ENV COMPOSER_ALLOW_SUPERUSER=1

COPY . /var/www

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions opentelemetry

#This is faster https://github.com/composer/composer/issues/8205#issuecomment-507256979
RUN php /usr/bin/composer config --global repos.packagist composer https://packagist.org

137 changes: 137 additions & 0 deletions src/like-service/app/Http/Controllers/JaegerPropagator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

namespace app\Http\Controllers;

use OpenTelemetry\API\Trace\Propagation\TraceContextValidator;
use function count;
use function explode;
use function hexdec;
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\API\Trace\TraceFlags;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter;
use OpenTelemetry\Context\Propagation\PropagationGetterInterface;
use OpenTelemetry\Context\Propagation\PropagationSetterInterface;
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;

/**
* There's different formats used for propagation (reading/transmitting trace and span data to identify
* causal relationships between spans or, put differently, connect spans of different services)
* The PHP OpenTelemetry library seemingly only supports the W3C Trace Context format
* (https://www.w3.org/TR/trace-context/); that class is found here: OpenTelemetry\API\Trace\Propagation\TraceContextPropagator.
eliasgierlinger marked this conversation as resolved.
Show resolved Hide resolved
* This class instead uses the Jaeger Format
* (https://www.jaegertracing.io/docs/1.49/client-libraries/#propagation-format).
*
* The identifying data here is transmitted via an HTTP header called uber-trace-id, whereas
* the W3C format has two headers, traceparent and tracestate, the latter of which being vendor-specific.
* The W3C traceparent is structured like this:
* version "-" trace-id "-" parent-id "-" trace-flags ------ version seems to always be 00, trace-id is a 32-digit hex string, parent-id is a 16-digit hex string, and trace-flags is a 2-digit hex string
*
* The Jaeger uber-trace-id is strcutured thus:
* trace-id ":" span-id ":" parent-span-id ":" flags ------ trace-id is a 16- or 32-digit hex string, span-id is a 16-digit hex string, parent-span-id is a 16-digit hex string (and deprecated), flags is a 1- or 2-digit hex string
*
* The formats are very similar. Therefore, this class is mostly adapted from the official TraceContextPropagator class.
*/
final class JaegerPropagator implements TextMapPropagatorInterface
{
public const UBERTRACEID = 'uber-trace-id';

public const FIELDS = [
self::UBERTRACEID
];

private static ?self $instance = null;

public static function getInstance(): self
{
if (null === self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

/** {@inheritdoc} */
public function fields(): array
{
return self::FIELDS;
}

/** {@inheritdoc} */
public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void
{
$setter ??= ArrayAccessGetterSetter::getInstance();
$context ??= Context::getCurrent();
$spanContext = Span::fromContext($context)->getContext();

if (!$spanContext->isValid()) {
return;
}

// Build and inject the ubertraceid header
$ubertraceid = $spanContext->getTraceId() . ':' . $spanContext->getSpanId() . ':' . $spanContext->getTraceId() . ':' . ($spanContext->isSampled() ? '01' : '00');
$setter->set($carrier, self::UBERTRACEID, $ubertraceid);
}

/** {@inheritdoc} */
public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface
{
$getter ??= ArrayAccessGetterSetter::getInstance();
$context ??= Context::getCurrent();

$spanContext = self::extractImpl($carrier, $getter);
if (!$spanContext->isValid()) {
return $context;
}

return $context->withContextValue(Span::wrap($spanContext));
}

private static function extractImpl($carrier, PropagationGetterInterface $getter): SpanContextInterface
{
$ubertraceid = $getter->get($carrier, self::UBERTRACEID);
if ($ubertraceid === null) {
return SpanContext::getInvalid();
}

// ubertraceid = {trace-id}:{span-id}:{parent-span-id}:{flags}
$pieces = explode(':', $ubertraceid);

// If the header does not have at least 4 pieces, it is invalid -- restart the trace.
if (count($pieces) < 4) {
return SpanContext::getInvalid();
}

[$traceId_unpadded, $spanId_unpadded, $parentSpanId_ignored, $traceFlags_unpadded] = $pieces;

// Pad in order to pass the Validators below
$traceId = str_pad($traceId_unpadded, SpanContextValidator::TRACE_LENGTH, "0", STR_PAD_LEFT);
$spanId = str_pad($spanId_unpadded, SpanContextValidator::SPAN_LENGTH, "0", STR_PAD_LEFT);
$traceFlags = str_pad($traceFlags_unpadded, TraceContextValidator::TRACE_FLAG_LENGTH, "0", STR_PAD_LEFT);

/**
* Return invalid if:
* Trace trace ID, span ID or trace flag are invalid
*/
if (!SpanContextValidator::isValidTraceId($traceId)
|| !SpanContextValidator::isValidSpanId($spanId)
|| !TraceContextValidator::isValidTraceFlag($traceFlags)
) {
return SpanContext::getInvalid();
}

// Only the sampled flag is extracted from the traceFlags (00000001)
$convertedTraceFlags = hexdec($traceFlags);
$isSampled = ($convertedTraceFlags & TraceFlags::SAMPLED) === TraceFlags::SAMPLED;

return SpanContext::createFromRemoteParent(
$traceId,
$spanId,
$isSampled ? TraceFlags::SAMPLED : TraceFlags::DEFAULT
);
}
}
21 changes: 21 additions & 0 deletions src/like-service/app/Http/Controllers/LikeController.php
Original file line number Diff line number Diff line change
@@ -55,6 +55,27 @@ static function getLikeCountAndState($request, $postId)
], 200);
}

static function getMultipleLikeCountsAndStates($request)
{
$user_token = $request->cookie('jwt');
$postIds = $request->query('postIds', $request->query('postIds[]', []));

if (!self::validateToken($user_token)) {
return response()->json([
'message' => 'Unauthorized'
], 401);
}
$userId = self::extractUserIdFromToken($user_token);

$counts = DB::table('like')->select('postId', DB::raw('count(*) as likeCount'))->whereIn('postId', $postIds)->groupBy('postId')->get();
$userLiked = DB::table('like')->select('postId')->where('userId', '=', $userId)->whereIn('postId', $postIds)->get();

return response()->json([
'likeCounts' => $counts,
'likedPosts' => $userLiked
], 200);
}

/*
* Function removeLike is vulnerable to SQL-Injection-Attacks.
* When "postId" is an array, all contents of the array are added to the SQL-Bindings of the subsequent query.
15 changes: 11 additions & 4 deletions src/like-service/composer.json
Original file line number Diff line number Diff line change
@@ -8,12 +8,16 @@
],
"license": "MIT",
"require": {
"php": "^7.3",
"php": "^8.0",
"fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "7.4.3",
"laravel/framework": "8.2",
"laravel/tinker": "^2.0"
"laravel/framework": "8.22.0",
"laravel/tinker": "^2.0",
"open-telemetry/exporter-otlp": "^1.0.0RC1",
"open-telemetry/opentelemetry-auto-laravel": "^0.0.12",
"open-telemetry/sdk": "^1.0.0RC1",
"php-http/guzzle7-adapter": "^1.0"
},
"require-dev": {
"facade/ignition": "^2.3.6",
@@ -25,7 +29,10 @@
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"php-http/discovery": true
}
},
"extra": {
"laravel": {
1,833 changes: 1,175 additions & 658 deletions src/like-service/composer.lock

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions src/like-service/public/index.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
<?php

use App\Http\Controllers\JaegerPropagator;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Instrumentation\Configurator;
use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Util\ShutdownHandler;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\Sampler\ParentBased;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SemConv\ResourceAttributes;

define('LARAVEL_START', microtime(true));

@@ -46,6 +60,36 @@

$app = require_once __DIR__.'/../bootstrap/app.php';

if (strtolower(getenv('JAEGER_DISABLED', false)) === "false") {
Globals::registerInitializer(function (Configurator $configurator) {
$propagator = JaegerPropagator::getInstance();
$transport = (new OtlpHttpTransportFactory())->create('http://' . getenv('JAEGER_AGENT_HOST', false) . ':' . getenv('JAEGER_PORT', false) . '/v1/traces', ContentTypes::JSON);
$exporter = new SpanExporter($transport);

$resource = ResourceInfo::create(Attributes::create([
ResourceAttributes::SERVICE_NAMESPACE => 'unguard',
ResourceAttributes::SERVICE_NAME => getenv('SERVICE_NAME', false),
ResourceAttributes::SERVICE_VERSION => '1.0',
ResourceAttributes::DEPLOYMENT_ENVIRONMENT => 'development',
]));

$tracerProvider = TracerProvider::builder()
->addSpanProcessor(
new SimpleSpanProcessor($exporter)
)
->setResource($resource)
->setSampler(new ParentBased(new AlwaysOnSampler()))
->build();

ShutdownHandler::register([$tracerProvider, 'shutdown']);

return $configurator
->withTracerProvider($tracerProvider)
->withPropagator($propagator);
});
}


$kernel = $app->make(Kernel::class);

$response = tap($kernel->handle(
4 changes: 4 additions & 0 deletions src/like-service/routes/web.php
Original file line number Diff line number Diff line change
@@ -18,6 +18,10 @@
include (__DIR__ . '/../app/Http/Controllers/LikeController.php');


Route::get('/like-service/like-count', function (Request $request){
return LikeController::getMultipleLikeCountsAndStates($request);
});

Route::get('/like-service/like-count/{postId}', function (Request $request, string $postId){
return LikeController::getLikeCountAndState($request, $postId);
});