Skip to content

Commit 93becdd

Browse files
Merge pull request #1230 from kendallroden/python-jobs-sdk-quickstart
init jobs quickstart for python sdk
2 parents 6d7cb06 + 3591aee commit 93becdd

File tree

9 files changed

+613
-55
lines changed

9 files changed

+613
-55
lines changed

jobs/python/http/README.md

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ In this quickstart, you'll schedule, get, and delete a job using Dapr's Job API.
44

55
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API.
66

7-
87
This quickstart includes two apps:
98

109
- `job-scheduler/app.py`, responsible for scheduling, retrieving and deleting jobs.
@@ -21,31 +20,42 @@ This quickstart includes two apps:
2120
- `JOB_SERVICE_DAPR_HTTP_PORT`: The Dapr HTTP port of the job-service (default: 6280)
2221
- `DAPR_HOST`: The Dapr host address (default: http://localhost)
2322

24-
## Install dependencies
23+
## Run all apps with multi-app run template file
24+
25+
This section shows how to run both applications at once using [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. This enables you to test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process.
26+
27+
1. Build the apps:
2528

2629
<!-- STEP
2730
name: Install python dependencies
2831
-->
32+
2933
```bash
3034
pip3 install -r requirements.txt
3135
```
32-
<!-- END_STEP -->
33-
34-
## Run all apps with multi-app run template file
3536

36-
This section shows how to run both applications at once using [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. This enables you to test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process.
37+
<!-- END_STEP -->
3738

38-
Open a new terminal window and run the multi app run template:
39+
2. Run the multi app run template:
3940

4041
<!-- STEP
4142
name: Run multi app run template
4243
expected_stdout_lines:
44+
- '== APP - job-scheduler == Sending request to schedule job: R2-D2'
45+
- '== APP - job-scheduler == Job scheduled: R2-D2'
46+
- '== APP - job-scheduler == Sending request to retrieve job: R2-D2'
47+
- '== APP - job-scheduler == Job details for R2-D2: {"name":"R2-D2", "dueTime":"15s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"@type":"type.googleapis.com/google.protobuf.StringValue", "value":"R2-D2:Oil Change"}}, "failurePolicy":{"constant":{"interval":"1s", "maxRetries":3}}}'
48+
- '== APP - job-scheduler == Sending request to schedule job: C-3PO'
49+
- '== APP - job-scheduler == Job scheduled: C-3PO'
4350
- '== APP - job-service == Received job request...'
51+
- '== APP - job-service == Starting droid: R2-D2'
4452
- '== APP - job-service == Executing maintenance job: Oil Change'
45-
- '== APP - job-scheduler == Job Scheduled: C-3PO'
53+
- '== APP - job-scheduler == Sending request to retrieve job: C-3PO'
54+
- '== APP - job-scheduler == Job details for C-3PO: {"name":"C-3PO", "dueTime":"20s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"@type":"type.googleapis.com/google.protobuf.StringValue", "value":"C-3PO:Limb Calibration"}}, "failurePolicy":{"constant":{"interval":"1s", "maxRetries":3}}}'
4655
- '== APP - job-service == Received job request...'
56+
- '== APP - job-service == Starting droid: C-3PO'
4757
- '== APP - job-service == Executing maintenance job: Limb Calibration'
48-
expected_stderr_lines:
58+
expected_stderr_lines: []
4959
output_match_mode: substring
5060
match_order: none
5161
background: true
@@ -60,33 +70,37 @@ dapr run -f .
6070
The terminal console output should look similar to this, where:
6171

6272
- The `R2-D2` job is being scheduled.
63-
- The `R2-D2` job is being executed after 2 seconds.
73+
- The `R2-D2` job is being retrieved.
6474
- The `C-3PO` job is being scheduled.
6575
- The `C-3PO` job is being retrieved.
76+
- The `R2-D2` job is being executed after 15 seconds.
77+
- The `C-3PO` job is being executed after 20 seconds.
6678

6779
```text
68-
== APP - job-scheduler == Job Scheduled: R2-D2
80+
== APP - job-scheduler == Sending request to schedule job: R2-D2
81+
== APP - job-scheduler == Job scheduled: R2-D2
82+
== APP - job-scheduler == Sending request to retrieve job: R2-D2
83+
== APP - job-scheduler == Job details for R2-D2: {"name":"R2-D2", "dueTime":"15s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"@type":"type.googleapis.com/google.protobuf.StringValue", "value":"R2-D2:Oil Change"}}, "failurePolicy":{"constant":{"interval":"1s", "maxRetries":3}}}
84+
== APP - job-scheduler == Sending request to schedule job: C-3PO
85+
== APP - job-scheduler == Job scheduled: C-3PO
6986
== APP - job-service == Received job request...
7087
== APP - job-service == Starting droid: R2-D2
7188
== APP - job-service == Executing maintenance job: Oil Change
72-
== APP - job-scheduler == Job Scheduled: C-3PO
73-
== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"30s", "data":{"@type":"ttype.googleapis.com/google.protobuf.StringValue", "expression":"C-3PO:Limb Calibration"}}
74-
```
75-
76-
After 30 seconds, the terminal output should present the `C-3PO` job being processed:
77-
78-
```text
89+
== APP - job-service == 127.0.0.1 - - "POST /job/R2-D2 HTTP/1.1" 200 -
90+
== APP - job-scheduler == Sending request to retrieve job: C-3PO
91+
== APP - job-scheduler == Job details for C-3PO: {"name":"C-3PO", "dueTime":"20s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"@type":"type.googleapis.com/google.protobuf.StringValue", "value":"C-3PO:Limb Calibration"}}, "failurePolicy":{"constant":{"interval":"1s", "maxRetries":3}}}
7992
== APP - job-service == Received job request...
8093
== APP - job-service == Starting droid: C-3PO
8194
== APP - job-service == Executing maintenance job: Limb Calibration
95+
== APP - job-service == 127.0.0.1 - - "POST /job/C-3PO HTTP/1.1" 200 -
8296
```
8397

8498
<!-- END_STEP -->
8599

86-
2. Stop and clean up application processes
100+
2. Stop and clean up application processes using a new terminal window.
87101

88102
<!-- STEP
89-
name: Stop multi-app run
103+
name: Stop multi-app run
90104
sleep: 5
91105
-->
92106

@@ -98,18 +112,20 @@ dapr stop -f .
98112

99113
## Run apps individually
100114

101-
### Start the job service
115+
### Schedule jobs
102116

103-
1. Open a terminal and run the `job-service` app:
117+
1. Open a terminal and run the `job-service` app. Build the dependencies if you haven't already.
104118

105119
```bash
106-
cd job-service
107-
dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- python app.py
120+
pip3 install -r requirements.txt
108121
```
109122

110-
### Schedule jobs
123+
```bash
124+
cd job-service
125+
dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 --dapr-grpc-port 6281 -- python app.py
126+
```
111127

112-
1. On a new terminal window, schedule the `R2-D2` Job using the Jobs API:
128+
2. In a new terminal window, schedule the `R2-D2` Job using the Jobs API:
113129

114130
```bash
115131
curl -X POST \
@@ -124,15 +140,15 @@ curl -X POST \
124140
}'
125141
```
126142

127-
Back at the `job-service` app terminal window, the output should be:
143+
In the `job-service` app terminal window, the output should be:
128144

129145
```text
130146
== APP - job-service == Received job request...
131147
== APP - job-service == Starting droid: R2-D2
132148
== APP - job-service == Executing maintenance job: Oil Change
133149
```
134150

135-
2. On the same terminal window, schedule the `C-3PO` Job using the Jobs API:
151+
3. On the same terminal window, schedule the `C-3PO` Job using the Jobs API:
136152

137153
```bash
138154
curl -X POST \
@@ -158,7 +174,7 @@ curl -X GET http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: appli
158174
You should see the following:
159175

160176
```text
161-
{"name":"C-3PO", "dueTime":"30s", "data":{"@type":"type.googleapis.com/google.protobuf.StringValue", "expression":"C-3PO:Limb Calibration"}}
177+
{"name":"c-3po", "dueTime":"30s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"@type":"type.googleapis.com/google.protobuf.StringValue", "value":"C-3PO:Limb Calibration"}}, "failurePolicy":{"constant":{"interval":"1s", "maxRetries":3}}}
162178
```
163179

164180
### Delete a scheduled job
@@ -178,5 +194,5 @@ curl -X GET http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: appli
178194
You should see an error message indicating that the job was not found:
179195

180196
```text
181-
{"errorCode":"ERR_JOBS_NOT_FOUND","message":"job not found: app||default||job-service||c-3po"}
182-
```
197+
{"errorCode":"DAPR_SCHEDULER_GET_JOB","message":"failed to get job due to: rpc error: code = NotFound desc = job not found: c-3po","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","domain":"dapr.io","metadata":{"appID":"job-service","namespace":"default"},"reason":"DAPR_SCHEDULER_GET_JOB"}]}
198+
```

jobs/python/http/job-scheduler/app.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,81 @@
55

66
C3PO_JOB_BODY = {
77
"data": {"@type": "type.googleapis.com/google.protobuf.StringValue", "value": "C-3PO:Limb Calibration"},
8-
"dueTime": "10s",
8+
"dueTime": "20s",
99
}
1010

1111
R2D2_JOB_BODY = {
1212
"data": {"@type": "type.googleapis.com/google.protobuf.StringValue", "value": "R2-D2:Oil Change"},
13-
"dueTime": "2s"
13+
"dueTime": "15s"
1414
}
1515

16+
dapr_host = os.getenv('DAPR_HOST', 'http://localhost')
17+
job_service_dapr_http_port = os.getenv('JOB_SERVICE_DAPR_HTTP_PORT', '6280')
18+
19+
1620
def schedule_job(host: str, port: str, job_name: str, job_body: dict) -> None:
1721
req_url = f"{host}:{port}/v1.0-alpha1/jobs/{job_name}"
18-
22+
23+
print(f"Sending request to schedule job: {job_name}", flush=True)
24+
1925
try:
2026
response = requests.post(
2127
req_url,
2228
json=job_body,
2329
headers={"Content-Type": "application/json"},
2430
timeout=15
2531
)
26-
2732
# Accept both 200 and 204 as success codes
2833
if response.status_code not in [200, 204]:
29-
raise Exception(f"Failed to schedule job. Status code: {response.status_code}, Response: {response.text}")
30-
31-
print(f"Job Scheduled: {job_name}")
34+
raise Exception(
35+
f"Failed to schedule job. Status code: {response.status_code}, Response: {response.text}")
36+
37+
print(f"Job scheduled: {job_name}", flush=True)
38+
3239
if response.text:
3340
print(f"Response: {response.text}")
34-
41+
3542
except requests.exceptions.RequestException as e:
36-
print(f"Error scheduling job {job_name}: {str(e)}")
43+
print(f"Error scheduling job {job_name}: {str(e)}", flush=True)
3744
raise
3845

46+
3947
def get_job_details(host: str, port: str, job_name: str) -> None:
4048
req_url = f"{host}:{port}/v1.0-alpha1/jobs/{job_name}"
41-
49+
50+
print(f"Sending request to retrieve job: {job_name}", flush=True)
51+
4252
try:
4353
response = requests.get(req_url, timeout=15)
4454
if response.status_code in [200, 204]:
45-
print(f"Job details for {job_name}: {response.text}")
55+
print(f"Job details for {job_name}: {response.text}", flush=True)
4656
else:
47-
print(f"Failed to get job details. Status code: {response.status_code}, Response: {response.text}")
48-
57+
print(
58+
f"Failed to get job details. Status code: {response.status_code}, Response: {response.text}")
59+
4960
except requests.exceptions.RequestException as e:
50-
print(f"Error getting job details for {job_name}: {str(e)}")
61+
print(
62+
f"Error getting job details for {job_name}: {str(e)}", flush=True)
5163
raise
5264

65+
5366
def main():
5467
# Wait for services to be ready
5568
time.sleep(5)
56-
57-
dapr_host = os.getenv('DAPR_HOST', 'http://localhost')
58-
job_service_dapr_http_port = os.getenv('JOB_SERVICE_DAPR_HTTP_PORT', '6280')
59-
6069
# Schedule R2-D2 job
6170
schedule_job(dapr_host, job_service_dapr_http_port, "R2-D2", R2D2_JOB_BODY)
6271
time.sleep(5)
63-
72+
73+
# Get R2-D2 job details
74+
get_job_details(dapr_host, job_service_dapr_http_port, "R2-D2")
75+
time.sleep(5)
6476
# Schedule C-3PO job
6577
schedule_job(dapr_host, job_service_dapr_http_port, "C-3PO", C3PO_JOB_BODY)
6678
time.sleep(5)
67-
6879
# Get C-3PO job details
6980
get_job_details(dapr_host, job_service_dapr_http_port, "C-3PO")
70-
time.sleep(5)
81+
time.sleep(30)
82+
7183

7284
if __name__ == "__main__":
73-
main()
85+
main()

jobs/python/http/job-service/app.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def _send_response(self, status_code: int, message: str = ""):
2525

2626
def do_POST(self):
2727
print('Received job request...', flush=True)
28-
2928
try:
3029
# Check if path starts with /job/
3130
if not self.path.startswith('/job/'):
@@ -51,7 +50,7 @@ def do_POST(self):
5150
self._send_response(200)
5251

5352
except Exception as e:
54-
print("Error processing job request:", flush= True)
53+
print("Error processing job request:", flush=True)
5554
print(traceback.format_exc())
5655
self._send_response(400, f"Error processing job: {str(e)}")
5756

0 commit comments

Comments
 (0)