Skip to content

Commit a83af6f

Browse files
craig[bot]golgeek
andcommitted
Merge #145388
145388: roachtest: add IBM Cloud r=rickystewart,srosenberg a=golgeek This patch bootstraps the ability to run roachtests on IBM Cloud. This builds on top of #145387. Epic: none Release note: none Co-authored-by: Ludovic Leroux <[email protected]>
2 parents 5fa10b5 + 877bada commit a83af6f

File tree

23 files changed

+464
-114
lines changed

23 files changed

+464
-114
lines changed

build/teamcity/cockroach/nightlies/roachtest_compile_bits.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ if [ "$#" -eq 0 ]; then
1717
echo "Builds all bits needed for roachtests and stages them in bin/ and lib/."
1818
echo ""
1919
echo "Usage: $0 [--with-code-coverage] arch [arch...]"
20-
echo " where arch is one of: amd64, arm64, amd64-fips"
20+
echo " where arch is one of: amd64, arm64, amd64-fips, s390x"
2121
exit 1
2222
fi
2323

build/teamcity/cockroach/nightlies/roachtest_compile_component.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if [ "$#" -eq 0 ]; then
1515
echo ""
1616
echo "Usage: $(basename $0) [--with-coverage] <os/arch/component>"
1717
echo " where os is one of: linux"
18-
echo " arch is one of: amd64, arm64, amd64-fips"
18+
echo " arch is one of: amd64, arm64, amd64-fips, s390x"
1919
echo " component is one of: cockroach, cockroach-ea, workload, libgeos, roachtest"
2020
echo " --with-coverage enables go code coverage instrumentation (only applies to cockroach binaries)"
2121
exit 1
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2025 The Cockroach Authors.
4+
#
5+
# Use of this software is governed by the CockroachDB Software License
6+
# included in the /LICENSE file.
7+
8+
9+
set -exuo pipefail
10+
11+
dir="$(dirname $(dirname $(dirname $(dirname "${0}"))))"
12+
13+
source "$dir/teamcity-support.sh" # For $root
14+
source "$dir/teamcity-bazel-support.sh" # For run_bazel
15+
16+
BAZEL_SUPPORT_EXTRA_DOCKER_ARGS="-e LITERAL_ARTIFACTS_DIR=$root/artifacts -e IBM_APIKEY -e BUILD_VCS_NUMBER -e CLOUD -e COCKROACH_DEV_LICENSE -e TESTS -e COUNT -e GITHUB_API_TOKEN -e GITHUB_ORG -e GITHUB_REPO -e GOOGLE_EPHEMERAL_CREDENTIALS -e SLACK_TOKEN -e TC_BUILDTYPE_ID -e TC_BUILD_BRANCH -e TC_BUILD_ID -e TC_SERVER_URL -e SELECT_PROBABILITY -e COCKROACH_RANDOM_SEED -e ROACHTEST_ASSERTIONS_ENABLED_SEED -e ROACHTEST_FORCE_RUN_INVALID_RELEASE_BRANCH -e CLEAR_CLUSTER_CACHE -e ARM_PROBABILITY -e USE_SPOT -e SELECTIVE_TESTS -e SFUSER -e SFPASSWORD -e SIDE_EYE_API_TOKEN -e COCKROACH_EA_PROBABILITY -e EXPORT_OPENMETRICS -e ROACHPERF_OPENMETRICS_CREDENTIALS -e MVT_UPGRADE_PATH -e MVT_DEPLOYMENT_MODE -e EXTRA_ROACHTEST_ARGS" \
17+
run_bazel build/teamcity/cockroach/nightlies/roachtest_nightly_impl.sh

build/teamcity/cockroach/nightlies/roachtest_nightly_impl.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ if [[ ! -f ~/.ssh/id_rsa.pub ]]; then
2020
fi
2121

2222
arch=amd64
23-
if [[ ${FIPS_ENABLED:-0} == 1 ]]; then
23+
if [[ ${CLOUD} == "ibm" ]]; then
24+
arch=s390x
25+
elif [[ ${FIPS_ENABLED:-0} == 1 ]]; then
2426
arch=amd64-fips
2527
fi
2628
$root/build/teamcity/cockroach/nightlies/roachtest_compile_bits.sh $arch
27-
$root/build/teamcity/cockroach/nightlies/roachtest_compile_bits.sh arm64
29+
if [[ $arch != "s390x" ]]; then
30+
$root/build/teamcity/cockroach/nightlies/roachtest_compile_bits.sh arm64
31+
fi
2832

2933
artifacts=/artifacts
3034
source $root/build/teamcity/util/roachtest_util.sh

