Skip to content
This repository was archived by the owner on Feb 2, 2022. It is now read-only.

Commit ec72ee3

Browse files
stishkinstasmgreisen
authored
V2 (#106)
* Support for listening on multiple ports in test targets (#58) Co-authored-by: stas <statis@microsoft.com> * Support for a list of swagger specifications (#62) Co-authored-by: stas <statis@microsoft.com> * SwaggerLocations renamed to ApiSpecifications and unify URL and FilePath (#90) Co-authored-by: stas <statis@microsoft.com> * Use "shell" only to run commands (#92) Co-authored-by: stas <statis@microsoft.com> * Make resource names unique #88 (#95) Co-authored-by: Stas <stishkin@live.com> * Refactor schema to include TestTargetConfiguration as common configuration for test tasks (#94) - TestTargetConfiguration includes ApiSepcifications list, IP, port and host (all are optional) - If task sets it's own TestTargetConfiguration, then it overrides the global one Co-authored-by: stas <statis@microsoft.com> * Expose more of RESTler configuration settings as part of Run Configuration (#98) Co-authored-by: stas <statis@microsoft.com> * Schema renames (#99) * Rename: testTargetConfiguration -> targetConfiguration * Rename: targets -> services * Script to run all samples Co-authored-by: stas <statis@microsoft.com> * "dredd" integration with RAFT (#101) Co-authored-by: stas <statis@microsoft.com> * use endpoint for target under test configuration (#102) Co-authored-by: stas <statis@microsoft.com> * - dedicated results storage (#103) - all of utils functionality (status tables, azure function blob storage) move to utils storage Co-authored-by: stas <statis@microsoft.com> * Refactor RAFT specific code into separate libraries, thus simplifying onboarding of tools written in node-js or Python (#104) Co-authored-by: stas <statis@microsoft.com> * Align docs with V2 changes (#105) Co-authored-by: stas <statis@microsoft.com> Co-authored-by: stas <statis@microsoft.com> Co-authored-by: Marc <marc@greisen.org>
1 parent 0f9e0c0 commit ec72ee3

File tree

117 files changed

+2822
-1852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+2822
-1852
lines changed

Scripts/CustomTools/tools/RESTler/config.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
{
2-
"container" : "{PrivateRegistryRestler}/restapifuzztesting/restler-agent:v1.latest",
2+
"container" : "{PrivateRegistryRestler}/restapifuzztesting/restler-agent:v2.latest",
3+
"shell" : "/bin/sh",
34
"run" : {
4-
"command" : "/bin/sh",
5-
"arguments" : ["-c",
5+
"shellArguments" : ["-c",
66
"sleep $RAFT_STARTUP_DELAY; dotnet /raft/agent/RestlerAgent.dll --agent-name ${RAFT_CONTAINER_NAME} --job-id ${RAFT_JOB_ID} --task-config-path ${RAFT_WORK_DIRECTORY}/task-config.json --work-directory ${RAFT_WORK_DIRECTORY} --restler-path /RESTler --app-insights-instrumentation-key ${RAFT_APP_INSIGHTS_KEY} --output-sas \"${RAFT_SB_OUT_SAS}\""
77
]
88
},
99
"idle" : {
10-
"command" : "/bin/sh",
11-
"arguments" : ["-c", "echo DebugMode; while true; do sleep 100000; done;"]
10+
"shellArguments" : ["-c", "echo DebugMode; while true; do sleep 100000; done;"]
1211
},
1312
"environmentVariables" : {
1413
"RESTLER_TELEMETRY_OPTOUT" : "0"

Scripts/Tests/bvt-petstore3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ def bvt(cli, definitions, subs):
8989
raise Exception('Expected job to be in completed state when retrieved job list.'
9090
f'{after_compile_pre_fuzz}')
9191

92-
if n != 2:
93-
raise Exception('Expected 2 after compile job step'
92+
if n != 3:
93+
raise Exception('Expected 3 after compile job step'
9494
f' for job {compile_job["jobId"]}'
9595
f' got {n}'
9696
f' {after_compile_pre_fuzz}')

Scripts/Tests/run-all-samples.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
import pathlib
5+
import sys
6+
import os
7+
import json
8+
import urllib.parse
9+
import time
10+
11+
cur_dir = os.path.dirname(os.path.abspath(__file__))
12+
cli_dir = os.path.join(cur_dir, '..', '..', 'cli')
13+
sys.path.append(cli_dir)
14+
from raft_sdk.raft_service import RaftCLI, RaftJobConfig
15+
16+
def find_files():
17+
configs = {}
18+
fs = ['dredd', 'zap', 'compile', 'test', 'fuzz']
19+
for root, _, files in os.walk(cli_dir):
20+
for f in fs:
21+
j = f + '.json'
22+
if j in files:
23+
if not configs.get(root):
24+
configs[root] = {}
25+
configs[root][f] = os.path.join(root, j)
26+
27+
y = f + '.yaml'
28+
if y in files:
29+
if not configs.get(root):
30+
configs[root] = {}
31+
configs[root][f] = os.path.join(root, y)
32+
33+
return configs
34+
35+
def wait(configs, count, task_name, job_id_key):
36+
completed_count = 0
37+
while completed_count < count:
38+
for c in configs:
39+
if configs[c].get(task_name):
40+
status = cli.job_status(configs[c][job_id_key])
41+
completed, _ = cli.is_completed(status)
42+
if completed:
43+
completed_count += 1
44+
cli.print_status(status)
45+
for _ in range(1,9) :
46+
sys.stdout.write('.')
47+
sys.stdout.flush()
48+
time.sleep(1)
49+
print('.')
50+
51+
52+
def compile_and_dredd(cli, configs):
53+
compile_count = 0
54+
dredd_count = 0
55+
for c in configs:
56+
if configs[c].get('compile'):
57+
compile_job_config = RaftJobConfig(file_path=configs[c]['compile'], substitutions=subs)
58+
compile_job = cli.new_job(compile_job_config)
59+
configs[c]['compile_job_id'] = compile_job['jobId']
60+
compile_count = compile_count + 1
61+
62+
if configs[c].get('dredd'):
63+
dredd_job_config = RaftJobConfig(file_path=configs[c]['dredd'], substitutions=subs)
64+
dredd_job = cli.new_job(dredd_job_config)
65+
configs[c]['dredd_job_id'] = dredd_job['jobId']
66+
dredd_count = dredd_count + 1
67+
68+
print('Compiling all ' + str(compile_count) + ' and running Dredd on ' + str(dredd_count) + ' samples ...')
69+
wait(configs, compile_count, 'compile', 'compile_job_id')
70+
wait(configs, dredd_count, 'dredd', 'dredd_job_id')
71+
72+
73+
def test(cli, configs):
74+
test_count = 0
75+
for c in configs:
76+
if configs[c].get('test'):
77+
subs['{compile.jobId}'] = configs[c]['compile_job_id']
78+
test_job_config = RaftJobConfig(file_path=configs[c]['test'], substitutions=subs)
79+
test_job = cli.new_job(test_job_config)
80+
configs[c]['test_job_id'] = test_job['jobId']
81+
test_count = test_count + 1
82+
print('Testing all ' + str(test_count) + ' samples ...')
83+
wait(configs, test_count, 'test', 'test_job_id')
84+
85+
86+
def fuzz_and_zap(cli, configs):
87+
fuzz_count = 0
88+
zap_count = 0
89+
for c in configs:
90+
if configs[c].get('fuzz'):
91+
subs['{compile.jobId}'] = configs[c]['compile_job_id']
92+
fuzz_job_config = RaftJobConfig(file_path=configs[c]['fuzz'], substitutions=subs)
93+
fuzz_job = cli.new_job(fuzz_job_config)
94+
configs[c]['fuzz_job_id'] = fuzz_job['jobId']
95+
fuzz_count = fuzz_count + 1
96+
97+
if configs[c].get('zap'):
98+
zap_job_config = RaftJobConfig(file_path=configs[c]['zap'], substitutions=subs)
99+
zap_job = cli.new_job(zap_job_config)
100+
configs[c]['zap_job_id'] = zap_job['jobId']
101+
zap_count = zap_count + 1
102+
103+
print('Fuzzing all ' + str(fuzz_count) + ' and ZAP: ' + str(zap_count) + ' samples ...')
104+
wait(configs, fuzz_count, 'fuzz', 'fuzz_job_id')
105+
wait(configs, zap_count, 'zap', 'zap_job_id')
106+
107+
if __name__ == "__main__":
108+
if len(sys.argv) == 1:
109+
print('Please provide a host on command line that does not require authentication. Something like: test-host.com')
110+
111+
sample_host = sys.argv[1]
112+
113+
cli = RaftCLI()
114+
subs = {
115+
'{sample.host}' : sample_host,
116+
'{defaults.deploymentName}' : cli.definitions.deployment,
117+
'{ci-run}': 'all-samples'
118+
}
119+
configs = find_files()
120+
compile_and_dredd(cli, configs)
121+
test(cli, configs)
122+
fuzz_and_zap(cli, configs)

ado/stages/deploy/steps/upload-tools.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ steps:
1313
path: cli/raft-tools/auth/dotnet-core-3.1
1414

1515
- task: AzureCLI@2
16+
continueOnError: false
1617
displayName: "Upload service utilities"
1718
inputs:
1819
azureSubscription: $(raft-subscription)

ado/variables/version-variables.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
variables:
22
- name: version.major
3-
value: 1
3+
value: 2
44
- name: version.minor
5-
value: 3
5+
value: 0
66
- name: version.revision
77
value: 0
88
- name: imageTag
9-
value: 'v1.3.0'
9+
value: 'v2.0.0'
1010
- name: imageTagLatest
11-
value: 'v1.latest'
11+
value: 'v2.latest'
1212
- name: devRevision
1313
value: 0
1414
- name: imageTagWithBuildDate
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
var msal = require('@azure/msal-node');
3+
4+
function get_token(client_id, tenant_id, secret, scopes, authority_uri, callback) {
5+
let authority;
6+
if (authority_uri) {
7+
authority = authority_uri + '/' + tenant_id;
8+
}
9+
else {
10+
authority = "https://login.microsoftonline.com/" + tenant_id;
11+
}
12+
13+
if (!scopes) {
14+
scopes = [client_id + "/.default"];
15+
}
16+
17+
const msalConfig = {
18+
auth: {
19+
clientId: client_id,
20+
authority : authority,
21+
clientSecret:secret
22+
} ,
23+
system: {
24+
loggerOptions: {
25+
loggerCallback(loglevel, message, containsPii) {
26+
console.log(message);
27+
},
28+
piiLoggingEnabled: false,
29+
//logLevel: msal.LogLevel.Verbose,
30+
logLevel: msal.LogLevel.Warning
31+
}
32+
}
33+
};
34+
35+
const accessTokenRequest = {
36+
scopes: scopes
37+
}
38+
const msalInstance = new msal.ConfidentialClientApplication(msalConfig);
39+
40+
msalInstance.acquireTokenByClientCredential(accessTokenRequest).then(function(accessTokenResponse) {
41+
callback(null, accessTokenResponse.tokenType + ' ' + accessTokenResponse.accessToken);
42+
}).catch(function (error) {
43+
log.error(error);
44+
callback(error);
45+
})
46+
}
47+
exports.tokenFromEnvVariable = function (env_variable_name, callback) {
48+
let auth = JSON.parse(process.env["RAFT_" + env_variable_name] || process.env[env_variable_name]);
49+
if (auth) {
50+
console.log("Getting MSAL token");
51+
get_token(auth['client'], auth['tenant'], auth['secret'], auth['scopes'], auth['authorityUri'], callback);
52+
}
53+
else {
54+
callback(new Error("Authentication parameters are not set in environment variable " + env_variable_name));
55+
}
56+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name" : "@nsv/raft-msal",
3+
"version" : "1.0.0",
4+
"dependencies" : {
5+
"@azure/msal-node": "^1.0.0-alpha.9"
6+
}
7+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const url = require('url');
5+
const { exec } = require('child_process');
6+
7+
const appInsights = require('applicationinsights');
8+
const serviceBus = require('@azure/service-bus');
9+
10+
const toolDirectory = process.env.RAFT_TOOL_RUN_DIRECTORY;
11+
const workDirectory = process.env.RAFT_WORK_DIRECTORY;
12+
13+
const rawdata = fs.readFileSync(workDirectory + '/task-config.json');
14+
const config = JSON.parse(rawdata);
15+
16+
class RaftUtils {
17+
constructor(toolName) {
18+
this.telemetryClient = new appInsights.TelemetryClient(process.env['RAFT_APP_INSIGHTS_KEY']);
19+
this.serviceBus = new serviceBus.ServiceBusClient(process.env.RAFT_SB_OUT_SAS);
20+
this.jobId = process.env['RAFT_JOB_ID'];
21+
this.agentName = process.env['RAFT_CONTAINER_NAME'];
22+
this.traceProperties =
23+
{
24+
jobId : this.jobId,
25+
taskIndex : process.env['RAFT_TAKS_INDEX'],
26+
containerName : this.agentName
27+
};
28+
this.sbSender = this.serviceBus.createSender(this.serviceBus._connectionContext.config.entityPath);
29+
this.toolName = toolName;
30+
}
31+
32+
reportBug(bugDetails) {
33+
console.log('Sending bug found event: ' + bugDetails.name);
34+
this.sbSender.sendMessages(
35+
{
36+
body: {
37+
eventType : 'BugFound',
38+
message : {
39+
tool : this.toolName,
40+
jobId : this.jobId,
41+
agentName : this.agentName,
42+
bugDetails : bugDetails
43+
}
44+
},
45+
sessionId : this.jobId
46+
}
47+
);
48+
}
49+
50+
reportStatus(state, details) {
51+
console.log('Sending job status event: ' + state);
52+
return this.sbSender.sendMessages(
53+
{
54+
body: {
55+
eventType: 'JobStatus',
56+
message : {
57+
tool: this.toolName,
58+
jobId : this.jobId,
59+
agentName : this.agentName,
60+
details : details,
61+
utcEventTime : (new Date()).toUTCString(),
62+
state : state
63+
}
64+
},
65+
sessionId : this.jobId
66+
});
67+
}
68+
69+
reportStatusCreated(details){
70+
this.reportStatus('Created', details);
71+
}
72+
73+
reportStatusRunning(details){
74+
this.reportStatus('Running', details);
75+
}
76+
77+
reportStatusCompleted(details){
78+
return this.reportStatus('Completed', details);
79+
}
80+
81+
reportStatusError(details){
82+
return this.reportStatus('Error', details);
83+
}
84+
85+
logTrace(traceMessage) {
86+
this.telemetryClient.trackTrace({message: traceMessage, properties: this.traceProperties});
87+
}
88+
89+
logException(exception) {
90+
this.telemetryClient.trackException({exception: exception, properties: this.traceProperties});
91+
}
92+
93+
flush(){
94+
this.telemetryClient.flush();
95+
this.serviceBus.close();
96+
}
97+
}
98+
function getAuthHeader(callback) {
99+
if (!config.authenticationMethod) {
100+
callback(null, null);
101+
} else {
102+
const authMethods = Object.keys(config.authenticationMethod)
103+
if (authMethods.length == 0) {
104+
callback(null, null);
105+
} else if (authMethods.length > 1) {
106+
callback(new Error("More than one authentication method is specified: " + config.authenticationMethod), null);
107+
} else {
108+
const authMethod = authMethods[0];
109+
switch (authMethod.toLowerCase()) {
110+
case 'msal':
111+
const msalDirectory = toolDirectory + "/../../auth/node-js/msal";
112+
exec("npm install " + msalDirectory, (error, _) => {
113+
if (error) {
114+
callback(error);
115+
} else {
116+
const raftMsal = require(msalDirectory + '/msal_token.js')
117+
raftMsal.tokenFromEnvVariable(config.authenticationMethod[authMethod], (error, result) => {
118+
callback(error, result);
119+
}
120+
);
121+
}
122+
}
123+
);
124+
break;
125+
case 'txttoken':
126+
callback(null, process.env['RAFT_' + config.authenticationmethod.txttoken] || process.env[config.authenticationmethod.txttoken] );
127+
break;
128+
case 'commandline':
129+
exec(config.authenticationmethod.commandline, (error, result) => {
130+
callback(erorr, result);
131+
}
132+
);
133+
break;
134+
default:
135+
callback(new Error("Unhandled authentication method: " + config.authenticationMethod), null);
136+
break;
137+
}
138+
}
139+
}
140+
}
141+
142+
exports.workDirectory = workDirectory;
143+
exports.config = config;
144+
exports.RaftUtils = RaftUtils;
145+
exports.getAuthHeader = getAuthHeader;

0 commit comments

Comments
 (0)