Skip to content

Commit 4576063

Browse files
committed
add example using pre and post hook to monitor fleet workers
Signed-off-by: Luke Roy <luke.roy@ibm.com>
1 parent f4af7c7 commit 4576063

7 files changed

Lines changed: 627 additions & 0 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Binaries
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
fleet-register
8+
9+
# Test files
10+
*_test.go
11+
12+
# Output files
13+
*.xlsx
14+
*.xls
15+
16+
# Git
17+
.git
18+
.gitignore
19+
20+
# IDE
21+
.vscode
22+
.idea
23+
*.swp
24+
*.swo
25+
*~
26+
27+
# Documentation
28+
README.md
29+
30+
# Docker
31+
Dockerfile
32+
.dockerignore
33+
34+
# OS
35+
.DS_Store
36+
Thumbs.db
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Stage 1: Build stage
2+
FROM quay.io/projectquay/golang:1.25 AS builder
3+
4+
# Set working directory
5+
WORKDIR /build
6+
7+
# Copy go mod files
8+
COPY go.mod go.sum ./
9+
10+
# Download dependencies
11+
RUN go mod download
12+
13+
# Copy source code
14+
COPY main.go ./
15+
16+
# Build the application
17+
# CGO_ENABLED=0 for static binary
18+
# -ldflags="-w -s" to reduce binary size
19+
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o fleet-register .
20+
21+
# Stage 2: Runtime stage
22+
FROM gcr.io/distroless/static-debian13
23+
24+
# Copy binary from builder
25+
COPY --from=builder /build/fleet-register /app/fleet-register
26+
27+
# Set working directory
28+
WORKDIR /app
29+
30+
# Expose port
31+
EXPOSE 8080
32+
33+
# Set environment variable for the workbook path
34+
ENV WORKBOOK_PATH=/fleet-workers/fleet-register.xlsx
35+
36+
# Run the application
37+
CMD ["/app/fleet-register"]
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Fleet Register
2+
3+
A lightweight Go HTTP server that monitors IBM Cloud Code Engine fleet workers by tracking their lifecycle events in an Excel file.
4+
5+
## Use Case
6+
7+
This application is deployed as a Code Engine application to monitor fleet workers. Fleet workers call the `/register` endpoint when they start and the `/deregister` endpoint before they shut down. The application maintains a persistent Excel file tracking all worker activity, including registration and completion timestamps.
8+
9+
**Deployment Flow:**
10+
1. Deploy this app as a Code Engine application with persistent storage
11+
2. Configure the fleet with register/deregister hooks pointing to this app
12+
3. Fleet workers automatically report their status throughout their lifecycle
13+
14+
## Endpoints
15+
16+
- `POST /register`
17+
Creates a row in `fleet-register.xlsx` with status `running` and records the registration timestamp.
18+
19+
- `POST /deregister`
20+
Updates the matching row by `worker_name` and `worker_ip` to `completed` and records the completion timestamp.
21+
If no row exists, it creates a new row with status `completed`.
22+
23+
- `GET /download`
24+
Downloads the Excel file.
25+
26+
## Request body
27+
28+
```json
29+
{
30+
"worker_name": "worker-01",
31+
"worker_ip": "192.168.1.10"
32+
}
33+
```
34+
35+
## Deployment
36+
37+
### Code Engine (Production)
38+
39+
1. **Deploy the application with persistent storage:**
40+
```bash
41+
ibmcloud ce app create --name fleet-workers \
42+
--build-dockerfile Dockerfile \
43+
--build-source . \
44+
--mount-data-store /fleet-workers=fleet-data
45+
```
46+
47+
2. **Run fleet with hooks:**
48+
49+
The `run` script automatically retrieves the app URL and creates a fleet with register/deregister hooks:
50+
```bash
51+
./run
52+
```
53+
54+
Or manually create a fleet:
55+
```bash
56+
APP_URL=$(ibmcloud ce app get --name fleet-workers -o url)
57+
58+
PREHOOK="curl -X POST \${APP_URL}/register -H 'Content-Type: application/json' -d '{\"worker_name\":\"\${CE_WORKER_NAME}\",\"worker_ip\":\"\${CE_WORKER_IP}\"}'"
59+
60+
POSTHOOK="curl -X POST \${APP_URL}/deregister -H 'Content-Type: application/json' -d '{\"worker_name\":\"\${CE_WORKER_NAME}\",\"worker_ip\":\"\${CE_WORKER_IP}\"}'"
61+
62+
ibmcloud code-engine fleet create --name my-fleet \
63+
--tasks-state-store fleet-task-store \
64+
--subnetpool-name fleet-subnetpool \
65+
--image registry.access.redhat.com/ubi10/ubi-minimal \
66+
--max-scale 100 \
67+
--command="sleep" \
68+
--arg "30" \
69+
--tasks 100 \
70+
--env __CE_INTERNAL_HOOK_AFTER_STARTUP="${PREHOOK}" \
71+
--env __CE_INTERNAL_HOOK_AFTER_STARTUP_RETRY_LIMIT=3 \
72+
--env __CE_INTERNAL_HOOK_AFTER_STARTUP_MAX_EXECUTION_TIME=30m \
73+
--env __CE_INTERNAL_HOOK_BEFORE_SHUTDOWN="${POSTHOOK}" \
74+
--env __CE_INTERNAL_HOOK_BEFORE_SHUTDOWN_RETRY_LIMIT=3 \
75+
--env __CE_INTERNAL_HOOK_BEFORE_SHUTDOWN_MAX_EXECUTION_TIME=30m \
76+
--env APP_URL="${APP_URL}" \
77+
--cpu 2 \
78+
--memory 4G
79+
```
80+
81+
### Local Development
82+
83+
```bash
84+
go run .
85+
```
86+
87+
Server listens on `:8080`.
88+
89+
### Docker
90+
91+
```bash
92+
docker build -t fleet-register .
93+
docker run -p 8080:8080 -v fleet-data:/fleet-workers fleet-register
94+
```
95+
96+
The Excel file is saved to `/fleet-workers/fleet-register.xlsx` and persisted in the volume.
97+
98+
## Examples
99+
100+
Register a worker:
101+
102+
```bash
103+
curl -X POST http://localhost:8080/register \
104+
-H "Content-Type: application/json" \
105+
-d '{
106+
"worker_name": "worker-01",
107+
"worker_ip": "192.168.1.10"
108+
}'
109+
```
110+
111+
Deregister a worker:
112+
113+
```bash
114+
curl -X POST http://localhost:8080/deregister \
115+
-H "Content-Type: application/json" \
116+
-d '{
117+
"worker_name": "worker-01",
118+
"worker_ip": "192.168.1.10"
119+
}'
120+
```
121+
122+
Download the workbook:
123+
124+
```bash
125+
curl -O -J http://localhost:8080/download
126+
```
127+
128+
## Excel file
129+
130+
The server automatically creates `fleet-register.xlsx` in the project root (or `/fleet-workers/` in Docker) if it does not exist.
131+
132+
The workbook contains one sheet named `Workers` with these columns:
133+
134+
- `worker_name` - Name of the worker
135+
- `worker_ip` - IP address of the worker
136+
- `status` - Current status (running/completed)
137+
- `registered_at` - Timestamp when the worker was registered (ISO 8601 format)
138+
- `completed_at` - Timestamp when the worker completed (ISO 8601 format)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module fleet-register
2+
3+
go 1.22
4+
5+
require github.com/xuri/excelize/v2 v2.8.1
6+
7+
require (
8+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
9+
github.com/richardlehane/mscfb v1.0.4 // indirect
10+
github.com/richardlehane/msoleps v1.0.3 // indirect
11+
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
12+
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
13+
golang.org/x/crypto v0.19.0 // indirect
14+
golang.org/x/net v0.21.0 // indirect
15+
golang.org/x/text v0.14.0 // indirect
16+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
4+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
5+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
8+
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
9+
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
10+
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
11+
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
12+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
13+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
14+
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0=
15+
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
16+
github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
17+
github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
18+
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
19+
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
20+
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
21+
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
22+
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
23+
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
24+
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
25+
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
26+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
27+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
28+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
29+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)