build/teamcity/util/roachtest_arch_util.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ function arch_to_config() {
1515
amd64-fips)
1616
echo "crosslinuxfips"
1717
;;
18+
s390x)
19+
echo "crosslinuxs390x"
20+
;;
1821
*)
1922
echo "Error: invalid arch '$1'" >&2
2023
exit 1

pkg/cmd/roachprod/cli/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func ValidateAndConfigure(cmd *cobra.Command, args []string) {
217217
if archOpt := cmd.Flags().Lookup("arch"); archOpt != nil && archOpt.Changed {
218218
arch := vm.CPUArch(strings.ToLower(archOpt.Value.String()))
219219

220-
if arch != vm.ArchAMD64 && arch != vm.ArchARM64 && arch != vm.ArchFIPS {
220+
if arch != vm.ArchAMD64 && arch != vm.ArchARM64 && arch != vm.ArchFIPS && arch != vm.ArchS390x {
221221
printErrAndExit(fmt.Errorf("unsupported architecture %q", arch))
222222
}
223223
if string(arch) != archOpt.Value.String() {

pkg/cmd/roachtest/cluster.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ func validateBinaryFormat(path string, arch vm.CPUArch, checkEA bool) (string, e
101101
} else if arch == vm.ArchAMD64 && !strings.Contains(fileFormat, "x86-64") && !strings.Contains(fileFormat, "x86_64") {
102102
// Otherwise, we expect a binary that was built for amd64.
103103
return "", errors.Newf("%s has incompatible architecture; want: %q, got: %q", abspath, arch, fileFormat)
104+
} else if arch == vm.ArchS390x && !strings.Contains(fileFormat, "ibm s/390") {
105+
return "", errors.Newf("%s has incompabile architecture; want: %q, got: %q", abspath, arch, fileFormat)
104106
}
105107
if arch == vm.ArchFIPS && strings.HasSuffix(abspath, "cockroach") {
106108
// Check that the binary is patched to use OpenSSL FIPS.
@@ -278,6 +280,8 @@ func initBinariesAndLibraries() {
278280
if string(defaultArch) != runtime.GOARCH {
279281
fmt.Printf("WARN: local cluster's architecture (%q) differs from default (%q)\n", runtime.GOARCH, defaultArch)
280282
}
283+
} else if roachtestflags.Cloud == spec.IBM {
284+
defaultArch = vm.ArchS390x
281285
}
282286
fmt.Printf("Locating and verifying binaries for os=%q, arch=%q\n", defaultOSName, defaultArch)
283287

@@ -356,7 +360,7 @@ func initBinariesAndLibraries() {
356360

357361
// In v20.2 or higher, optionally expect certain library files to exist.
358362
// Since they may not be found in older versions, do not hard error if they are not found.
359-
for _, arch := range []vm.CPUArch{vm.ArchAMD64, vm.ArchARM64, vm.ArchFIPS} {
363+
for _, arch := range []vm.CPUArch{vm.ArchAMD64, vm.ArchARM64, vm.ArchFIPS, vm.ArchS390x} {
360364
if roachtestflags.ARM64Probability == 0 && defaultArch != vm.ArchARM64 && arch == vm.ArchARM64 {
361365
// arm64 isn't used, skip finding libs for it.
362366
continue
@@ -541,7 +545,7 @@ func makeClusterName(name string) string {
541545
return vm.DNSSafeName(name)
542546
}
543547

544-
// MachineTypeToCPUs returns a CPU count for GCE, AWS, and Azure machine types.
548+
// MachineTypeToCPUs returns a CPU count for GCE, AWS, Azure and IBM machine types.
545549
// -1 is returned for unknown machine types.
546550
func MachineTypeToCPUs(s string) int {
547551
{
@@ -564,6 +568,23 @@ func MachineTypeToCPUs(s string) int {
564568
}
565569
}
566570

571+
{
572+
// IBM machine types.
573+
var v int
574+
// bz2 is the balanced IBM Z machine type family.
575+
if _, err := fmt.Sscanf(s, "bz2-%dx", &v); err == nil {
576+
return v
577+
}
578+
// bz2 is the compute optimized IBM Z machine type family.
579+
if _, err := fmt.Sscanf(s, "cz2-%dx", &v); err == nil {
580+
return v
581+
}
582+
// bz2 is the memory optimized IBM Z machine type family.
583+
if _, err := fmt.Sscanf(s, "mx2-%dx", &v); err == nil {
584+
return v
585+
}
586+
}
587+
567588
typeAndSize := strings.Split(s, ".")
568589

569590
if len(typeAndSize) == 2 {
@@ -3229,6 +3250,12 @@ func archForTest(ctx context.Context, l *logger.Logger, testSpec registry.TestSp
32293250
return testSpec.Cluster.Arch
32303251
}
32313252

3253+
if roachtestflags.Cloud == spec.IBM {
3254+
// N.B. IBM only supports S390x on the "s390x" architecture.
3255+
l.PrintfCtx(ctx, "IBM Cloud: forcing arch=%q (only supported), %s", vm.ArchS390x, testSpec.Name)
3256+
return vm.ArchS390x
3257+
}
3258+
32323259
// CPU architecture is unspecified, choose one according to the
32333260
// probability distribution.
32343261
var arch vm.CPUArch

pkg/cmd/roachtest/cluster_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,134 @@ func TestAzureMachineType(t *testing.T) {
643643
require.Error(t, err2)
644644
}
645645

646+
func TestIBMMachineType(t *testing.T) {
647+
testCases := []struct {
648+
name string
649+
cpus int
650+
mem spec.MemPerCPU
651+
arch vm.CPUArch
652+
expectedMachine string
653+
expectedArch vm.CPUArch
654+
expectedError string
655+
}{}
656+
657+
// Helper function to generate the expected machine type string
658+
ibmMachineType := func(series string, cpus int, ramRatio int) string {
659+
return fmt.Sprintf("%s-%dx%d", series, cpus, cpus*ramRatio)
660+
}
661+
662+
// IBM Z only supports s390x architecture
663+
arch := vm.ArchS390x
664+
665+
// Add test cases for each memory configuration
666+
addTestCases := func(mem spec.MemPerCPU) {
667+
var series string
668+
var ramRatio int
669+
670+
switch mem {
671+
case spec.Auto, spec.Standard:
672+
series = "bz2" // balanced
673+
ramRatio = 4
674+
case spec.High:
675+
series = "mz2" // memory optimized
676+
ramRatio = 8
677+
case spec.Low:
678+
series = "cz2" // compute optimized
679+
ramRatio = 2
680+
}
681+
682+
// IBM Z only supports 2, 4, 8, or 16 CPUs
683+
for _, cpus := range []int{2, 4, 8, 16} {
684+
testName := fmt.Sprintf("valid_%dcpu_%s", cpus, mem)
685+
testCases = append(testCases, struct {
686+
name string
687+
cpus int
688+
mem spec.MemPerCPU
689+
arch vm.CPUArch
690+
expectedMachine string
691+
expectedArch vm.CPUArch
692+
expectedError string
693+
}{
694+
name: testName,
695+
cpus: cpus,
696+
mem: mem,
697+
arch: arch,
698+
expectedMachine: ibmMachineType(series, cpus, ramRatio),
699+
expectedArch: arch,
700+
expectedError: "",
701+
})
702+
}
703+
704+
// Add test cases for unsupported CPU counts
705+
invalidCPUs := []int{1, 6, 10, 32, 96, 128}
706+
for _, cpus := range invalidCPUs {
707+
testName := fmt.Sprintf("invalid_%dcpu_%s", cpus, mem)
708+
testCases = append(testCases, struct {
709+
name string
710+
cpus int
711+
mem spec.MemPerCPU
712+
arch vm.CPUArch
713+
expectedMachine string
714+
expectedArch vm.CPUArch
715+
expectedError string
716+
}{
717+
name: testName,
718+
cpus: cpus,
719+
mem: mem,
720+
arch: arch,
721+
expectedMachine: "",
722+
expectedArch: arch,
723+
expectedError: fmt.Sprintf("invalid number of cpus %d for IBM", cpus),
724+
})
725+
}
726+
}
727+
728+
// Add test cases for each memory configuration
729+
for _, mem := range []spec.MemPerCPU{spec.Auto, spec.Standard, spec.High, spec.Low} {
730+
addTestCases(mem)
731+
}
732+
733+
// Add test cases for unsupported architectures
734+
for _, invalidArch := range []vm.CPUArch{vm.ArchAMD64, vm.ArchARM64, vm.ArchFIPS} {
735+
testName := fmt.Sprintf("invalid_arch_%s", invalidArch)
736+
testCases = append(testCases, struct {
737+
name string
738+
cpus int
739+
mem spec.MemPerCPU
740+
arch vm.CPUArch
741+
expectedMachine string
742+
expectedArch vm.CPUArch
743+
expectedError string
744+
}{
745+
name: testName,
746+
cpus: 4,
747+
mem: spec.Auto,
748+
arch: invalidArch,
749+
expectedMachine: "",
750+
expectedArch: invalidArch,
751+
expectedError: fmt.Sprintf("invalid architecture %q for IBM", invalidArch),
752+
})
753+
}
754+
755+
for _, tc := range testCases {
756+
t.Run(tc.name, func(t *testing.T) {
757+
machineType, selectedArch, err := spec.SelectIBMMachineType(tc.cpus, tc.mem, tc.arch)
758+
759+
if tc.expectedError != "" {
760+
// We expect a specific error
761+
require.Error(t, err)
762+
require.Equal(t, tc.expectedError, err.Error())
763+
require.Equal(t, tc.expectedMachine, machineType)
764+
require.Equal(t, tc.expectedArch, selectedArch)
765+
} else {
766+
require.NoError(t, err)
767+
require.Equal(t, tc.expectedMachine, machineType)
768+
require.Equal(t, tc.expectedArch, selectedArch)
769+
}
770+
})
771+
}
772+
}
773+
646774
func TestMachineTypes(t *testing.T) {
647775
datadriven.Walk(t, datapathutils.TestDataPath(t, "cluster_test"), func(t *testing.T, path string) {
648776
datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {

pkg/cmd/roachtest/registry/test_spec.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ type CloudSet struct {
293293
}
294294

295295
// AllClouds contains all clouds.
296-
var AllClouds = Clouds(spec.Local, spec.GCE, spec.AWS, spec.Azure)
296+
var AllClouds = Clouds(spec.Local, spec.GCE, spec.AWS, spec.Azure, spec.IBM)
297297

298298
// AllExceptLocal contains all clouds except Local.
299299
var AllExceptLocal = AllClouds.NoLocal()
@@ -304,6 +304,9 @@ var AllExceptAWS = AllClouds.NoAWS()
304304
// AllExceptAzure contains all clouds except Azure.
305305
var AllExceptAzure = AllClouds.NoAzure()
306306

307+
// AllExceptIBM contains all clouds except IBM.
308+
var AllExceptIBM = AllClouds.NoIBM()
309+
307310
// OnlyAWS contains only the AWS cloud.
308311
var OnlyAWS = Clouds(spec.AWS)
309312

@@ -313,14 +316,17 @@ var OnlyGCE = Clouds(spec.GCE)
313316
// OnlyAzure contains only the Azure cloud.
314317
var OnlyAzure = Clouds(spec.Azure)
315318

319+
// OnlyIBM contains only the IBM cloud.
320+
var OnlyIBM = Clouds(spec.IBM)
321+
316322
// OnlyLocal contains only the Local cloud.
317323
var OnlyLocal = Clouds(spec.Local)
318324

319325
// CloudsWithServiceRegistration contains clouds that support service registration.
320326
var CloudsWithServiceRegistration = Clouds(spec.Local, spec.GCE)
321327

322328
// Clouds creates a CloudSet for the given clouds. Cloud names must be one of:
323-
// spec.Local, spec.GCE, spec.AWS, spec.Azure.
329+
// spec.Local, spec.GCE, spec.AWS, spec.Azure, spec.IBM.
324330
func Clouds(clouds ...spec.Cloud) CloudSet {
325331
return CloudSet{m: addToSet(nil, clouds...)}
326332
}
@@ -340,6 +346,11 @@ func (cs CloudSet) NoAzure() CloudSet {
340346
return CloudSet{m: removeFromSet(cs.m, spec.Azure)}
341347
}
342348

349+
// NoIBM removes the IBM cloud and returns the new set.
350+
func (cs CloudSet) NoIBM() CloudSet {
351+
return CloudSet{m: removeFromSet(cs.m, spec.IBM)}
352+
}
353+
343354
// Remove removes all clouds passed in and returns the new set.
344355
func (cs CloudSet) Remove(clouds CloudSet) CloudSet {
345356
copyCs := CloudSet{m: cs.m}

pkg/cmd/roachtest/registry/test_spec_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ func TestCloudSet(t *testing.T) {
1616
expect := func(c CloudSet, exp string) {
1717
require.Equal(t, exp, c.String())
1818
}
19-
expect(AllClouds, "local,gce,aws,azure")
20-
expect(AllExceptAWS, "local,gce,azure")
21-
expect(AllExceptLocal, "gce,aws,azure")
22-
expect(AllExceptLocal.NoAWS(), "gce,azure")
23-
expect(AllClouds.NoAWS().NoAzure(), "local,gce")
19+
expect(AllClouds, "local,gce,aws,azure,ibm")
20+
expect(AllExceptAWS, "local,gce,azure,ibm")
21+
expect(AllExceptLocal, "gce,aws,azure,ibm")
22+
expect(AllExceptLocal.NoAWS(), "gce,azure,ibm")
23+
expect(AllClouds.NoAWS().NoAzure(), "local,gce,ibm")
24+
expect(AllClouds.NoIBM(), "local,gce,aws,azure")
2425

2526
require.True(t, AllExceptAWS.Contains(spec.GCE))
2627
require.True(t, AllExceptAWS.Contains(spec.Local))

0 commit comments

Comments
 (0)