Skip to content

Commit 002c195

Browse files
authoredNov 23, 2021
chore: add python/pydevd tests for python 3.9 (#100)
1 parent 4f99815 commit 002c195

File tree

7 files changed

+285
-0
lines changed

7 files changed

+285
-0
lines changed
 

‎python/skaffold.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ profiles:
4343

4444
# integration: set of `skaffold debug`-like integration tests
4545
- name: integration
46+
patches:
47+
- op: add
48+
path: /build/artifacts/-
49+
value:
50+
image: python39app
51+
context: test/pythonapp
52+
docker:
53+
buildArgs:
54+
PYTHONVERSION: "3.9"
55+
- op: add
56+
path: /build/artifacts/-
57+
value:
58+
image: pydevconnect
59+
context: test/pydevconnect
60+
deploy:
61+
kubectl:
62+
manifests:
63+
- test/k8s-test-pydevd-python39.yaml
4664

4765
# release: pushes images to production with :latest
4866
- name: release
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: python39pod
5+
labels:
6+
app: hello
7+
protocol: pydevd
8+
runtime: python39
9+
spec:
10+
containers:
11+
- name: python39app
12+
image: python39app
13+
command: ["/dbg/python/launcher", "--mode", "pydevd", "--port", "12345", "--"]
14+
args: ["python", "-m", "flask", "run", "--host=0.0.0.0"]
15+
ports:
16+
- containerPort: 5000
17+
- containerPort: 12345
18+
name: pydevd
19+
env:
20+
- name: WRAPPER_VERBOSE
21+
value: debug
22+
readinessProbe:
23+
httpGet:
24+
path: /
25+
port: 5000
26+
volumeMounts:
27+
- mountPath: /dbg
28+
name: python-debugging-support
29+
initContainers:
30+
- image: skaffold-debug-python
31+
name: install-python-support
32+
resources: {}
33+
volumeMounts:
34+
- mountPath: /dbg
35+
name: python-debugging-support
36+
volumes:
37+
- emptyDir: {}
38+
name: python-debugging-support
39+
40+
---
41+
apiVersion: v1
42+
kind: Service
43+
metadata:
44+
name: hello-pydevd-python39
45+
spec:
46+
ports:
47+
- name: http
48+
port: 5000
49+
protocol: TCP
50+
- name: pydevd
51+
port: 12345
52+
protocol: TCP
53+
selector:
54+
app: hello
55+
protocol: pydevd
56+
runtime: python39
57+
58+
---
59+
apiVersion: batch/v1
60+
kind: Job
61+
metadata:
62+
name: connect-to-python39
63+
labels:
64+
project: container-debug-support
65+
type: integration-test
66+
spec:
67+
ttlSecondsAfterFinished: 10
68+
backoffLimit: 1
69+
template:
70+
spec:
71+
restartPolicy: Never
72+
containers:
73+
- name: verify-python39
74+
image: pydevconnect
75+
args: ["hello-pydevd-python39:12345"]
76+
77+

‎python/test/pydevconnect/Dockerfile

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2021 The Skaffold Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# This Dockerfile creates a test image for verifying that pydevd is
16+
# running somewhere.
17+
18+
FROM golang:1.17 as build
19+
COPY . .
20+
RUN CGO_ENABLED=0 go build -o pydevconnect -ldflags '-s -w -extldflags "-static"' pydevconnect.go
21+
22+
# Now populate the test image
23+
FROM busybox
24+
COPY --from=build /go/pydevconnect /
25+
ENTRYPOINT ["/pydevconnect"]
+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
Copyright 2021 The Skaffold Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Test utility to connect to a pydevd server to validate that it is working.
18+
// Protocol: https://github.com/fabioz/PyDev.Debugger/blob/main/_pydevd_bundle/pydevd_comm.py
19+
package main
20+
21+
import (
22+
"bufio"
23+
"fmt"
24+
"log"
25+
"net"
26+
"net/url"
27+
"os"
28+
"strconv"
29+
"strings"
30+
"time"
31+
)
32+
33+
const (
34+
CMD_RUN = 101
35+
CMD_LIST_THREADS = 102
36+
CMD_THREAD_CREATE = 103
37+
CMD_THREAD_KILL = 104
38+
CMD_THREAD_RUN = 106
39+
CMD_SET_BREAK = 111
40+
CMD_WRITE_TO_CONSOLE = 116
41+
CMD_VERSION = 501
42+
CMD_RETURN = 502
43+
CMD_ERROR = 901
44+
)
45+
46+
func main() {
47+
if len(os.Args) != 2 {
48+
fmt.Printf("Check that pydevd is running.\n")
49+
fmt.Printf("use: %s host:port\n", os.Args[0])
50+
os.Exit(1)
51+
}
52+
53+
var conn net.Conn
54+
for i := 0; i < 60; i++ {
55+
var err error
56+
conn, err = net.Dial("tcp", os.Args[1])
57+
if err == nil {
58+
break
59+
}
60+
fmt.Printf("(sleeping) unable to connect to %s: %v\n", os.Args[1], err)
61+
time.Sleep(2 * time.Second)
62+
}
63+
64+
pydb := newPydevdDebugConnection(conn)
65+
66+
code, response := pydb.makeRequestWithResponse(CMD_VERSION, "pydevconnect")
67+
if code != CMD_VERSION {
68+
log.Fatalf("expected CMD_VERSION (%d) response (%q)", code, response)
69+
}
70+
if decoded, err := url.QueryUnescape(response); err != nil {
71+
log.Fatalf("CMD_VERSION response (%q): decoding error: %v", response, err)
72+
} else {
73+
fmt.Printf("version: %s", decoded)
74+
}
75+
76+
pydb.makeRequest(CMD_RUN, "test")
77+
}
78+
79+
type pydevdDebugConnection struct {
80+
conn net.Conn
81+
reader *bufio.Reader
82+
msgID int
83+
}
84+
85+
func newPydevdDebugConnection(c net.Conn) *pydevdDebugConnection {
86+
return &pydevdDebugConnection{
87+
conn: c,
88+
reader: bufio.NewReader(c),
89+
msgID: 1,
90+
}
91+
}
92+
93+
func (c *pydevdDebugConnection) makeRequest(code int, arg string) {
94+
currMsgID := c.msgID
95+
c.msgID += 2 // outgoing requests should have odd msgID
96+
97+
fmt.Printf("Making request: code=%d msgId=%d arg=%q\n", code, currMsgID, arg)
98+
fmt.Fprintf(c.conn, "%d\t%d\t%s\n", code, currMsgID, arg)
99+
}
100+
101+
func (c *pydevdDebugConnection) makeRequestWithResponse(code int, arg string) (int, string) {
102+
currMsgID := c.msgID
103+
c.msgID += 2 // outgoing requests should have odd msgID
104+
105+
fmt.Printf("Making request: code=%d msgId=%d arg=%q\n", code, currMsgID, arg)
106+
fmt.Fprintf(c.conn, "%d\t%d\t%s\n", code, currMsgID, arg)
107+
108+
for {
109+
response, err := c.reader.ReadString('\n')
110+
if err != nil {
111+
log.Fatalf("error receiving response: %v", err)
112+
}
113+
fmt.Printf("Received response: %q\n", response)
114+
115+
// check response
116+
tsv := strings.Split(response, "\t")
117+
if len(tsv) != 3 {
118+
log.Fatalf("invalid response: expecting three tab-separated components: %q", response)
119+
}
120+
121+
code, err = strconv.Atoi(tsv[0])
122+
if err != nil {
123+
log.Fatalf("could not parse response code: %q", tsv[0])
124+
}
125+
126+
responseID, err := strconv.Atoi(tsv[1])
127+
if err != nil {
128+
log.Fatalf("could not parse response ID: %q", tsv[1])
129+
} else if responseID == currMsgID {
130+
return code, tsv[2]
131+
}
132+
133+
// handle commands sent to us
134+
switch code {
135+
case CMD_THREAD_CREATE:
136+
fmt.Printf("CMD_THREAD_CREATE: %s\n", tsv[2:])
137+
138+
default:
139+
log.Fatalf("Unknown/unhandled code %d: %q", code, tsv[2:])
140+
}
141+
}
142+
}

‎python/test/pythonapp/Dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ARG PYTHONVERSION
2+
FROM python:${PYTHONVERSION}
3+
4+
RUN pip install --upgrade pip
5+
6+
ARG DEBUG=0
7+
ENV FLASK_DEBUG $DEBUG
8+
ENV FLASK_APP=src/app.py
9+
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0"]
10+
11+
COPY requirements.txt .
12+
ENV PATH="/home/python/.local/bin:${PATH}"
13+
RUN pip install -r requirements.txt
14+
15+
COPY src src
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Flask

‎python/test/pythonapp/src/app.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from flask import Flask
2+
app = Flask(__name__)
3+
4+
@app.route('/')
5+
def hello_world():
6+
print("incoming request")
7+
return 'Hello, World from Flask!\n'

0 commit comments

Comments
 (0)
Please sign in to comment.