Skip to content

Commit c5a1aad

Browse files
Use IRSO in e2e tests
Signed-off-by: Muhammad Adil Ghaffar <[email protected]>
1 parent 226c65d commit c5a1aad

40 files changed

+642
-853
lines changed

scripts/ci-e2e.sh

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,14 @@ export KUBERNETES_VERSION=${KUBERNETES_VERSION}
4949
export IMAGE_OS=${IMAGE_OS}
5050
export FORCE_REPO_UPDATE="false"
5151
export SKIP_NODE_IMAGE_PREPULL="true"
52-
export USE_IRSO="${USE_IRSO:-false}"
52+
export IPA_BASEURI=https://artifactory.nordix.org/artifactory/openstack-remote-cache/ironic-python-agent/dib
5353
EOF
5454

55+
# Set USE_IRSO only when IMAGE_OS is not ubuntu
56+
if [[ "${IMAGE_OS}" != "ubuntu" ]]; then
57+
echo 'export USE_IRSO="true"' >> "${M3_DEV_ENV_PATH}/config_${USER}.sh"
58+
fi
59+
5560
# Always set DATE variable for nightly builds because it is needed to form
5661
# the URL for CAPI nightly build components in e2e_conf.yaml even if not used.
5762
DATE=$(date '+%Y%m%d' -d '1 day ago')
@@ -162,44 +167,63 @@ kustomize_envsubst() {
162167
echo "envsubst applied to ${file}"
163168
}
164169

