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

Commit b0046a8

Browse files
mgreisenstishkin
andauthored
Tutorial on how to create a github workflow (#130)
Co-authored-by: Stas <stishkin@live.com>
1 parent 0c56fe4 commit b0046a8

File tree

7 files changed

+315
-0
lines changed

7 files changed

+315
-0
lines changed

docs/how-to-use-github-workflow.md

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
# How to fuzz using a github workflow
2+
3+
As a developer you want to incorporate continuous testing into your pipeline.
4+
Github workflows makes it easy to leverage RAFT to continually test your service API's during development.
5+
6+
Creating a github workflow is simple. The workflow can be triggered a number of ways including
7+
on `push` requests, or manually as we are doing in this tutorial.
8+
9+
This tutorial includes all the
10+
files (except for the raft CLI files) you will need to create a working workflow which fuzzes
11+
the [petstore3 example docker container](https://hub.docker.com/r/swaggerapi/petstore3).
12+
13+
To get started you will need to:
14+
1. Deploy RAFT if you have not yet done so. </br>
15+
See [How to deploy RAFT](how-to-deploy.md)
16+
17+
2. Copy the python sdk files into your repo. </br>
18+
You will need the following python sdk files from the RAFT repo. Put these into your repo so they can
19+
be referenced from the script invoked by the workflow.
20+
- cli/raft.py
21+
- cli/requirements.txt
22+
- cli/raft_sdk/raft_common.py
23+
- cli/raft_sdk/raft-deploy.py
24+
- cli/raft_sdk/raft_service.py </br>
25+
26+
3. Create a python script called `script/petstore3/run.py` using the code below.
27+
This script will execute the logic to deploy to raft the job definitions that
28+
will fuzz the API.
29+
In this example we are using RESTler to fuzz the container.</br>
30+
31+
```python
32+
# Copyright (c) Microsoft Corporation.
33+
# Licensed under the MIT License.
34+
35+
import pathlib
36+
import sys
37+
import os
38+
import json
39+
import argparse
40+
41+
cur_dir = os.path.dirname(os.path.abspath(__file__))
42+
cli_dir = os.path.join(cur_dir, '..', '..', 'cli')
43+
sys.path.append(cli_dir)
44+
import raft
45+
from raft_sdk.raft_service import RaftCLI, RaftJobConfig, RaftJobError, RaftDefinitions
46+
47+
48+
def run(cli, config, subs):
49+
# Create job configuration object
50+
job_config = RaftJobConfig(file_path=config, substitutions=subs)
51+
print(f'Running {config}')
52+
53+
# Submit a new job config and get new job ID
54+
job = cli.new_job(job_config)
55+
56+
# Wait for a job with ID to finish the run
57+
cli.poll(job['jobId'])
58+
return job['jobId']
59+
60+
if __name__ == "__main__":
61+
try:
62+
defaults = None
63+
64+
formatter = argparse.ArgumentDefaultsHelpFormatter
65+
parser = argparse.ArgumentParser(description='petstore3', formatter_class=formatter)
66+
raft.add_defaults_and_secret_args(parser)
67+
parser.add_argument('--buildId', required=True)
68+
args = parser.parse_args()
69+
70+
if args.defaults_context_json:
71+
print(f"Loading defaults from command line: {args.defaults_context_json}")
72+
defaults = json.loads(args.defaults_context_json)
73+
else:
74+
with open(args.defaults_context_path, 'r') as defaults_json:
75+
defaults = json.load(defaults_json)
76+
77+
print(f"BUILD ID : {args.buildId}")
78+
79+
if args.secret:
80+
print(f"Secret given on command line")
81+
defaults['secret'] = args.secret
82+
83+
# instantiate RAFT CLI
84+
cli = RaftCLI(defaults)
85+
defs = RaftDefinitions(defaults)
86+
87+
compile_job_id = None
88+
# You can see how this substitution is used in the compile and fuzz json files
89+
subs = {
90+
"{ci-run}" : f"{args.buildId}",
91+
}
92+
93+
compile_job_id = run(cli, os.path.join(cur_dir, 'compile.json'), subs)
94+
subs['{compile.jobId}'] = compile_job_id
95+
96+
run(cli, os.path.join(cur_dir, "fuzz.json"), subs),
97+
98+
except RaftJobError as ex:
99+
print(f'ERROR: {ex.message}')
100+
sys.exit(1)
101+
```
102+
103+
4. Create a `script/petstore3/compile.json` job definition file.
104+
```JSON
105+
{
106+
"rootFileShare" : "{ci-run}",
107+
"namePrefix" : "petstore3-compile-",
108+
109+
"resources": {
110+
"Cores": 4,
111+
"MemoryGBs": 8
112+
},
113+
114+
"testTargets" : {
115+
"resources" : {
116+
"Cores" : 2,
117+
"MemoryGBs" : 4
118+
},
119+
"services" : [
120+
{
121+
"Container" : "swaggerapi/petstore3",
122+
"Ports" : [8082],
123+
"ExpectedDurationUntilReady" : "00:00:30",
124+
"Shell" : "/bin/sh",
125+
"Run" : {
126+
"ShellArguments" : ["-c", "java -jar /swagger-petstore/jetty-runner.jar --log /var/log/yyyy_mm_dd-requests.log --port 8082 /swagger-petstore/server.war"]
127+
},
128+
"PostRun" : {
129+
"ShellArguments" : ["-c", "cp /var/log/*-requests.log $RAFT_WORK_DIRECTORY"],
130+
"ExpectedRunDuration" : "00:00:10"
131+
},
132+
"OutputFolder" : "petstore3"
133+
}
134+
]
135+
},
136+
"testTasks" : {
137+
"targetConfiguration" : {
138+
"apiSpecifications": ["http://localhost:8082/api/v3/openapi.json"],
139+
"endpoint" : "http://localhost:8082"
140+
},
141+
"tasks": [
142+
{
143+
"toolName": "RESTler",
144+
"outputFolder": "compile",
145+
"toolConfiguration": {
146+
"task": "Compile"
147+
}
148+
},
149+
{
150+
"toolName": "Dredd",
151+
"outputFolder": "dredd"
152+
}
153+
]
154+
}
155+
}
156+
```
157+
158+
5. Create a `script/petstore3/fuzz.json` job definition file.
159+
```JSON
160+
{
161+
"rootFileShare" : "{ci-run}",
162+
"namePrefix" : "petstore3-test-fuzz-lean-",
163+
164+
"resources": {
165+
"Cores": 4,
166+
"MemoryGBs": 8
167+
},
168+
169+
"webhook":{
170+
"name" : "petstore3-fuzz",
171+
"metadata" : {
172+
"buildId" : "{ci-run}"
173+
}
174+
},
175+
176+
"readonlyFileShareMounts": [
177+
{
178+
"FileShareName": "{ci-run}",
179+
"MountPath": "/ci-run"
180+
}
181+
],
182+
183+
"testTargets" : {
184+
"resources" : {
185+
"Cores" : 2,
186+
"MemoryGBs" : 4
187+
},
188+
"services" : [
189+
{
190+
"Container" : "swaggerapi/petstore3",
191+
"Ports" : [8081],
192+
"ExpectedDurationUntilReady" : "00:02:00",
193+
"OutputFolder" : "petstore3-fuzz",
194+
"Shell" : "/bin/sh",
195+
"Run" : {
196+
"ShellArguments" : ["-c", "java -jar /swagger-petstore/jetty-runner.jar --log $RAFT_WORK_DIRECTORY/yyyy_mm_dd-requests.log --port 8081 /swagger-petstore/server.war"]
197+
}
198+
},
199+
{
200+
"Container" : "swaggerapi/petstore3",
201+
"Ports" : [8082],
202+
"OutputFolder" : "petstore3-zap",
203+
"ExpectedDurationUntilReady" : "00:02:00",
204+
"Shell" : "/bin/sh",
205+
"Run" : {
206+
"ShellArguments" : ["-c", "java -jar /swagger-petstore/jetty-runner.jar --log $RAFT_WORK_DIRECTORY/yyyy_mm_dd-requests.log --port 8082 /swagger-petstore/server.war"]
207+
}
208+
}
209+
]
210+
},
211+
"testTasks" : {
212+
"tasks": [
213+
{
214+
"toolName": "RESTler",
215+
"outputFolder": "fuzz",
216+
"duration": "00:10:00",
217+
"targetConfiguration" : {
218+
"endpoint": "http://localhost:8081"
219+
},
220+
"toolConfiguration": {
221+
"task": "Fuzz",
222+
"runConfiguration": {
223+
"useSsl" : false,
224+
"inputFolderPath": "/ci-run/{compile.jobId}/compile"
225+
}
226+
}
227+
},
228+
{
229+
"toolName": "ZAP",
230+
"outputFolder": "zap",
231+
"targetConfiguration" : {
232+
"apiSpecifications": [
233+
"http://localhost:8082/api/v3/openapi.json"
234+
],
235+
"endpoint" : "http://localhost:8082"
236+
}
237+
}
238+
]
239+
}
240+
}
241+
```
242+
6. Create a workflow file in your repo.</br>
243+
Copy the example workflow below into a file called `main.yml`, put this file into your `.github/workflows` folder.
244+
See the github documentation on [workflows](https://docs.github.com/en/actions/quickstart)
245+
for more details.
246+
247+
248+
```
249+
name: 'Fuzz Petstore3'
250+
251+
# Use the workflow_dispatch to manually run the workflow
252+
# Other options can be seen at https://docs.github.com/en/actions/reference/events-that-trigger-workflows
253+
on:
254+
workflow_dispatch:
255+
256+
jobs:
257+
fuzz_petstore3_job:
258+
runs-on: ubuntu-latest
259+
name: 'Fuzz the Petstore 3 docker container'
260+
steps:
261+
- name: Checkout code to Runner
262+
uses: actions/checkout@v2
263+
264+
- name: Setup Python
265+
uses: actions/setup-python@v2
266+
with:
267+
python-version: '3.6'
268+
269+
- run: pip3 install -r cli/requirements.txt
270+
- run: python script/petstore3/run.py --buildId $GITHUB_RUN_ID --defaults-context-json '${{ secrets.RAFT_DEFAULTS }}' --secret '${{ secrets.RAFT_SECRET }}'
271+
272+
```
273+
274+
You should now have a folder layout that looks like this, assuming your top level directory is `demo`:
275+
276+
```
277+
+---demo
278+
| +---.github
279+
| | \---workflows
280+
| | main.yml
281+
| +---cli
282+
| | | raft.py
283+
| | | requirements.txt
284+
| | \---raft_sdk
285+
| | | raft_common.py
286+
| | | raft_deploy.py
287+
| | | raft_service.py
288+
| \---script
289+
| \---petstore3
290+
| compile.json
291+
| fuzz.json
292+
| run.py
293+
```
294+
295+
7. Create two secrets in your repo.
296+
* RAFT_DEFAULTS</br>
297+
This is the contents of your `defaults.json` file you created when you deploy RAFT. Remove all the
298+
newline characters so that all the contents are on a single line. </br>
299+
* RAFT_SECRET</br>
300+
Using the Azure portal create a new secret for your RAFT service principle.
301+
- In the portal select "Azure Active Directory" icon</br>
302+
![](images/azureactivedirectoryicon.jpg)</br></br>
303+
- Select your "App registration" for your deployment. Use the filter option if needed to
304+
help find the registration. In this example our deployment name is "Demo"</br>
305+
![](images/AppRegistration.jpg)</br></br>
306+
- Click "Certificates & Secrets" and "New client secret"</br>
307+
![](images/CertificatesAndSecrets.jpg)</br></br>
308+
- Then "New client Secret"</br>
309+
![](images/NewClientSecret.jpg)
310+
311+
The secret you create here will be the contents of RAFT_SECRET.
312+
313+
8. Run the workflow</br>
314+
![](images/RunWorkflow.jpg)

docs/images/AppRegistration.jpg

22.2 KB
Loading
3.75 KB
Loading
2.32 KB
Loading

docs/images/NewClientSecret.jpg

1.79 KB
Loading

docs/images/RunWorkflow.jpg

24.1 KB
Loading

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* How to configure [Webhooks](how-to-configure-webhooks.md)
88
* How to [submit a job](how-to-submit-a-job.md)
99
* How to [handle webhooks with a Logic App](logicApps/github-email.md)
10+
* How to [fuzz using a github workfow](how-to-use-github-workflow.md)
1011
* Using the RAFT [command line](cli-reference.md)
1112
* REST API
1213
* [Swagger](sdk/swagger.md)

0 commit comments

Comments
 (0)