Skip to content

Commit

Permalink
feat: basic authn benchmark (#416)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcosentino11 authored Jan 11, 2024
1 parent 7da4fe3 commit 277ceee
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 77 deletions.
66 changes: 66 additions & 0 deletions .github/scripts/benchmark-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

readJson = (file) => {
const fs = require('fs');
return JSON.parse(fs.readFileSync(file, 'utf8'));
};

jmhResultsAsMarkdownTable = (results) => {
const header = '| Benchmark | Score |\n| --- | --- |';
const rows = results.map(benchmarkInfo => {
const benchmarkName = benchmarkInfo.benchmark;
const score = benchmarkInfo.primaryMetric.score.toFixed(2);
const scoreUnit = benchmarkInfo.primaryMetric.scoreUnit;
return `| ${benchmarkName} | ${score} ${scoreUnit} |`;
});
return `${header}\n${rows.join('\n')}`;
};

replaceComment = async (github, context, prefix, body) => {
console.log(`calling listComments(${context.issue.number}, ${context.repo.owner}, ${context.repo.repo})`)
let listResp = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(`listComments resp: ${JSON.stringify(listResp)}`);

const comment = listResp.data.find(comment => comment.body?.startsWith(prefix));
if (comment) {
console.log(`calling updateComment(${context.repo.owner}, ${context.repo.repo}, ${comment.id}, ${body})`);
let updateResp = await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
body: body,
});
console.log(`updateComment resp: ${JSON.stringify(updateResp)}`);
return;
} else {
console.log(`no existing comment found with prefix: ${prefix}, creating new one...`)
}

console.log(`calling createComment(${context.issue.number}, ${context.repo.owner}, ${context.repo.repo}, ${body})}`);
let createResp = await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body,
});
console.log(`createComment resp: ${JSON.stringify(createResp)}`);
};

module.exports = async ({github, context, core}) => {
const commentTitle = 'Benchmark Results';
const commentHeader = `${commentTitle}\n---\n\n`;

const {RESULTS_FILE} = process.env;
const results = readJson(RESULTS_FILE);

const resultsTable = jmhResultsAsMarkdownTable(results);
const body = `${commentHeader}${resultsTable}`;
await replaceComment(github, context, commentTitle, body);
};
36 changes: 36 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Benchmarks

on:
push:
branches:
- main
pull_request:
branches: '*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Run Benchmarks
run: ./run-benchmarks.sh
working-directory: ./benchmark
- name: Upload Results
uses: actions/[email protected]
with:
name: Benchmark Results
path: ./benchmark/jmh-result.json
- name: Comment With Results
uses: actions/github-script@v7
env:
RESULTS_FILE: ./benchmark/jmh-result.json
with:
script: |
const postBenchmarkComment = require('./.github/scripts/benchmark-comment.js');
await postBenchmarkComment({github, context, core});
1 change: 1 addition & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jmh-result.json
11 changes: 11 additions & 0 deletions benchmark/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.aws.greengrass</groupId>
<artifactId>nucleus</artifactId>
<version>2.6.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.aws.greengrass</groupId>
<artifactId>client-devices-auth</artifactId>
Expand Down
6 changes: 3 additions & 3 deletions benchmark/run-benchmarks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ set -e

# install CDA locally
cd ./..
mvn clean install -DskipTests
mvn -B -ntp clean install -DskipTests
cd -

# build benchmark project
mvn clean package
mvn -B -ntp clean package

# run all benchmarks
java -jar target/benchmarks.jar
java -jar target/benchmarks.jar -rf json
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.aws.greengrass.clientdevices.auth.benchmark;