170+
yaml_envsubst() {
171+
local kustomize_dir="$1"
172+
173+
for file in "${kustomize_dir}"/*.yaml; do
174+
if [[ -f "${file}" ]]; then
175+
local tmp_file
176+
tmp_file=$(mktemp)
177+
envsubst < "${file}" > "${tmp_file}" && mv "${tmp_file}" "${file}"
178+
fi
179+
done
180+
}
181+
165182
# Generate credentials
166183
BMO_OVERLAYS=(
167-
"${REPO_ROOT}/test/e2e/data/bmo-deployment/overlays/release-0.9"
168184
"${REPO_ROOT}/test/e2e/data/bmo-deployment/overlays/release-0.10"
169185
"${REPO_ROOT}/test/e2e/data/bmo-deployment/overlays/release-0.11"
170186
"${REPO_ROOT}/test/e2e/data/bmo-deployment/overlays/pr-test"
171187
"${REPO_ROOT}/test/e2e/data/bmo-deployment/overlays/release-latest"
172188
)
173-
IRONIC_OVERLAYS=(
174-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/release-27.0"
175-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/release-29.0"
176-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/release-31.0"
177-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/release-32.0"
178-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/pr-test"
179-
"${REPO_ROOT}/test/e2e/data/ironic-deployment/overlays/release-latest"
189+
IRSO_IRONIC_OVERLAYS=(
190+
"${REPO_ROOT}/test/e2e/data/ironic-standalone-operator/ironic/overlays/release-31.0"
191+
"${REPO_ROOT}/test/e2e/data/ironic-standalone-operator/ironic/overlays/release-32.0"
192+
"${REPO_ROOT}/test/e2e/data/ironic-standalone-operator/ironic/overlays/pr-test"
193+
"${REPO_ROOT}/test/e2e/data/ironic-standalone-operator/ironic/overlays/latest"
180194
)
181195

182196
# Update BMO and Ironic images in kustomization.yaml files to use the same image that was used before pivot in the metal3-dev-env
183197
case "${REPO_NAME:-}" in
184198
baremetal-operator)
185199
# shellcheck disable=SC2034
186-
BARE_METAL_OPERATOR_IMAGE="${REGISTRY}/localimages/tested_repo:latest"
200+
export BARE_METAL_OPERATOR_IMAGE="${REGISTRY}/localimages/tested_repo:latest"
187201
;;
188202

189203
ironic-image)
190204
# shellcheck disable=SC2034
191-
IRONIC_IMAGE="${REGISTRY}/localimages/tested_repo:latest"
205+
export IRONIC_IMAGE="${REGISTRY}/localimages/tested_repo:latest"
192206
;;
193207
esac
194208

209+
IRONIC_IMAGE="${IRONIC_IMAGE:-quay.io/metal3-io/ironic:main}"
195210
update_kustomize_image quay.io/metal3-io/baremetal-operator BARE_METAL_OPERATOR_IMAGE "${REPO_ROOT}"/test/e2e/data/bmo-deployment/overlays/pr-test
196-
update_kustomize_image quay.io/metal3-io/ironic IRONIC_IMAGE "${REPO_ROOT}"/test/e2e/data/ironic-deployment/overlays/pr-test
197211

198212
# Apply envsubst to kustomization.yaml files in BMO and Ironic overlays
199213
kustomize_envsubst "${REPO_ROOT}"/test/e2e/data/bmo-deployment/overlays/pr-test
200-
kustomize_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-deployment/overlays/pr-test
214+
kustomize_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/base
215+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/base/
216+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/basic-auth/
217+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/tls/
218+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/operator
219+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/overlays/pr-test/
220+
yaml_envsubst "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/overlays/latest/
221+
222+
for overlay in "${IRSO_IRONIC_OVERLAYS[@]}"; do
223+
kustomize_envsubst "${overlay}"
224+
done
201225

202-
# Create usernames and passwords and other files related to ironi basic auth if they
226+
# Create usernames and passwords and other files related to ironic basic auth if they
203227
# are missing
204228
if [[ "${IRONIC_BASIC_AUTH}" == "true" ]]; then
205229
IRONIC_AUTH_DIR="${IRONIC_AUTH_DIR:-${IRONIC_DATA_DIR}/auth}"
@@ -222,15 +246,17 @@ if [[ "${IRONIC_BASIC_AUTH}" == "true" ]]; then
222246
IRONIC_PASSWORD="$(cat "${IRONIC_AUTH_DIR}/ironic-password")"
223247
fi
224248
fi
225-
IRONIC_INSPECTOR_USERNAME="${IRONIC_INSPECTOR_USERNAME:-${IRONIC_USERNAME}}"
226-
IRONIC_INSPECTOR_PASSWORD="${IRONIC_INSPECTOR_PASSWORD:-${IRONIC_PASSWORD}}"
227249

228250
export IRONIC_USERNAME
229251
export IRONIC_PASSWORD
230-
export IRONIC_INSPECTOR_USERNAME
231-
export IRONIC_INSPECTOR_PASSWORD
232252
fi
233253

254+
echo "${IRONIC_USERNAME}" > "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/basic-auth/ironic-username
255+
echo "${IRONIC_PASSWORD}" > "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/basic-auth/ironic-password
256+
257+
cp "${IRONIC_KEY_FILE}" "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/tls/
258+
cp "${IRONIC_CERT_FILE}" "${REPO_ROOT}"/test/e2e/data/ironic-standalone-operator/ironic/components/tls/
259+
234260
for overlay in "${BMO_OVERLAYS[@]}"; do
235261
echo "${IRONIC_USERNAME}" > "${overlay}/ironic-username"
236262
echo "${IRONIC_PASSWORD}" > "${overlay}/ironic-password"
@@ -240,23 +266,13 @@ for overlay in "${BMO_OVERLAYS[@]}"; do
240266
fi
241267
done
242268

243-
for overlay in "${IRONIC_OVERLAYS[@]}"; do
244-
echo "IRONIC_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_USERNAME}" "${IRONIC_PASSWORD}")" > \
245-
"${overlay}/ironic-htpasswd"
246-
envsubst < "${REPO_ROOT}/test/e2e/data/ironic-deployment/components/basic-auth/ironic-auth-config-tpl" > \
247-
"${overlay}/ironic-auth-config"
248-
IRONIC_INSPECTOR_AUTH_CONFIG_TPL="/tmp/ironic-inspector-auth-config-tpl"
249-
curl -o "${IRONIC_INSPECTOR_AUTH_CONFIG_TPL}" https://raw.githubusercontent.com/metal3-io/baremetal-operator/release-0.5/ironic-deployment/components/basic-auth/ironic-inspector-auth-config-tpl
250-
envsubst < "${IRONIC_INSPECTOR_AUTH_CONFIG_TPL}" > \
251-
"${overlay}/ironic-inspector-auth-config"
252-
echo "INSPECTOR_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_INSPECTOR_USERNAME}" \
253-
"${IRONIC_INSPECTOR_PASSWORD}")" > "${overlay}/ironic-inspector-htpasswd"
254-
done
255-
256269
# run e2e tests
257270
if [[ -n "${CLUSTER_TOPOLOGY:-}" ]]; then
258271
export CLUSTER_TOPOLOGY=true
259272
make e2e-clusterclass-tests
260273
else
261274
make e2e-tests
262275
fi
276+
277+
mkdir -p "/tmp/manifests/irso_yamls"
278+
cp -r "${REPO_ROOT}/test/e2e/data/ironic-standalone-operator/" "/tmp/manifests/irso_yamls/"

test/e2e/common.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
bmov1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
2525
infrav1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
2626
ipamv1 "github.com/metal3-io/ip-address-manager/api/v1alpha1"
27+
irsov1alpha1 "github.com/metal3-io/ironic-standalone-operator/api/v1alpha1"
2728
. "github.com/onsi/ginkgo/v2"
2829
. "github.com/onsi/gomega"
2930
"github.com/pkg/errors"
@@ -1227,3 +1228,226 @@ func IsMetal3DataCountEqualToMachineCount(ctx context.Context, c client.Client,
12271228

12281229
return len(m3DataList.Items) == len(machineList.Items)
12291230
}
1231+
1232+
type InstallIRSOInput struct {
1233+
E2EConfig *clusterctl.E2EConfig
1234+
ClusterProxy framework.ClusterProxy
1235+
IronicNamespace string
1236+
ClusterName string
1237+
IrsoOperatorKustomize string
1238+
IronicKustomize string
1239+
}
1240+
1241+
func InstallIRSO(ctx context.Context, input InstallIRSOInput) error {
1242+
By("Create Ironic namespace")
1243+
targetClusterClientSet := input.ClusterProxy.GetClientSet()
1244+
ironicNamespaceObj := &corev1.Namespace{
1245+
ObjectMeta: metav1.ObjectMeta{
1246+
Name: input.IronicNamespace,
1247+
},
1248+
}
1249+
_, err := targetClusterClientSet.CoreV1().Namespaces().Create(ctx, ironicNamespaceObj, metav1.CreateOptions{})
1250+
if err != nil {
1251+
if apierrors.IsAlreadyExists(err) {
1252+
Logf("Ironic namespace %q already exists, continuing", input.IronicNamespace)
1253+
} else {
1254+
Expect(err).ToNot(HaveOccurred(), "Unable to create the Ironic namespace")
1255+
}
1256+
}
1257+
1258+
irsoDeployLogFolder := filepath.Join(os.TempDir(), "target_cluster_logs", "ironic-deploy-logs", input.ClusterProxy.GetName())
1259+
By(fmt.Sprintf("Installing IRSO from kustomization %s on the target cluster", input.IrsoOperatorKustomize))
1260+
err = BuildAndApplyKustomization(ctx, &BuildAndApplyKustomizationInput{
1261+
Kustomization: input.IrsoOperatorKustomize,
1262+
ClusterProxy: input.ClusterProxy,
1263+
WaitForDeployment: true,
1264+
WatchDeploymentLogs: true,
1265+
LogPath: irsoDeployLogFolder,
1266+
DeploymentName: "ironic-standalone-operator-controller-manager",
1267+
DeploymentNamespace: IRSOControllerNameSpace,
1268+
WaitIntervals: input.E2EConfig.GetIntervals("default", "wait-deployment"),
1269+
})
1270+
Expect(err).NotTo(HaveOccurred())
1271+
1272+
By("Install Ironic CR in the target cluster")
1273+
By(fmt.Sprintf("Installing Ironic from kustomization %s on the target cluster", input.IronicKustomize))
1274+
err = BuildAndApplyKustomization(ctx, &BuildAndApplyKustomizationInput{
1275+
Kustomization: input.IronicKustomize,
1276+
ClusterProxy: input.ClusterProxy,
1277+
WaitForDeployment: false,
1278+
WatchDeploymentLogs: false,
1279+
})
1280+
Expect(err).NotTo(HaveOccurred())
1281+
1282+
WaitForIronicReady(ctx, WaitForIronicInput{
1283+
Client: input.ClusterProxy.GetClient(),
1284+
Name: "ironic",
1285+
Namespace: input.IronicNamespace,
1286+
Intervals: input.E2EConfig.GetIntervals("default", "wait-deployment"),
1287+
})
1288+
return nil
1289+
}
1290+
1291+
// WaitForIronicReady waits until the given Ironic resource has Ready condition = True.
1292+
func WaitForIronicReady(ctx context.Context, input WaitForIronicInput) {
1293+
Logf("Waiting for Ironic %q to be Ready", input.Name)
1294+
1295+
Eventually(func(g Gomega) {
1296+
ironic := &irsov1alpha1.Ironic{}
1297+
err := input.Client.Get(ctx, client.ObjectKey{
1298+
Namespace: input.Namespace,
1299+
Name: input.Name,
1300+
}, ironic)
1301+
g.Expect(err).ToNot(HaveOccurred())
1302+
1303+
ready := false
1304+
for _, cond := range ironic.Status.Conditions {
1305+
if cond.Type == string(irsov1alpha1.IronicStatusReady) && cond.Status == metav1.ConditionTrue && ironic.Status.InstalledVersion != "" {
1306+
ready = true
1307+
break
1308+
}
1309+
}
1310+
g.Expect(ready).To(BeTrue(), "Ironic %q is not Ready yet", input.Name)
1311+
}, input.Intervals...).Should(Succeed())
1312+
1313+
Logf("Ironic %q is Ready", input.Name)
1314+
}
1315+
1316+
// WaitForIronicInput bundles the parameters for WaitForIronicReady.
1317+
type WaitForIronicInput struct {
1318+
Client client.Client
1319+
Name string
1320+
Namespace string
1321+
Intervals []interface{} // e.g. []interface{}{time.Minute * 15, time.Second * 5}
1322+
}
1323+
1324+
// InstallBMOInput bundles parameters for InstallBMO.
1325+
type InstallBMOInput struct {
1326+
E2EConfig *clusterctl.E2EConfig
1327+
ClusterProxy framework.ClusterProxy
1328+
Namespace string // Namespace where BMO will run (shared with Ironic)
1329+
BmoKustomization string // Kustomization path or URL for BMO manifests
1330+
ClusterName string // For legacy log folder naming (deprecated, prefer LogFolder)
1331+
LogFolder string // Optional explicit log folder; if empty a default is derived
1332+
WaitIntervals []any // Optional override; if nil uses default e2e config intervals
1333+
WatchLogs bool // Whether to watch deployment logs
1334+
}
1335+
1336+
// InstallBMO installs the Baremetal Operator (BMO) in the target cluster similar to InstallIRSO.
1337+
func InstallBMO(ctx context.Context, input InstallBMOInput) error {
1338+
By("Ensure BMO namespace exists")
1339+
clientset := input.ClusterProxy.GetClientSet()
1340+
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: input.Namespace}}
1341+
_, err := clientset.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
1342+
if err != nil {
1343+
if apierrors.IsAlreadyExists(err) {
1344+
Logf("Namespace %q already exists, continuing", input.Namespace)
1345+
} else {
1346+
return fmt.Errorf("failed creating namespace %q: %w", input.Namespace, err)
1347+
}
1348+
}
1349+
1350+
// Determine log folder
1351+
logFolder := input.LogFolder
1352+
if logFolder == "" {
1353+
logFolder = filepath.Join(os.TempDir(), "target_cluster_logs", "bmo-deploy-logs", input.ClusterProxy.GetName())
1354+
}
1355+
intervals := input.WaitIntervals
1356+
if intervals == nil {
1357+
intervals = input.E2EConfig.GetIntervals("default", "wait-deployment")
1358+
}
1359+
1360+
By(fmt.Sprintf("Installing BMO from kustomization %s on the target cluster", input.BmoKustomization))
1361+
err = BuildAndApplyKustomization(ctx, &BuildAndApplyKustomizationInput{
1362+
Kustomization: input.BmoKustomization,
1363+
ClusterProxy: input.ClusterProxy,
1364+
WaitForDeployment: true,
1365+
WatchDeploymentLogs: input.WatchLogs,
1366+
LogPath: logFolder,
1367+
DeploymentName: "baremetal-operator-controller-manager",
1368+
DeploymentNamespace: input.Namespace,
1369+
WaitIntervals: intervals,
1370+
})
1371+
if err != nil {
1372+
return fmt.Errorf("failed installing BMO: %w", err)
1373+
}
1374+
1375+
By("BMO deployment applied and available")
1376+
return nil
1377+
}
1378+
1379+
type UninstallIRSOAndIronicResourcesInput struct {
1380+
E2EConfig *clusterctl.E2EConfig
1381+
ClusterProxy framework.ClusterProxy
1382+
IronicNamespace string
1383+
IrsoOperatorKustomize string
1384+
IronicKustomization string
1385+
IsDevEnvUninstall bool
1386+
}
1387+
1388+
// UninstallIRSOAndIronicResources removes the IRSO deployment, Ironic CR, IronicDatabase CR (if present), and related secrets.
1389+
func UninstallIRSOAndIronicResources(ctx context.Context, input UninstallIRSOAndIronicResourcesInput) error {
1390+
if input.IsDevEnvUninstall {
1391+
ironicObj := &irsov1alpha1.Ironic{
1392+
ObjectMeta: metav1.ObjectMeta{
1393+
Name: "ironic",
1394+
Namespace: input.IronicNamespace,
1395+
},
1396+
}
1397+
err := input.ClusterProxy.GetClient().Delete(ctx, ironicObj)
1398+
Expect(err).ToNot(HaveOccurred(), "Failed to delete Ironic")
1399+
} else {
1400+
By("Remove Ironic CR in the cluster " + input.ClusterProxy.GetName())
1401+
err := BuildAndRemoveKustomization(ctx, input.IronicKustomization, input.ClusterProxy)
1402+
Expect(err).NotTo(HaveOccurred())
1403+
}
1404+
1405+
By("Remove Ironic Service Deployment in the cluster " + input.ClusterProxy.GetName())
1406+
RemoveDeployment(ctx, func() RemoveDeploymentInput {
1407+
return RemoveDeploymentInput{
1408+
ManagementCluster: input.ClusterProxy,
1409+
Namespace: input.IronicNamespace,
1410+
Name: "ironic-service",
1411+
}
1412+
})
1413+
1414+
if input.IsDevEnvUninstall {
1415+
By("Remove Ironic Standalone Operator Deployment in the cluster " + input.ClusterProxy.GetName())
1416+
RemoveDeployment(ctx, func() RemoveDeploymentInput {
1417+
return RemoveDeploymentInput{
1418+
ManagementCluster: input.ClusterProxy,
1419+
Namespace: IRSOControllerNameSpace,
1420+
Name: "ironic-standalone-operator-controller-manager",
1421+
}
1422+
})
1423+
} else {
1424+
By("Uninstalling IRSO operator via kustomize")
1425+
err := BuildAndRemoveKustomization(ctx, input.IrsoOperatorKustomize, input.ClusterProxy)
1426+
Expect(err).NotTo(HaveOccurred())
1427+
}
1428+
1429+
clusterClient := input.ClusterProxy.GetClient()
1430+
1431+
// Delete secrets
1432+
secretNames := []string{"ironic-auth", "ironic-cert", "ironic-cacert"}
1433+
for _, s := range secretNames {
1434+
Byf("Deleting secret %s", s)
1435+
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: s, Namespace: input.IronicNamespace}}
1436+
_ = clusterClient.Delete(ctx, secret)
1437+
}
1438+
1439+
// Wait for secrets to be deleted
1440+
By("Waiting for Ironic secrets to be deleted")
1441+
Eventually(func() bool {
1442+
for _, s := range secretNames {
1443+
errS := clusterClient.Get(ctx, client.ObjectKey{Name: s, Namespace: input.IronicNamespace}, &corev1.Secret{})
1444+
if errS == nil || !apierrors.IsNotFound(errS) {
1445+
return false
1446+
}
1447+
}
1448+
return true
1449+
}, input.E2EConfig.GetIntervals("default", "wait-delete-ironic")...).Should(BeTrue(), "IRSO/Ironic resources not fully deleted")
1450+
1451+
By("IRSO and Ironic resources uninstalled")
1452+
return nil
1453+
}

0 commit comments

Comments
 (0)