-
-
Notifications
You must be signed in to change notification settings - Fork 110
639 lines (601 loc) · 43.3 KB
/
Copy pathbuild.yml
File metadata and controls
639 lines (601 loc) · 43.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Copyright 2021-2026 3A Systems, LLC.
name: Build
on:
push:
branches: [ 'sustaining/4.10.x','master', 'issues/**', 'features/**' ]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-maven:
runs-on: ${{ matrix.os }}
strategy:
# The full Java sweep runs on ubuntu only; macOS and Windows build with the
# minimum supported (11) and the latest (26) Java.
matrix:
os: [ 'ubuntu-latest' ]
java: [ '11','17','21','25','26']
include:
- { os: 'macos-latest', java: '11' }
- { os: 'macos-latest', java: '26' }
- { os: 'windows-latest', java: '11' }
- { os: 'windows-latest', java: '26' }
fail-fast: false
steps:
- name: Install wine+rpm for distribution
if: runner.os == 'Linux'
shell: bash
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo dpkg --add-architecture i386
sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
wine --version
version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
wine msiexec /i /tmp/wine-mono.msi
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- name: Java ${{ matrix.Java }} (${{ matrix.os }})
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
distribution: 'zulu'
- name: Cache Maven packages
uses: actions/cache@v5
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-repository-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-repository
- name: Setup MSVC Developer Command Prompt (x86)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
env:
# Opt in to Node.js 24 for this action, which still ships on Node.js 20.
# See https://github.com/ilammy/msvc-dev-cmd/issues/99
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
with:
arch: x86
- name: Build Windows native executables
if: runner.os == 'Windows'
shell: cmd
run: |
cd opendj-server-legacy\src\build-tools\windows
nmake all
xcopy /Y *.exe ..\..\..\lib\
git status
- name: Set Integration Test Environment
id: failsafe
if: runner.os == 'Linux'
run: |
echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT
- name: Build with Maven
timeout-minutes: 180
env:
MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10
run: mvn --batch-mode --errors --update-snapshots verify --file pom.xml ${{ steps.failsafe.outputs.MAVEN_PROFILE_FLAG }}
- name: Test on Unix
if: runner.os == 'Linux'
run: |
export OPENDJ_JAVA_ARGS="-server -Xmx512m"
opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj/bin/status --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 5000
# --- Load-testing tools smoke tests (server online, 5000 sample users) ---
echo "===== searchrate ====="
opendj-server-legacy/target/package/opendj/bin/searchrate --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password --baseDN "ou=people,dc=example,dc=com" --searchScope sub -c 4 -t 1 -m 2000 -i 1 -g "rand(0,4999)" "(uid=user.%d)" 1.1
echo "===== authrate ====="
opendj-server-legacy/target/package/opendj/bin/authrate --hostname localhost --port 1636 --useSSL --trustAll -D "uid=user.%d,ou=people,dc=example,dc=com" -w password -c 4 -m 2000 -i 1 -g "rand(0,4999)"
echo "===== modrate ====="
opendj-server-legacy/target/package/opendj/bin/modrate --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password -b "uid=user.%d,ou=people,dc=example,dc=com" -c 4 -t 1 -m 2000 -i 1 -g "rand(0,4999)" "description:modrate-load-test"
echo "===== addrate ====="
cat > /tmp/addrate.template <<'EOF'
define suffix=dc=example,dc=com
branch: ou=People,[suffix]
subordinateTemplate: person
template: person
rdnAttr: uid
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: addrate.<sequential:0>
cn: AddRate {uid}
sn: AddRate
userPassword: password
EOF
opendj-server-legacy/target/package/opendj/bin/addrate --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password -c 4 -t 1 -m 2000 -i 1 -C fifo -s 100 /tmp/addrate.template
opendj-server-legacy/target/package/opendj/bin/dsconfig create-backend --hostname localhost --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=example2 --type je --set=base-dn:dc=example2,dc=com --set=enabled:true --no-prompt --trustAll
opendj-server-legacy/target/package/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example2,dc=com opendj-server-legacy/target/package/opendj/config/MakeLDIF/example.template
opendj-server-legacy/target/package/opendj/bin/stop-ds
echo "4.9.9.0" > opendj-server-legacy/target/package/opendj/config/buildinfo
opendj-server-legacy/target/package/opendj/upgrade -n
opendj-server-legacy/target/package/opendj/bin/import-ldif --offline --ldifFile /tmp/test.ldif --backendID=example2
opendj-server-legacy/target/package/opendj/bin/rebuild-index --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll
opendj-server-legacy/target/package/opendj/bin/start-ds
opendj-server-legacy/target/package/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000
opendj-server-legacy/target/package/opendj/bin/stop-ds
rm -rf opendj-server-legacy/target/package/opendj/{config,db,changelogDb,logs,tmp}
- name: Test on Unix FIPS
if: runner.os == 'Linux'
run: |
export OPENDJ_JAVA_ARGS="-server -Xmx512m"
echo password > /tmp/opendj.keystore.pin
keytool -genkey -alias server-cert -keyalg rsa \
-dname "CN=example.com,O=OpenDJ RSA Self-Signed Certificate" \
-keystore /tmp/opendj.bcfks -storetype BCFKS -validity 3650 -providername BCFIPS \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath ./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bc-fips.jar:./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bcpkix-fips.jar \
-keypass:file /tmp/opendj.keystore.pin -storepass:file /tmp/opendj.keystore.pin -keysize 2048 -sigalg SHA256WITHRSA
keytool -selfcert -alias server-cert -keystore /tmp/opendj.bcfks \
-storetype BCFKS -validity 3650 -providername BCFIPS \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath ./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bc-fips.jar:./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bcpkix-fips.jar \
-storepass:file /tmp/opendj.keystore.pin
keytool -genkey -alias admin-cert -keyalg rsa \
-dname "CN=example.com,O=Administration Connector RSA Self-Signed Certificate" \
-keystore /tmp/opendj.bcfks -storetype BCFKS -validity 3650 -providername BCFIPS \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath ./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bc-fips.jar:./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bcpkix-fips.jar \
-keypass:file /tmp/opendj.keystore.pin -storepass:file /tmp/opendj.keystore.pin -keysize 2048 -sigalg SHA256WITHRSA
keytool -selfcert -alias admin-cert -keystore /tmp/opendj.bcfks \
-storetype BCFKS -validity 3650 -providername BCFIPS \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath ./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bc-fips.jar:./opendj-server-legacy/target/package/opendj/lib/org.bouncycastle.bcpkix-fips.jar \
-storepass:file /tmp/opendj.keystore.pin
echo "useBcfksKeystore=/tmp/opendj.bcfks
keyStorePasswordFile=/tmp/opendj.keystore.pin" > /tmp/opendj-setup.properties.bcfks
opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 \
--enableStartTLS --certNickname admin-cert --rootUserDN "cn=Directory Manager" --rootUserPassword password \
--baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt \
--propertiesFilePath /tmp/opendj-setup.properties.bcfks --doNotStart
opendj-server-legacy/target/package/opendj/bin/start-ds
opendj-server-legacy/target/package/opendj/bin/status --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 5000
opendj-server-legacy/target/package/opendj/bin/dsconfig create-backend --hostname localhost --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=example2 --type je --set=base-dn:dc=example2,dc=com --set=enabled:true --no-prompt --trustAll
opendj-server-legacy/target/package/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example2,dc=com opendj-server-legacy/target/package/opendj/config/MakeLDIF/example.template
opendj-server-legacy/target/package/opendj/bin/stop-ds
echo "4.9.9.0" > opendj-server-legacy/target/package/opendj/config/buildinfo
opendj-server-legacy/target/package/opendj/upgrade -n
opendj-server-legacy/target/package/opendj/bin/import-ldif --offline --ldifFile /tmp/test.ldif --backendID=example2
opendj-server-legacy/target/package/opendj/bin/rebuild-index --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll
opendj-server-legacy/target/package/opendj/bin/start-ds
opendj-server-legacy/target/package/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000
opendj-server-legacy/target/package/opendj/bin/stop-ds
rm -rf opendj-server-legacy/target/package/opendj/{config,db,changelogDb,logs,tmp}
- name: Test LDAP in Cassandra
if: runner.os == 'Linux'
run: |
docker run --rm -it -d -p 9042:9042 --name cassandra cassandra
timeout 5m bash -c 'until docker logs cassandra | grep -q "Created default superuser role"; do sleep 5; done'
export OPENDJ_JAVA_ARGS="-server -Xmx512m -Ddatastax-java-driver.basic.contact-points.0=localhost:9042 -Ddatastax-java-driver.basic.load-balancing-policy.local-datacenter=datacenter1"
opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj/bin/dsconfig create-backend -h localhost -p 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=userRoot --type cas --set base-dn:dc=example,dc=com --set db-directory:keyspace_name --set enabled:true --no-prompt --trustAll
opendj-server-legacy/target/package/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example,dc=com opendj-server-legacy/target/package/opendj/config/MakeLDIF/example.template
opendj-server-legacy/target/package/opendj/bin/import-ldif --ldifFile /tmp/test.ldif --backendID=userRoot -h localhost -p 4444 --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy/target/package/opendj/bin/status --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000
opendj-server-legacy/target/package/opendj/bin/stop-ds
rm -rf opendj-server-legacy/target/package/opendj/{config,db,changelogDb,logs,tmp}
- name: Test LDAP in Postgres
if: runner.os == 'Linux'
run: |
docker run --rm -it -d -p 5432:5432 -e POSTGRES_DB=database_name -e POSTGRES_PASSWORD=password --name postgres postgres
timeout 5m bash -c 'until docker logs postgres | grep -q "database system is ready to accept connections"; do sleep 5; done'
export OPENDJ_JAVA_ARGS="-server -Xmx512m"
opendj-server-legacy/target/package/opendj/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj/bin/dsconfig create-backend -h localhost -p 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=userRoot --type jdbc --set base-dn:dc=example,dc=com --set db-directory:jdbc:postgresql://localhost:5432/database_name?user=postgres\&password=password --set enabled:true --no-prompt --trustAll
opendj-server-legacy/target/package/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example,dc=com opendj-server-legacy/target/package/opendj/config/MakeLDIF/example.template
opendj-server-legacy/target/package/opendj/bin/ldapmodify --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll -f /tmp/test.ldif -a 1> /dev/null
opendj-server-legacy/target/package/opendj/bin/status --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000
opendj-server-legacy/target/package/opendj/bin/stop-ds
rm -rf opendj-server-legacy/target/package/opendj/{config,db,changelogDb,logs,tmp}
- name: Test replication
if: runner.os == 'Linux'
run: |
cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj1
cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj2
cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj3
echo "Setup OpenDJ-1"
opendj-server-legacy/target/package/opendj1/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS \
--generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
--sampleData 100000 --cli --acceptLicense --no-prompt
echo "Setup OpenDJ-2 with replication"
opendj-server-legacy/target/package/opendj2/setup -h localhost -p 2389 --ldapsPort 2636 --adminConnectorPort 24444 --enableStartTLS \
--generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
--addBaseEntry --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj2/bin/dsreplication enable --no-prompt --host1 localhost --port1 4444 --bindDN1 "cn=Directory Manager" --bindPassword1 password --replicationPort1 8989 \
--host2 localhost --port2 24444 --bindDN2 "cn=Directory Manager" --bindPassword2 password --replicationPort2 28989 \
--adminUID admin --adminPassword password --baseDN dc=example,dc=com --trustAll --noPropertiesFile
opendj-server-legacy/target/package/opendj2/bin/dsreplication initialize --baseDN dc=example,dc=com --adminUID admin --adminPassword password --hostSource localhost \
--portSource 4444 --hostDestination localhost --portDestination 24444 -X -n
opendj-server-legacy/target/package/opendj2/bin/ldapsearch --port 2636 --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll \
--baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 100000
echo "Setup OpenDJ-3 with replication"
opendj-server-legacy/target/package/opendj3/setup -h localhost -p 3389 --ldapsPort 3636 --adminConnectorPort 34444 --enableStartTLS \
--generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
--addBaseEntry --cli --acceptLicense --no-prompt
opendj-server-legacy/target/package/opendj3/bin/dsreplication enable --no-prompt --host1 localhost --port1 24444 --bindDN1 "cn=Directory Manager" --bindPassword1 password --replicationPort1 28989 \
--host2 localhost --port2 34444 --bindDN2 "cn=Directory Manager" --bindPassword2 password --replicationPort2 38989 \
--adminUID admin --adminPassword password --baseDN dc=example,dc=com --trustAll --noPropertiesFile
opendj-server-legacy/target/package/opendj3/bin/dsreplication initialize --baseDN dc=example,dc=com --adminUID admin --adminPassword password --hostSource localhost \
--portSource 24444 --hostDestination localhost --portDestination 34444 -X -n
opendj-server-legacy/target/package/opendj2/bin/ldapsearch --port 3636 --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll \
--baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 100000
echo "Stopping and removing OpenDJ instances"
opendj-server-legacy/target/package/opendj1/bin/stop-ds
opendj-server-legacy/target/package/opendj2/bin/stop-ds
opendj-server-legacy/target/package/opendj3/bin/stop-ds
rm -rf ./opendj-server-legacy/target/package/opendj1
rm -rf ./opendj-server-legacy/target/package/opendj2
rm -rf ./opendj-server-legacy/target/package/opendj3
- name: Test on Windows
if: runner.os == 'Windows'
run: |
set OPENDJ_JAVA_ARGS="-server -Xmx512m"
opendj-server-legacy\target\package\opendj\setup.bat -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt
opendj-server-legacy\target\package\opendj\bat\status.bat --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "5000"
# --- Load-testing tools smoke tests (server online, 5000 sample users) ---
Write-Host "===== searchrate ====="
opendj-server-legacy\target\package\opendj\bat\searchrate.bat --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password --baseDN "ou=people,dc=example,dc=com" --searchScope sub -c 4 -t 1 -m 2000 -i 1 -g "rand(0,4999)" "(uid=user.%d)" 1.1
if ($LASTEXITCODE -ne 0) { throw "searchrate failed with exit code $LASTEXITCODE" }
Write-Host "===== authrate ====="
opendj-server-legacy\target\package\opendj\bat\authrate.bat --hostname localhost --port 1636 --useSSL --trustAll -D "uid=user.%d,ou=people,dc=example,dc=com" -w password -c 4 -m 2000 -i 1 -g "rand(0,4999)"
if ($LASTEXITCODE -ne 0) { throw "authrate failed with exit code $LASTEXITCODE" }
Write-Host "===== modrate ====="
opendj-server-legacy\target\package\opendj\bat\modrate.bat --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password -b "uid=user.%d,ou=people,dc=example,dc=com" -c 4 -t 1 -m 2000 -i 1 -g "rand(0,4999)" "description:modrate-load-test"
if ($LASTEXITCODE -ne 0) { throw "modrate failed with exit code $LASTEXITCODE" }
Write-Host "===== addrate ====="
@'
define suffix=dc=example,dc=com
branch: ou=People,[suffix]
subordinateTemplate: person
template: person
rdnAttr: uid
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: addrate.<sequential:0>
cn: AddRate {uid}
sn: AddRate
userPassword: password
'@ | Set-Content -Encoding ascii addrate.template
opendj-server-legacy\target\package\opendj\bat\addrate.bat --hostname localhost --port 1636 --useSSL --trustAll --bindDN "cn=Directory Manager" --bindPassword password -c 4 -t 1 -m 2000 -i 1 -C fifo -s 100 addrate.template
if ($LASTEXITCODE -ne 0) { throw "addrate failed with exit code $LASTEXITCODE" }
opendj-server-legacy\target\package\opendj\bat\dsconfig.bat create-backend --hostname localhost --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=example2 --type je --set=base-dn:dc=example2,dc=com --set=enabled:true --no-prompt --trustAll
opendj-server-legacy\target\package\opendj\bat\makeldif.bat -o test.ldif -c suffix=dc=example2,dc=com opendj-server-legacy\target\package\opendj\config\MakeLDIF\example.template
opendj-server-legacy\target\package\opendj\bat\stop-ds.bat
echo "4.9.9.0" > opendj-server-legacy\target\package\opendj\config\buildinfo
opendj-server-legacy\target\package\opendj\upgrade.bat
opendj-server-legacy\target\package\opendj\bat\import-ldif.bat --offline --ldifFile test.ldif --backendID=example2
opendj-server-legacy\target\package\opendj\bat\rebuild-index.bat --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll
opendj-server-legacy\target\package\opendj\bat\start-ds.bat
opendj-server-legacy\target\package\opendj\bat\rebuild-index.bat --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "10000"
opendj-server-legacy\target\package\opendj\bat\stop-ds.bat
opendj-server-legacy\target\package\opendj\bat\windows-service.bat --enableService
net start "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net start 'OpenDJ Server' failed with exit code $LASTEXITCODE" }
for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "10000"
net stop "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net stop 'OpenDJ Server' failed with exit code $LASTEXITCODE" }
opendj-server-legacy\target\package\opendj\bat\windows-service.bat --disableService
- name: Upload Windows exe artifacts
if: runner.os == 'Windows'
uses: actions/upload-artifact@v7
with:
name: windows-exe-${{ matrix.java }}
retention-days: 5
path: opendj-server-legacy/src/build-tools/windows/*.exe
- name: Upload artifacts OpenDJ Server
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.os }}-${{ matrix.java }}
retention-days: 5
path: |
opendj-server-legacy/target/package/*.zip
opendj-ldap-toolkit/target/*.zip
opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb
opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm
opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
opendj-packages/opendj-docker/target/Dockerfile.zip
opendj-packages/opendj-openshift-template/*.yaml
opendj-doc-generated-ref/target/*.zip
opendj-dsml-servlet/target/*.war
opendj-rest2ldap-servlet/target/*.war
build-docker:
needs: build-maven
runs-on: 'ubuntu-latest'
services:
registry:
image: registry:3
ports:
- 5000:5000
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: .github/benchmark
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: ubuntu-latest-11
- name: Get latest release version
shell: bash
run: |
export git_version_last="$(curl -i -o - --silent https://api.github.com/repos/OpenIdentityPlatform/OpenDJ/releases/latest | grep -m1 "\"name\"" | cut -d\" -f4)" ; echo "last release: $git_version_last"
echo "release_version=$git_version_last" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: |
localhost:5000/${{ github.repository }}
tags: |
type=raw,value=${{ env.release_version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
driver-opts: network=host
- name: Prepare Dockerfile
shell: bash
run: |
unzip -d ./opendj-packages/opendj-docker ./opendj-packages/opendj-docker/target/Dockerfile.zip
cp ./opendj-server-legacy/target/package/opendj-*.zip ./opendj-packages/opendj-docker
sed -i -E '/^#COPY opendj/s/^#//' ./opendj-packages/opendj-docker/Dockerfile
- name: Build image (default)
uses: docker/build-push-action@v7
with:
context: ./opendj-packages/opendj-docker
file: ./opendj-packages/opendj-docker/Dockerfile
build-args: |
VERSION=${{ env.release_version }}
platforms: linux/amd64, linux/arm64/8, linux/ppc64le, linux/s390x, linux/riscv64 #, linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Docker test
shell: bash
run: |
trap 'code=$?; echo "::group::container logs (test)"; docker logs test 2>&1 || true; echo "::endgroup::"; exit $code' ERR
docker run --rm -it -d --memory="512m" --name=test localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}
timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test | grep -q \"healthy\"; do sleep 10; done'
docker exec test 'sh' '-c' '/opt/opendj/bin/dsconfig create-backend --hostname localhost --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=example2 --type je --set=base-dn:dc=example2,dc=com --set=enabled:true --no-prompt --trustAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example2,dc=com /opt/opendj/data/config/MakeLDIF/example.template'
docker exec test 'sh' '-c' '/opt/opendj/bin/stop-ds'
docker exec test 'sh' '-c' '/opt/opendj/bin/import-ldif --offline --ldifFile /tmp/test.ldif --backendID=example2'
docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/start-ds'
docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000'
docker kill test
- name: Docker test custom password
shell: bash
run: |
trap 'code=$?; echo "::group::container logs (test_custom)"; docker logs test_custom 2>&1 || true; echo "::endgroup::"; exit $code' ERR
docker run --rm -it -d --memory="512m" -e ADD_BASE_ENTRY="--addBaseEntry" -e ROOT_PASSWORD=custom_password --name=test_custom localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}
timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test_custom | grep -q \"healthy\"; do sleep 10; done'
docker exec test_custom 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword custom_password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1'
docker kill test_custom
- name: Cache JMeter
uses: actions/cache@v5
with:
path: ~/jmeter
key: jmeter-5.6.3
- name: Benchmark PDB vs JE
shell: bash
run: |
A_BACKEND=je B_BACKEND=pdb THREADS=200 DURATION=150 bash .github/benchmark/compare-opendj.sh \
"JE" localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }} \
"PDB" localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}
- name: Upload benchmark artifacts (PDB vs JE)
if: always()
uses: actions/upload-artifact@v7
with:
name: benchmark-pdb-vs-je
path: |
a/
b/
*.jtl
*.docker.log
*.jmeter.out
if-no-files-found: warn
retention-days: 3
- name: Benchmark Build vs Release
shell: bash
run: |
THREADS=200 DURATION=150 bash .github/benchmark/compare-opendj.sh \
"Build" localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }} \
"Release" openidentityplatform/opendj:latest
- name: Upload benchmark artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: benchmark-build-vs-release
path: |
a/
b/
*.jtl
*.docker.log
*.jmeter.out
if-no-files-found: warn
retention-days: 3
build-docker-alpine:
needs: build-maven
runs-on: 'ubuntu-latest'
services:
registry:
image: registry:3
ports:
- 5000:5000
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: .github/benchmark
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: ubuntu-latest-11
- name: Get latest release version
shell: bash
run: |
export git_version_last="$(curl -i -o - --silent https://api.github.com/repos/OpenIdentityPlatform/OpenDJ/releases/latest | grep -m1 "\"name\"" | cut -d\" -f4)" ; echo "last release: $git_version_last"
echo "release_version=$git_version_last" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: |
localhost:5000/${{ github.repository }}
tags: |
type=raw,value=alpine
type=raw,value=${{ env.release_version }}-alpine
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
driver-opts: network=host
- name: Prepare Dockerfile
shell: bash
run: |
unzip -d ./opendj-packages/opendj-docker ./opendj-packages/opendj-docker/target/Dockerfile.zip
cp ./opendj-server-legacy/target/package/opendj-*.zip ./opendj-packages/opendj-docker
sed -i -E '/^#COPY opendj/s/^#//' ./opendj-packages/opendj-docker/Dockerfile-alpine
- name: Build image
uses: docker/build-push-action@v7
with:
context: ./opendj-packages/opendj-docker
file: ./opendj-packages/opendj-docker/Dockerfile-alpine
build-args: |
VERSION=${{ env.release_version }}
platforms: linux/amd64, linux/arm64/8, linux/s390x, linux/ppc64le, linux/386, linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Docker test
shell: bash
run: |
trap 'code=$?; echo "::group::container logs (test)"; docker logs test 2>&1 || true; echo "::endgroup::"; exit $code' ERR
docker run --rm -it -d --memory="1g" --name=test localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}-alpine
timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test | grep -q \"healthy\"; do sleep 10; done'
docker exec test 'sh' '-c' '/opt/opendj/bin/dsconfig create-backend --hostname localhost --port 4444 --bindDN "cn=Directory Manager" --bindPassword password --backend-name=example2 --type je --set=base-dn:dc=example2,dc=com --set=enabled:true --no-prompt --trustAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/makeldif -o /tmp/test.ldif -c suffix=dc=example2,dc=com /opt/opendj/data/config/MakeLDIF/example.template'
docker exec test 'sh' '-c' '/opt/opendj/bin/stop-ds'
docker exec test 'sh' '-c' '/opt/opendj/bin/import-ldif --offline --ldifFile /tmp/test.ldif --backendID=example2'
docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/start-ds'
docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll'
docker exec test 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000'
docker kill test
- name: Docker test custom password
shell: bash
run: |
trap 'code=$?; echo "::group::container logs (test_custom)"; docker logs test_custom 2>&1 || true; echo "::endgroup::"; exit $code' ERR
docker run --rm -it -d --memory="1g" -e ADD_BASE_ENTRY="--addBaseEntry" -e ROOT_PASSWORD=custom_password --name=test_custom localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}-alpine
timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test_custom | grep -q \"healthy\"; do sleep 10; done'
docker exec test_custom 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword custom_password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1'
docker kill test_custom
- name: Cache JMeter
uses: actions/cache@v5
with:
path: ~/jmeter
key: jmeter-5.6.3
- name: Benchmark Build-alpine vs Release-alpine
shell: bash
run: |
THREADS=200 DURATION=150 bash .github/benchmark/compare-opendj.sh \
"Build-alpine" localhost:5000/${GITHUB_REPOSITORY,,}:${{ env.release_version }}-alpine \
"Release-alpine" openidentityplatform/opendj:alpine
- name: Upload benchmark artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: benchmark-build-vs-release-alpine
path: |
a/
b/
*.jtl
*.docker.log
*.jmeter.out
if-no-files-found: warn
retention-days: 90
test-msi:
needs: build-maven
runs-on: 'windows-latest'
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: windows-latest-11
- name: Set up Java
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'zulu'
- name: Install MSI (silent)
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" }
Write-Host "MSI: $msi"
# No OPENDJ property: use the default install directory (a path with spaces),
# which the server scripts must handle.
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install.log"
if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 80; throw "msiexec /i failed: $($p.ExitCode)" }
$root = @("C:\Program Files (x86)\OpenDJ","C:\Program Files\OpenDJ") | Where-Object { Test-Path "$_\setup.bat" } | Select-Object -First 1
if (-not $root) { Get-Content install.log -Tail 80; throw "OpenDJ install root with setup.bat not found" }
Write-Host "Installed to $root"
"OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Setup and start/stop the Windows service
shell: pwsh
run: |
$root = $env:OPENDJ_ROOT
$env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
& "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart
if ($LASTEXITCODE -ne 0) { throw "setup.bat failed: $LASTEXITCODE" }
& "$root\bat\windows-service.bat" --enableService
if ($LASTEXITCODE -ne 0) { throw "windows-service --enableService failed: $LASTEXITCODE" }
net start "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net start failed: $LASTEXITCODE" }
for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
& "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
if ($LASTEXITCODE -ne 0) { throw "ldapsearch failed: $LASTEXITCODE" }
net stop "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net stop failed: $LASTEXITCODE" }
& "$root\bat\windows-service.bat" --disableService
- name: Uninstall MSI
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall.log"
if ($p.ExitCode -ne 0) { Get-Content uninstall.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" }
Write-Host "Uninstalled OK"