import com.aws.greengrass.clientdevices.auth.AuthorizationRequest;
import com.aws.greengrass.clientdevices.auth.DeviceAuthClient;
import com.aws.greengrass.clientdevices.auth.configuration.AuthorizationPolicyStatement;
import com.aws.greengrass.clientdevices.auth.configuration.GroupConfiguration;
import com.aws.greengrass.clientdevices.auth.configuration.GroupDefinition;
import com.aws.greengrass.clientdevices.auth.configuration.GroupManager;
import com.aws.greengrass.clientdevices.auth.configuration.parser.ParseException;
import com.aws.greengrass.clientdevices.auth.exception.AuthorizationException;
import com.aws.greengrass.clientdevices.auth.session.Session;
import com.aws.greengrass.clientdevices.auth.session.SessionManager;
import com.aws.greengrass.clientdevices.auth.session.attribute.AttributeProvider;
import com.aws.greengrass.clientdevices.auth.session.attribute.DeviceAttribute;
import com.aws.greengrass.clientdevices.auth.session.attribute.StringLiteralAttribute;
import com.aws.greengrass.clientdevices.auth.session.attribute.WildcardSuffixAttribute;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
public class AuthorizationBenchmarks {

@State(Scope.Thread)
public static class SimpleAuthRequest extends PolicyTestState {

final AuthorizationRequest basicRequest = AuthorizationRequest.builder()
.operation("mqtt:publish")
.resource("mqtt:topic:humidity")
.sessionId("sessionId")
.build();

@Setup
public void doSetup() throws ParseException, AuthorizationException {
sessionManager.registerSession("sessionId", FakeSession.forDevice("MyThingName"));
groupManager.setGroupConfiguration(GroupConfiguration.builder()
.definitions(Collections.singletonMap(
"group1", GroupDefinition.builder()
.selectionRule("thingName: " + "MyThingName")
.policyName("policy1")
.build()))
.policies(Collections.singletonMap(
"policy1", Collections.singletonMap(
"Statement1", AuthorizationPolicyStatement.builder()
.statementDescription("Policy description")
.effect(AuthorizationPolicyStatement.Effect.ALLOW)
.resources(new HashSet<>(Collections.singleton("mqtt:topic:humidity")))
.operations(new HashSet<>(Collections.singleton("mqtt:publish")))
.build())))
.build());
}
}

@Benchmark
public boolean GIVEN_single_group_permission_WHEN_simple_auth_request_THEN_successful_auth(SimpleAuthRequest state) throws Exception {
return state.deviceAuthClient.canDevicePerform(state.basicRequest);
}

static abstract class PolicyTestState {
final FakeSessionManager sessionManager = new FakeSessionManager();
final GroupManager groupManager = new GroupManager();
final DeviceAuthClient deviceAuthClient = new DeviceAuthClient(sessionManager, groupManager, null);
}

static class FakeSession implements Session {
private final String thingName;
private final boolean isComponent;

static FakeSession forComponent() {
return new FakeSession(null, true);
}

static FakeSession forDevice(String thingName) {
return new FakeSession(thingName, false);
}

private FakeSession(String thingName, boolean isComponent) {
this.thingName = thingName;
this.isComponent = isComponent;
}

@Override
public AttributeProvider getAttributeProvider(String attributeProviderNameSpace) {
throw new UnsupportedOperationException();
}

@Override
public DeviceAttribute getSessionAttribute(String ns, String name) {
if ("Component".equalsIgnoreCase(ns) && name.equalsIgnoreCase("component")) {
return isComponent ? new StringLiteralAttribute("component") : null;
}
if ("Thing".equalsIgnoreCase(ns) && name.equalsIgnoreCase("thingName")) {
return new WildcardSuffixAttribute(thingName);
}
throw new UnsupportedOperationException(String.format("Attribute %s.%s not supported", ns, name));
}
}

private static class FakeSessionManager extends SessionManager {
private final Map<String, Session> sessions = new HashMap<>();

public FakeSessionManager() {
super(null);
}

void registerSession(String id, Session session) {
sessions.put(id, session);
}

@Override
public Session findSession(String id) {
return sessions.get(id);
}
}
}

This file was deleted.

0 comments on commit 277ceee

Please sign in to comment.