Skip to content

Commit 22009ec

Browse files
authored
Try to add functionality test (#29)
* Try to add functionality test * Fixing services ports section * Try to use local image from previous job * Disable function test execution * Try to fix error * Another test * Fix artifact upload * Fix artifact download * Extract image ID * Fix Typo * Docker images debug output * Add more debug output * Add terst.json inside container by default * Add quotes to options * Remove option * Change server name * Add feature to set configuration file via environment variable * Remove job dependency * Remove platform support in functionality test build * Replace double with single quotes * Add conection test * Change method to alter Dockerfile and add debug output * Use an alternate Dockerfile that implements a test.json * Fix yaml syntax * Remove CMD, beacuse the functionality changed * Add output validation * Add folding details * Try fix folding * Try fix folding * Another try for folding content * Testing folding
1 parent ccdde7c commit 22009ec

File tree

6 files changed

+219
-16
lines changed

6 files changed

+219
-16
lines changed

.github/workflows/container-image-build-validation.yaml

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,156 @@ jobs:
3838
with:
3939
name: container-build
4040
path: /tmp/container.tar
41+
42+
function-test-preparation:
43+
name: Container functionality test preparation
44+
runs-on: ubuntu-latest
45+
steps:
46+
- name: Checkout GIT repository
47+
uses: actions/checkout@v3
48+
- name: Log in to Github container registry
49+
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
50+
- name: Build container for functionality test
51+
uses: docker/build-push-action@v4.1.0
52+
with:
53+
push: true
54+
load: false
55+
context: .
56+
file: ./Dockerfile.test
57+
tags: ghcr.io/cybcon/modbus-server/modbus-server:test
58+
59+
function-test-execution:
60+
name: Container functionality test execution
61+
needs: function-test-preparation
62+
runs-on: ubuntu-latest
63+
services:
64+
modbusserver:
65+
image: ghcr.io/cybcon/modbus-server/modbus-server:test
66+
env:
67+
CONFIG_FILE: /test.json
68+
ports:
69+
- 5020:5020
70+
steps:
71+
- name: Pull Modbus Client for functionality test
72+
run: |
73+
docker pull oitc/modbus-client:latest
74+
echo "# Modbus functionality test" >> ${GITHUB_STEP_SUMMARY}
75+
- name: Get modbus server address
76+
id: modbus-server-address
77+
run: |
78+
LOCAL_MACHINE=$(ifconfig -a eth0|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:")
79+
echo "DEBUG: ${LOCAL_MACHINE}"
80+
echo "ip=${LOCAL_MACHINE}" >> $GITHUB_OUTPUT
81+
- name: Validate server endpoint
82+
run: |
83+
modbusserver=${{steps. modbus-server-address.outputs.ip}}
84+
ping -c 1 -q ${modbusserver}
85+
RC=$?
86+
if [ ${RC} -gt 0 ]; then
87+
echo "ERROR: Modbus slave not reachable" >&2
88+
exit 1
89+
fi
90+
- name: Test Discrete Output Coils
91+
run: |
92+
modbusserver=${{steps. modbus-server-address.outputs.ip}}
93+
echo "## Get Discrete Output Coils" >> ${GITHUB_STEP_SUMMARY}
94+
echo '<details>' >> ${GITHUB_STEP_SUMMARY}
95+
echo ' <summary>Test output details</summary>' >> ${GITHUB_STEP_SUMMARY}
96+
echo "" >> ${GITHUB_STEP_SUMMARY}
97+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
98+
echo " docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 1 -r 0 -l 10" >> ${GITHUB_STEP_SUMMARY}
99+
docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 1 -r 0 -l 10 > output.tmp 2>&1
100+
cat output.tmp | while read line
101+
do
102+
echo " ${line}" >> ${GITHUB_STEP_SUMMARY}
103+
done
104+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
105+
echo '</details>' >> ${GITHUB_STEP_SUMMARY}
106+
echo "" >> ${GITHUB_STEP_SUMMARY}
107+
# analyze output
108+
RESULT=$(cat output.tmp | grep ^2 | awk '{print $3}')
109+
if [ "${RESULT}" == "True" ]; then
110+
echo "Test succesfull :white_check_mark:" >> ${GITHUB_STEP_SUMMARY}
111+
else
112+
echo "Test failed :x:" >> ${GITHUB_STEP_SUMMARY}
113+
exit 1
114+
fi
115+
- name: Test Discrete Input Contacts
116+
run: |
117+
modbusserver=${{steps. modbus-server-address.outputs.ip}}
118+
echo "## Get Discrete Input Contacts" >> ${GITHUB_STEP_SUMMARY}
119+
echo '<details>' >> ${GITHUB_STEP_SUMMARY}
120+
echo ' <summary>Test output details</summary>' >> ${GITHUB_STEP_SUMMARY}
121+
echo "" >> ${GITHUB_STEP_SUMMARY}
122+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
123+
echo " docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 2 -r 0 -l 10" >> ${GITHUB_STEP_SUMMARY}
124+
docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 2 -r 0 -l 10 > output.tmp 2>&1
125+
cat output.tmp | while read line
126+
do
127+
echo " ${line}" >> ${GITHUB_STEP_SUMMARY}
128+
done
129+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
130+
echo '</details>' >> ${GITHUB_STEP_SUMMARY}
131+
echo "" >> ${GITHUB_STEP_SUMMARY}
132+
# analyze output
133+
RESULT=$(cat output.tmp | grep ^10002 | awk '{print $3}')
134+
if [ "${RESULT}" == "True" ]; then
135+
echo "Test succesfull :white_check_mark:" >> ${GITHUB_STEP_SUMMARY}
136+
else
137+
echo "Test failed :x:" >> ${GITHUB_STEP_SUMMARY}
138+
exit 1
139+
fi
140+
- name: Test Analog Output Holding Register
141+
run: |
142+
modbusserver=${{steps. modbus-server-address.outputs.ip}}
143+
echo "## Get Analog Output Holding Register" >> ${GITHUB_STEP_SUMMARY}
144+
echo '<details>' >> ${GITHUB_STEP_SUMMARY}
145+
echo ' <summary>Test output details</summary>' >> ${GITHUB_STEP_SUMMARY}
146+
echo "" >> ${GITHUB_STEP_SUMMARY}
147+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
148+
echo " docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 3 -r 0 -l 10" >> ${GITHUB_STEP_SUMMARY}
149+
docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 3 -r 0 -l 10 > output.tmp 2>&1
150+
cat output.tmp | while read line
151+
do
152+
echo " ${line}" >> ${GITHUB_STEP_SUMMARY}
153+
done
154+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
155+
echo '</details>' >> ${GITHUB_STEP_SUMMARY}
156+
echo "" >> ${GITHUB_STEP_SUMMARY}
157+
# analyze output
158+
RESULT=$(cat output.tmp | grep ^40002 | awk '{print $2}')
159+
if [ "${RESULT}" == "0xCC03" ]; then
160+
echo "Test succesfull :white_check_mark:" >> ${GITHUB_STEP_SUMMARY}
161+
else
162+
echo "Test failed :x:" >> ${GITHUB_STEP_SUMMARY}
163+
exit 1
164+
fi
165+
- name: Test Analog Input Register
166+
run: |
167+
modbusserver=${{steps. modbus-server-address.outputs.ip}}
168+
echo "## Get Analog Input Register" >> ${GITHUB_STEP_SUMMARY}
169+
echo '<details>' >> ${GITHUB_STEP_SUMMARY}
170+
echo ' <summary>Test output details</summary>' >> ${GITHUB_STEP_SUMMARY}
171+
echo "" >> ${GITHUB_STEP_SUMMARY}
172+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
173+
echo " docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 4 -r 0 -l 10" >> ${GITHUB_STEP_SUMMARY}
174+
docker run --rm oitc/modbus-client:latest -s ${modbusserver} -p 5020 -t 4 -r 0 -l 10 > output.tmp 2>&1
175+
cat output.tmp | while read line
176+
do
177+
echo " ${line}" >> ${GITHUB_STEP_SUMMARY}
178+
done
179+
echo ' ```' >> ${GITHUB_STEP_SUMMARY}
180+
echo '</details>' >> ${GITHUB_STEP_SUMMARY}
181+
echo "" >> ${GITHUB_STEP_SUMMARY}
182+
# analyze output
183+
RESULT=$(cat output.tmp | grep ^30002 | head -1 | awk '{print $2}')
184+
if [ "${RESULT}" == "0xDD03" ]; then
185+
echo "Test succesfull :white_check_mark:" >> ${GITHUB_STEP_SUMMARY}
186+
else
187+
echo "Test failed :x:" >> ${GITHUB_STEP_SUMMARY}
188+
exit 1
189+
fi
190+
41191
scan:
42192
name: Container vulnerability scan
43193
needs: container-build

Dockerfile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
FROM python:3.10.9-alpine3.17
1+
FROM alpine:3.18.5
22

33
LABEL maintainer="Michael Oberdorf IT-Consulting <info@oberdorf-itc.de>"
4-
LABEL site.local.program.version="1.2.0"
4+
LABEL site.local.program.version="1.3.0"
5+
6+
RUN apk upgrade --available --no-cache --update \
7+
&& apk add --no-cache --update \
8+
python3=3.11.6-r0 \
9+
py3-pip=23.1.2-r0 \
10+
# Cleanup APK
11+
&& rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
512

613
COPY --chown=root:root /src /
714

@@ -13,4 +20,3 @@ USER 1434:1434
1320

1421
# Start Server
1522
ENTRYPOINT ["python", "-u", "/app/modbus_server.py"]
16-
CMD ["-f", "/app/modbus_server.json"]

Dockerfile.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM alpine:3.18.5
2+
3+
LABEL maintainer="Michael Oberdorf IT-Consulting <info@oberdorf-itc.de>"
4+
LABEL site.local.program.version="1.3.0"
5+
6+
RUN apk upgrade --available --no-cache --update \
7+
&& apk add --no-cache --update \
8+
python3=3.11.6-r0 \
9+
py3-pip=23.1.2-r0 \
10+
# Cleanup APK
11+
&& rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
12+
13+
COPY --chown=root:root /src /
14+
COPY --chown=root:root /examples/test.json /test.json
15+
16+
RUN pip3 install --no-cache-dir -r /requirements.txt
17+
18+
EXPOSE 5020/tcp
19+
20+
USER 1434:1434
21+
22+
# Start Server
23+
ENTRYPOINT ["python", "-u", "/app/modbus_server.py"]

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ Container image: [DockerHub](https://hub.docker.com/r/oitc/modbus-server)
88

99
# Supported tags and respective `Dockerfile` links
1010

11-
* [`latest`, `1.2.0`](https://github.com/cybcon/modbus-server/blob/v1.2.0/Dockerfile)
11+
* [`latest`, `1.3.0`](https://github.com/cybcon/modbus-server/blob/v1.3.0/Dockerfile)
12+
* [`1.2.0`](https://github.com/cybcon/modbus-server/blob/v1.2.0/Dockerfile)
1213
* [`1.1.5`](https://github.com/cybcon/modbus-server/blob/v1.1.5/Dockerfile)
1314
* [`1.1.4`](https://github.com/cybcon/modbus-server/blob/v1.1.4/Dockerfile)
1415
* [`1.1.3`](https://github.com/cybcon/modbus-server/blob/v1.1.3/Dockerfile)
@@ -55,11 +56,20 @@ docker run --rm -p 5020:5020 -v ./server_config.json:/app/modbus_server.json oit
5556
```
5657

5758
# Configuration
59+
## Container configuration
60+
61+
The container reads some configuration via environment variables.
62+
63+
| Environment variable name | Description | Required | Default value |
64+
|------------------------------|------------------------------------------------------------------------------------|--------------|---------------------------|
65+
| `CONFIG_FILE` | The configuration file that that should be used to build the initial Modbus slave. | **OPTIONAL** | `/app/modbus_server.json` |
5866

59-
## Configuration file
6067

61-
The path and filename to the general configuration file can be set via command option `-f <file>` after mounting it inside of the container. By default, the script will use `/app/modbus_server.json`.
68+
## Parameter
69+
Alternatively, the container can also be configured with a command line option `-f <file>` instead of an environment variable. By default, the script will use `/app/modbus_server.json`.
6270

71+
72+
## Configuration file
6373
### Default configuration file of the container
6474

6575
The `/app/modbus_server.json` file comes with following content:

src/app/modbus_server.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Author: Michael Oberdorf IT-Consulting
55
Datum: 2020-03-30
66
Last modified by: Michael Oberdorf
7-
Last modified at: 2023-11-13
7+
Last modified at: 2023-12-06
88
*************************************************************************** """
99
import sys
1010
import os
@@ -20,8 +20,8 @@
2020
import json
2121

2222
# default configuration file path
23-
config_file='/app/modbus_server.json'
24-
VERSION='1.2.0'
23+
default_config_file='/app/modbus_server.json'
24+
VERSION='1.3.0'
2525
"""
2626
###############################################################################
2727
# F U N C T I O N S
@@ -206,18 +206,33 @@ def prepareRegister(register: dict, initType: str, initializeUndefinedRegisters:
206206
# M A I N
207207
###############################################################################
208208
"""
209+
# intialize variable
210+
config_file=None
211+
212+
# Parsing environment variables
213+
if 'CONFIG_FILE' in os.environ:
214+
if not os.path.isfile(os.environ['CONFIG_FILE']):
215+
print('ERROR:', 'configuration file not exist:', os.environ['CONFIG_FILE'])
216+
sys.exit(1)
217+
else:
218+
config_file=os.environ['CONFIG_FILE']
219+
209220

210221
# Parsing command line arguments
211222
parser = argparse.ArgumentParser(description='Modbus TCP Server')
212223
group = parser.add_argument_group()
213-
group.add_argument('-f', '--config_file', help='The configuration file in json format (default: ' + config_file +')')
224+
group.add_argument('-f', '--config_file', help='The configuration file in json format (default: ' + default_config_file +')')
214225
args = parser.parse_args()
215226
if args.config_file:
216-
if not os.path.isfile(args.config_file):
217-
print('ERROR:', 'configuration file not exist:', args.config_file)
218-
sys.exit(1)
219-
else:
220-
config_file=args.config_file
227+
if not os.path.isfile(args.config_file):
228+
print('ERROR:', 'configuration file not exist:', args.config_file)
229+
sys.exit(1)
230+
else:
231+
config_file=args.config_file
232+
233+
# define default if no config file path is set
234+
if not config_file:
235+
config_file=default_config_file
221236

222237
# read configuration file
223238
with open(config_file, encoding='utf-8') as f:

src/requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
pip >= 23
21
pymodbus >= 2, < 3

0 commit comments

Comments
 (0)