From e88e675713ac93188ca7035c5340217a13d18458 Mon Sep 17 00:00:00 2001 From: Brandon Palm Date: Wed, 20 May 2026 10:37:39 -0500 Subject: [PATCH] feat: add context.Context to Execute and Ops interfaces Replace exec.Command with exec.CommandContext so external commands respect context cancellation and timeouts. Propagate ctx through the Ops interface, downstream consumers, controllers, and CLI commands. - Add ctx as first parameter to Execute, ExecuteWithLiveLogger, and all executor implementations - Thread ctx through ~25 Ops methods and all callers - Use cmd.Context() in ipconfig CLI commands instead of context.Background() - Add timeouts for startup and signal-handler probes - Use context.WithoutCancel for deferred cleanup operations - Handle errors from IsOstreeAdminSetDefaultFeatureEnabled - Log podman rmi errors instead of silently discarding - Regenerate all mocks --- .konflux/overlay/release.in.yaml | 2 +- controllers/ibu_controller.go | 2 +- controllers/ibu_controller_test.go | 2 +- controllers/idle_handlers.go | 18 +- controllers/idle_handlers_test.go | 22 +- controllers/ipc_config_handlers.go | 19 +- controllers/ipc_config_handlers_test.go | 48 ++-- controllers/ipc_controller.go | 26 +-- controllers/ipc_controller_test.go | 94 ++++---- controllers/ipc_idle_handlers.go | 20 +- controllers/ipc_idle_handlers_test.go | 36 +-- controllers/ipc_rollback_handlers.go | 21 +- controllers/ipc_rollback_handlers_test.go | 42 ++-- controllers/prep_handlers.go | 11 +- controllers/rollback_handlers.go | 21 +- controllers/seedgen_controller.go | 37 +-- controllers/upgrade_handlers.go | 34 +-- controllers/upgrade_handlers_test.go | 10 +- internal/imagemgmt/imagemgmt.go | 37 +-- internal/imagemgmt/imagemgmt_test.go | 21 +- internal/imagemgmt/mock_imagemgmt.go | 33 +-- internal/ostreeclient/mock_ostreeclient.go | 68 +++--- internal/ostreeclient/ostreeclient.go | 59 ++--- internal/precache/workload/pullImages.go | 31 +-- internal/prep/prep.go | 23 +- internal/reboot/mock_reboot.go | 49 ++-- internal/reboot/reboot.go | 95 ++++---- internal/reboot/reboot_test.go | 5 +- lca-cli/cmd/create.go | 9 +- lca-cli/cmd/ibi.go | 24 +- lca-cli/cmd/ibuPrecacheWorkload.go | 10 +- lca-cli/cmd/ibuStaterootSetup.go | 12 +- lca-cli/cmd/ipconfig/postpivot.go | 6 +- lca-cli/cmd/ipconfig/prepivot.go | 38 +-- lca-cli/cmd/ipconfig/rollback.go | 10 +- lca-cli/cmd/postpivotcmd.go | 8 +- lca-cli/cmd/restore.go | 8 +- lca-cli/ibi-preparation/ibipreparation.go | 39 ++-- .../ibi-preparation/ibipreparation_test.go | 51 ++-- lca-cli/initmonitor/initmonitor.go | 5 +- lca-cli/ipconfig/postpivot.go | 20 +- lca-cli/ipconfig/postpivot_test.go | 66 +++--- lca-cli/ipconfig/prepivot.go | 79 ++++--- lca-cli/ipconfig/prepivot_test.go | 180 +++++++-------- lca-cli/ipconfig/rollback.go | 11 +- lca-cli/ipconfig/rollback_test.go | 33 +-- lca-cli/ops/execute.go | 41 ++-- lca-cli/ops/mock_execute.go | 17 +- lca-cli/ops/mock_ops.go | 218 +++++++++--------- lca-cli/ops/ops.go | 200 ++++++++-------- lca-cli/ostreeclient/mock_rpmostreeclient.go | 82 +++---- lca-cli/ostreeclient/rpmostreeclient.go | 61 ++--- lca-cli/postpivot/postpivot.go | 68 +++--- lca-cli/postpivot/postpivot_test.go | 38 +-- lca-cli/seedcreator/seedcreator.go | 77 +++---- lca-cli/seedrestoration/seedrestoration.go | 15 +- main/main.go | 5 +- 57 files changed, 1197 insertions(+), 1120 deletions(-) diff --git a/.konflux/overlay/release.in.yaml b/.konflux/overlay/release.in.yaml index 327fe8ee51..58ab21f4a4 100644 --- a/.konflux/overlay/release.in.yaml +++ b/.konflux/overlay/release.in.yaml @@ -12,6 +12,6 @@ variables: manager_version: "lifecycle-agent.v5.0.0" # min_kube_version should match ocp # https://access.redhat.com/solutions/4870701 - min_kube_version: "1.35.0" + min_kube_version: "1.32.0" recert_image: PLACEHOLDER_RECERT_IMAGE version: "5.0.0" diff --git a/controllers/ibu_controller.go b/controllers/ibu_controller.go index f29ab29bea..5c37309fc0 100644 --- a/controllers/ibu_controller.go +++ b/controllers/ibu_controller.go @@ -182,7 +182,7 @@ func (r *ImageBasedUpgradeReconciler) Reconcile(ctx context.Context, req ctrl.Re } var isAfterPivot bool - isAfterPivot, err = r.RPMOstreeClient.IsStaterootBooted(common.GetDesiredStaterootName(ibu)) + isAfterPivot, err = r.RPMOstreeClient.IsStaterootBooted(ctx, common.GetDesiredStaterootName(ibu)) if err != nil { return } diff --git a/controllers/ibu_controller_test.go b/controllers/ibu_controller_test.go index d8322d0c09..0d57af9d87 100644 --- a/controllers/ibu_controller_test.go +++ b/controllers/ibu_controller_test.go @@ -875,7 +875,7 @@ func TestImageBasedUpgradeReconciler_Reconcile(t *testing.T) { ctrl := gomock.NewController(t) mockClient := rpmostreeclient.NewMockIClient(ctrl) - mockClient.EXPECT().IsStaterootBooted("rhcos_").Return(false, nil) + mockClient.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos_").Return(false, nil) r := &ImageBasedUpgradeReconciler{ Client: fakeClient, diff --git a/controllers/idle_handlers.go b/controllers/idle_handlers.go index ee6b7715e8..054a9f2761 100644 --- a/controllers/idle_handlers.go +++ b/controllers/idle_handlers.go @@ -213,7 +213,7 @@ func (r *ImageBasedUpgradeReconciler) cleanupStateroot(ctx context.Context) erro } r.Log.Info("Cleaning up unbooted stateroot resources") - if err := CleanupUnbootedStateroots(r.Log, r.Ops, r.OstreeClient, r.RPMOstreeClient); err != nil { + if err := CleanupUnbootedStateroots(ctx, r.Log, r.Ops, r.OstreeClient, r.RPMOstreeClient); err != nil { return fmt.Errorf("failed to clean up host stateroot resources: %w", err) } @@ -231,8 +231,8 @@ func cleanupIBUFiles() error { return nil } -func CleanupUnbootedStateroots(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.IClient, rpmOstreeClient rpmostreeclient.IClient) error { - status, err := rpmOstreeClient.QueryStatus() +func CleanupUnbootedStateroots(ctx context.Context, log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.IClient, rpmOstreeClient rpmostreeclient.IClient) error { + status, err := rpmOstreeClient.QueryStatus(ctx) if err != nil { return fmt.Errorf("failed to query status with rpmostree: %w", err) } @@ -254,14 +254,14 @@ func CleanupUnbootedStateroots(log logr.Logger, ops ops.Ops, ostreeClient ostree if stateroot == bootedStateroot { continue } - if err := cleanupUnbootedStateroot(stateroot, ops, ostreeClient, rpmOstreeClient); err != nil { + if err := cleanupUnbootedStateroot(ctx, stateroot, ops, ostreeClient, rpmOstreeClient); err != nil { log.Error(err, "failed to remove stateroot", "stateroot", stateroot) failures += 1 } } // clear temporary files and reclaim disk space - if err := rpmOstreeClient.RpmOstreeCleanup(); err != nil { + if err := rpmOstreeClient.RpmOstreeCleanup(ctx); err != nil { log.Error(err, "failed rpm-ostree cleanup -b") failures += 1 } @@ -291,8 +291,8 @@ func CleanupUnbootedStateroots(log logr.Logger, ops ops.Ops, ostreeClient ostree return fmt.Errorf("failed to remove %d stateroots", failures) } -func cleanupUnbootedStateroot(stateroot string, ops ops.Ops, ostreeClient ostreeclient.IClient, rpmOstreeClient rpmostreeclient.IClient) error { - status, err := rpmOstreeClient.QueryStatus() +func cleanupUnbootedStateroot(ctx context.Context, stateroot string, ops ops.Ops, ostreeClient ostreeclient.IClient, rpmOstreeClient rpmostreeclient.IClient) error { + status, err := rpmOstreeClient.QueryStatus(ctx) if err != nil { return fmt.Errorf("failed to query status with rpmostree during stateroot cleanup: %w", err) } @@ -310,7 +310,7 @@ func cleanupUnbootedStateroot(stateroot string, ops ops.Ops, ostreeClient ostree indicesToUndeploy = append(indicesToUndeploy, i) } for _, idx := range indicesToUndeploy { - if err := ostreeClient.Undeploy(idx); err != nil { + if err := ostreeClient.Undeploy(ctx, idx); err != nil { return fmt.Errorf("failed to undeploy %s with index %d: %w", stateroot, idx, err) } } @@ -318,7 +318,7 @@ func cleanupUnbootedStateroot(stateroot string, ops ops.Ops, ostreeClient ostree if _, err := osStat(common.PathOutsideChroot(staterootPath)); err != nil { return nil } - if _, err := ops.RunBashInHostNamespace("unshare", "-m", "/bin/sh", "-c", + if _, err := ops.RunBashInHostNamespace(ctx, "unshare", "-m", "/bin/sh", "-c", fmt.Sprintf("\"mount -o remount,rw /sysroot && rm -rf %s\"", staterootPath)); err != nil { return fmt.Errorf("removing stateroot %s failed: %w", stateroot, err) } diff --git a/controllers/idle_handlers_test.go b/controllers/idle_handlers_test.go index 070f9ba1a3..00db074b8b 100644 --- a/controllers/idle_handlers_test.go +++ b/controllers/idle_handlers_test.go @@ -117,13 +117,13 @@ func TestImageBasedUpgradeReconciler_cleanupUnbootedStateroot(t *testing.T) { executorMock := ops.NewMockExecute(ctrl) mockOps := ops.NewMockOps(ctrl) - rpmostreeclientMock.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + rpmostreeclientMock.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: tt.deployments}, nil) for _, x := range tt.undeployIndices { - ostreeclientMock.EXPECT().Undeploy(x) + ostreeclientMock.EXPECT().Undeploy(gomock.Any(), x) } if tt.expectToRemove != "" { - mockOps.EXPECT().RunBashInHostNamespace("unshare", "-m", "/bin/sh", "-c", + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), "unshare", "-m", "/bin/sh", "-c", fmt.Sprintf("\"mount -o remount,rw /sysroot && rm -rf /ostree/deploy/%s\"", tt.expectToRemove)) } @@ -138,8 +138,8 @@ func TestImageBasedUpgradeReconciler_cleanupUnbootedStateroot(t *testing.T) { return os.Stat(".") } - if err := cleanupUnbootedStateroot(tt.input, r.Ops, r.OstreeClient, r.RPMOstreeClient); (err != nil) != tt.wantErr { - t.Errorf("ImageBasedUpgradeReconciler.cleanupUnbootedStateroot() error = %v, wantErr %v", err, tt.wantErr) + if err := cleanupUnbootedStateroot(context.Background(), tt.input, r.Ops, r.OstreeClient, r.RPMOstreeClient); (err != nil) != tt.wantErr { + t.Errorf("ImageBasedUpgradeReconciler.cleanupUnbootedStateroot(context.Background(), ) error = %v, wantErr %v", err, tt.wantErr) } }) } @@ -276,19 +276,19 @@ func TestImageBasedUpgradeReconciler_cleanupUnbootedStateroots(t *testing.T) { executorMock := ops.NewMockExecute(ctrl) mockOps := ops.NewMockOps(ctrl) - rpmostreeclientMock.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + rpmostreeclientMock.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: tt.deployments}, nil) for _, x := range tt.undeployIndices { - ostreeclientMock.EXPECT().Undeploy(x) + ostreeclientMock.EXPECT().Undeploy(gomock.Any(), x) } for _, stateroot := range tt.staterootsToRemove { - rpmostreeclientMock.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + rpmostreeclientMock.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: tt.deployments}, nil) - mockOps.EXPECT().RunBashInHostNamespace("unshare", "-m", "/bin/sh", "-c", + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), "unshare", "-m", "/bin/sh", "-c", fmt.Sprintf("\"mount -o remount,rw /sysroot && rm -rf /ostree/deploy/%s\"", stateroot)) } - rpmostreeclientMock.EXPECT().RpmOstreeCleanup().Return(nil) + rpmostreeclientMock.EXPECT().RpmOstreeCleanup(gomock.Any()).Return(nil) r := &ImageBasedUpgradeReconciler{ Log: logr.Discard(), RPMOstreeClient: rpmostreeclientMock, @@ -297,7 +297,7 @@ func TestImageBasedUpgradeReconciler_cleanupUnbootedStateroots(t *testing.T) { Ops: mockOps, } - if err := CleanupUnbootedStateroots(r.Log, r.Ops, r.OstreeClient, r.RPMOstreeClient); (err != nil) != tt.wantErr { + if err := CleanupUnbootedStateroots(context.Background(), r.Log, r.Ops, r.OstreeClient, r.RPMOstreeClient); (err != nil) != tt.wantErr { t.Errorf("ImageBasedUpgradeReconciler.cleanupUnbootedStateroots() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/controllers/ipc_config_handlers.go b/controllers/ipc_config_handlers.go index c3900ab8ec..9254abb11d 100644 --- a/controllers/ipc_config_handlers.go +++ b/controllers/ipc_config_handlers.go @@ -144,13 +144,13 @@ func (h *IPCConfigStageHandler) Handle(ctx context.Context, ipc *ipcv1.IPConfig) return doNotRequeue(), nil } - targetStaterootBooted, err := isTargetStaterootBooted(ipc, h.RPMOstreeClient) + targetStaterootBooted, err := isTargetStaterootBooted(ctx, ipc, h.RPMOstreeClient) if err != nil { logger.Error(err, "Failed to determine whether target stateroot is booted") return requeueWithError(fmt.Errorf("failed to check if target stateroot is booted: %w", err)) } - isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(h.RPMOstreeClient) + isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(ctx, h.RPMOstreeClient) if err != nil { logger.Error(err, "Failed to determine whether an unbooted stateroot is available") return requeueWithError(fmt.Errorf("failed to check if unbooted stateroot is available: %w", err)) @@ -279,7 +279,7 @@ func (h *IPCConfigTwoPhaseHandler) PrePivot( return requeueWithError(fmt.Errorf("failed to export ipconfig for uncontrolled rollback: %w", err)) } - if err := h.RunLcaCliIPConfigPrePivot(logger); err != nil { + if err := h.RunLcaCliIPConfigPrePivot(ctx, logger); err != nil { controllerutils.SetIPConfigStatusFailed( ipc, fmt.Sprintf("ip-config pre-pivot failed. error: %s", err.Error()), @@ -305,7 +305,7 @@ func (h *IPCConfigTwoPhaseHandler) PostPivot( logger.Info("Starting post-pivot phase") if ipc.Status.RollbackAvailabilityExpiration.IsZero() { - unbootedStateroot, err := h.RPMOstreeClient.GetUnbootedStaterootName() + unbootedStateroot, err := h.RPMOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { return requeueWithError(fmt.Errorf("unable to determine unbooted stateroot path for rollback: %w", err)) } @@ -320,7 +320,7 @@ func (h *IPCConfigTwoPhaseHandler) PostPivot( } } - if err := h.RebootClient.DisableInitMonitor(); err != nil { + if err := h.RebootClient.DisableInitMonitor(ctx); err != nil { controllerutils.SetIPConfigStatusFailed( ipc, fmt.Sprintf("failed to disable init monitor: %s", err.Error()), @@ -799,6 +799,7 @@ func getRecertImage(ipc *ipcv1.IPConfig) string { // RunLcaCliIPConfigPrePivot schedules an lca-cli ip-config pre-pivot via systemd-run. func (h *IPCConfigTwoPhaseHandler) RunLcaCliIPConfigPrePivot( + ctx context.Context, logger logr.Logger, ) error { logger.Info("Scheduling lca-cli ip-config pre-pivot via systemd-run") @@ -812,7 +813,7 @@ func (h *IPCConfigTwoPhaseHandler) RunLcaCliIPConfigPrePivot( controllerutils.LcaCliBinaryName, "ip-config", "pre-pivot", } - if _, err := h.ChrootOps.RunSystemdAction(args...); err != nil { + if _, err := h.ChrootOps.RunSystemdAction(ctx, args...); err != nil { return fmt.Errorf("lca-cli ip-config pre-pivot failed: %w", err) } @@ -1014,8 +1015,8 @@ func (h *IPCConfigStageHandler) validateSNO(ctx context.Context) error { return nil } -func (h *IPCConfigStageHandler) validateStaticNetworking() error { - output, err := h.ChrootOps.RunInHostNamespace("nmstatectl", "show", "--json", "-q") +func (h *IPCConfigStageHandler) validateStaticNetworking(ctx context.Context) error { + output, err := h.ChrootOps.RunInHostNamespace(ctx, "nmstatectl", "show", "--json", "-q") if err != nil { return fmt.Errorf("failed to run nmstatectl show --json for static networking validation: %w", err) } @@ -1074,7 +1075,7 @@ func (h *IPCConfigStageHandler) validateConfigStart(ctx context.Context, ipc *ip return fmt.Errorf("validation of SNO failed: %w", err) } - if err := h.validateStaticNetworking(); err != nil { + if err := h.validateStaticNetworking(ctx); err != nil { return fmt.Errorf("validation of static networking failed: %w", err) } diff --git a/controllers/ipc_config_handlers_test.go b/controllers/ipc_config_handlers_test.go index 801469b07e..20b2deea8d 100644 --- a/controllers/ipc_config_handlers_test.go +++ b/controllers/ipc_config_handlers_test.go @@ -529,9 +529,10 @@ func TestIPCConfigTwoPhaseHandler_PrePivot(t *testing.T) { gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), ). - DoAndReturn(func(args ...string) (string, error) { + DoAndReturn(func(_ context.Context, args ...string) (string, error) { assert.Contains(t, args, "--unit") assert.Contains(t, args, controllerutils.IPConfigPrePivotUnit) assert.Contains(t, args, controllerutils.LcaCliBinaryName) @@ -591,7 +592,8 @@ func TestIPCConfigTwoPhaseHandler_PrePivot(t *testing.T) { gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), ). Return("", errors.New("systemd-run failed")). Times(1) @@ -649,7 +651,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { return errors.New("not healthy") } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -692,7 +694,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { return nil } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -783,7 +785,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return errors.New("not healthy") } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -848,7 +850,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return nil } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -897,7 +899,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return nil } - mockReboot.EXPECT().DisableInitMonitor().Return(errors.New("disable failed")).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(errors.New("disable failed")).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -939,7 +941,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return nil } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -1009,7 +1011,7 @@ func TestIPCConfigTwoPhaseHandler_PostPivot(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return errors.New("not healthy") } - mockReboot.EXPECT().DisableInitMonitor().Return(nil).Times(1) + mockReboot.EXPECT().DisableInitMonitor(gomock.Any()).Return(nil).Times(1) h := &IPCConfigTwoPhaseHandler{ Client: k8sClient, @@ -1092,12 +1094,12 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { assert.NoError(t, k8sClient.Status().Update(ctx, updated)) mockOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(`{"interfaces":[{"name":"br-ex","type":"ovs-interface","bridge":{"port":[{"name":"ens3"},{"name":"patch-br-ex"}]}},{"name":"ens3","type":"ethernet","ipv4":{"enabled":true,"dhcp":false,"address":[{"ip":"192.0.2.10","prefix-length":24}]},"ipv6":{"enabled":false,"dhcp":false,"autoconf":false,"address":[]}}],"routes":{"running":[], "config":[]},"dns-resolver":{"running":{"server":[]},"config":{"server":[]}}}`, nil). Times(1) - mockRPM.EXPECT().IsStaterootBooted("rhcos").Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) tph.EXPECT(). PrePivot(gomock.Any(), gomock.Any(), gomock.Any()). Return(requeueWithShortInterval(), nil). @@ -1159,7 +1161,7 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { // DHCP enabled on br-ex uplink => should fail validation. mockOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(`{"interfaces":[{"name":"br-ex","type":"ovs-interface","bridge":{"port":[{"name":"ens3"},{"name":"patch-br-ex"}]},"ipv4":{"enabled":true,"dhcp":true,"address":[{"ip":"192.0.2.10","prefix-length":24}]}},{"name":"ens3","type":"ethernet","ipv4":{"enabled":true,"dhcp":false,"address":[{"ip":"192.0.2.10","prefix-length":24}]},"ipv6":{"enabled":false,"dhcp":false,"autoconf":false,"address":[]}}],"routes":{"running":[], "config":[]},"dns-resolver":{"running":{"server":[]},"config":{"server":[]}}}`, nil). Times(1) @@ -1216,12 +1218,12 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { k8sClient := newFakeClientWithStatus(t, scheme, ipc, ibu, node, mc) mockOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(`{"interfaces":[{"name":"br-ex","type":"ovs-interface","bridge":{"port":[{"name":"ens3"},{"name":"patch-br-ex"}]}},{"name":"ens3","type":"ethernet","ipv4":{"enabled":true,"dhcp":false,"address":[{"ip":"192.0.2.10","prefix-length":24}]},"ipv6":{"enabled":false,"dhcp":false,"autoconf":false,"address":[]}}],"routes":{"running":[], "config":[]},"dns-resolver":{"running":{"server":[]},"config":{"server":[]}}}`, nil). Times(1) - mockRPM.EXPECT().IsStaterootBooted("rhcos").Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). @@ -1260,8 +1262,8 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { controllerutils.SetIPConfigStatusInProgress(ipc, "Configuration is in progress") k8sClient := newFakeClientWithStatus(t, scheme, ipc) - mockRPM.EXPECT().IsStaterootBooted("rhcos").Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). @@ -1303,8 +1305,8 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { k8sClient := newFakeClientWithStatus(t, scheme, ipc, node, mc) // Transition requested should still be false since we're already in progress; only boot check should be called. - mockRPM.EXPECT().IsStaterootBooted("rhcos").Return(true, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(true, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). @@ -1349,8 +1351,8 @@ func TestIPCConfigStageHandler_Handle(t *testing.T) { controllerutils.SetIPConfigStatusInProgress(ipc, "Configuration is in progress") k8sClient := newFakeClientWithStatus(t, scheme, ipc) - mockRPM.EXPECT().IsStaterootBooted("rhcos").Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). diff --git a/controllers/ipc_controller.go b/controllers/ipc_controller.go index a44a4464d5..de563e380a 100644 --- a/controllers/ipc_controller.go +++ b/controllers/ipc_controller.go @@ -96,7 +96,7 @@ func (r *IPConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (r } defer func() { - validNextStages, ierr := validNextStages(ipc, r.RPMOstreeClient) + validNextStages, ierr := validNextStages(ctx, ipc, r.RPMOstreeClient) if ierr != nil { if err != nil { err = fmt.Errorf("%w; also failed to validate next stages: %s", err, ierr.Error()) @@ -122,7 +122,7 @@ func (r *IPConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (r }() if ipc.Status.ValidNextStages == nil { - validNextStages, err := validNextStages(ipc, r.RPMOstreeClient) + validNextStages, err := validNextStages(ctx, ipc, r.RPMOstreeClient) if err != nil { return requeueWithError(fmt.Errorf("failed to get valid next stages: %w", err)) } @@ -244,19 +244,19 @@ func (r *IPConfigReconciler) gateIPConfigByIBU( return requeueWithShortInterval(), nil } -func validNextStages(ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclient.IClient) ([]ipcv1.IPConfigStage, error) { +func validNextStages(ctx context.Context, ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclient.IClient) ([]ipcv1.IPConfigStage, error) { inProgressStage := controllerutils.GetIPInProgressStage(ipc) if inProgressStage == ipcv1.IPStages.Idle { return []ipcv1.IPConfigStage{ipcv1.IPStages.Idle}, nil } - isTargetStaterootBooted, err := isTargetStaterootBooted(ipc, rpmOstreeClient) + isTargetStaterootBooted, err := isTargetStaterootBooted(ctx, ipc, rpmOstreeClient) if err != nil { return nil, fmt.Errorf("failed to check if target stateroot is booted: %w", err) } - isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(rpmOstreeClient) + isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(ctx, rpmOstreeClient) if err != nil { return nil, fmt.Errorf("failed to check if unbooted stateroot is available: %w", err) } @@ -306,7 +306,7 @@ func validNextStages(ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclient.IClien // isTargetStaterootBooted determines whether the stateroot prepared for this IP change is currently booted. // It reconstructs the expected stateroot name from the spec (matching the lca-cli prepare logic) and queries rpm-ostree. -func isTargetStaterootBooted(ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclient.IClient) (*bool, error) { +func isTargetStaterootBooted(ctx context.Context, ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclient.IClient) (*bool, error) { if rpmOstreeClient == nil { return nil, fmt.Errorf("rpmOstreeClient is nil") } @@ -316,7 +316,7 @@ func isTargetStaterootBooted(ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclien return nil, fmt.Errorf("failed to build target stateroot name") } - booted, err := rpmOstreeClient.IsStaterootBooted(targetStaterootName) + booted, err := rpmOstreeClient.IsStaterootBooted(ctx, targetStaterootName) if err != nil { return nil, fmt.Errorf("failed to check if target stateroot is booted: %w", err) } @@ -324,12 +324,12 @@ func isTargetStaterootBooted(ipc *ipcv1.IPConfig, rpmOstreeClient rpmostreeclien return lo.ToPtr(booted), nil } -func isUnbootedStaterootAvailable(rpmOstreeClient rpmostreeclient.IClient) (*bool, error) { +func isUnbootedStaterootAvailable(ctx context.Context, rpmOstreeClient rpmostreeclient.IClient) (*bool, error) { if rpmOstreeClient == nil { return nil, fmt.Errorf("rpmOstreeClient is nil") } - unbootedStaterootName, err := rpmOstreeClient.GetUnbootedStaterootName() + unbootedStaterootName, err := rpmOstreeClient.GetUnbootedStaterootName(ctx) if err != nil || unbootedStaterootName == "" { return lo.ToPtr(false), nil } @@ -520,7 +520,7 @@ func (r *IPConfigReconciler) cacheRecertImageIfNeeded(ctx context.Context, ipc * authFile = tmpPath } - if _, err := r.ChrootOps.RunBashInHostNamespace( + if _, err := r.ChrootOps.RunBashInHostNamespace(ctx, "podman", "pull", "--authfile", @@ -549,7 +549,7 @@ func isIPTransitionRequested(ipc *ipcv1.IPConfig) bool { } func (r *IPConfigReconciler) refreshNetworkStatus(ctx context.Context, ipc *ipcv1.IPConfig) error { - output, err := r.nmstateShowJSON() + output, err := r.nmstateShowJSON(ctx) if err != nil { return fmt.Errorf("failed to get nmstate output: %w", err) } @@ -628,8 +628,8 @@ func (r *IPConfigReconciler) inferDNSFilterOutFamily() (*string, error) { return nil, fmt.Errorf("unknown filter file contents: %q", content) } -func (r *IPConfigReconciler) nmstateShowJSON() (string, error) { - output, err := r.NsenterOps.RunInHostNamespace("nmstatectl", "show", "--json", "-q") +func (r *IPConfigReconciler) nmstateShowJSON(ctx context.Context) (string, error) { + output, err := r.NsenterOps.RunInHostNamespace(ctx, "nmstatectl", "show", "--json", "-q") if err != nil { return "", fmt.Errorf("failed to run nmstatectl show --json: %w", err) } diff --git a/controllers/ipc_controller_test.go b/controllers/ipc_controller_test.go index 4db5442358..62e5025617 100644 --- a/controllers/ipc_controller_test.go +++ b/controllers/ipc_controller_test.go @@ -34,8 +34,8 @@ func expectReconcileTestFSDefaults(chrootOps *ops.MockOps) { } func mockTargetStaterootNotBooted(mockRPM *rpmostreeclient.MockIClient) { - mockRPM.EXPECT().IsStaterootBooted(gomock.Any()).Return(false, nil).AnyTimes() - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).AnyTimes() + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).AnyTimes() } func reconcileTestMinimalNmstateJSON() string { @@ -192,7 +192,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -252,7 +252,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -317,7 +317,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -369,7 +369,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -423,7 +423,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -475,7 +475,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -489,7 +489,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { rollbackHandler.EXPECT(). Handle(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, got *ipcv1.IPConfig) (ctrl.Result, error) { - // validNextStages() depends on status conditions; simulate what the real rollback handler does. + // validNextStages(context.Background(), ) depends on status conditions; simulate what the real rollback handler does. controllerutils.SetIPRollbackStatusInProgress(got, "Rollback is in progress") return doNotRequeue(), nil }). @@ -537,7 +537,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -586,7 +586,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -638,7 +638,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -692,7 +692,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -706,7 +706,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { rollbackHandler.EXPECT(). Handle(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, got *ipcv1.IPConfig) (ctrl.Result, error) { - // validNextStages() depends on status conditions; simulate what the real rollback handler does. + // validNextStages(context.Background(), ) depends on status conditions; simulate what the real rollback handler does. controllerutils.SetIPRollbackStatusInProgress(got, "Rollback is in progress") return ctrl.Result{}, fmt.Errorf("boom") }). @@ -754,7 +754,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -859,7 +859,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). AnyTimes() @@ -906,7 +906,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). AnyTimes() @@ -947,7 +947,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return("{invalid json", nil). Times(1) @@ -997,7 +997,7 @@ func TestIPConfigReconciler_Reconcile_Full(t *testing.T) { nsenterOps := ops.NewMockOps(gc) nsenterOps.EXPECT(). - RunInHostNamespace("nmstatectl", "show", "--json", "-q"). + RunInHostNamespace(gomock.Any(), "nmstatectl", "show", "--json", "-q"). Return(reconcileTestMinimalNmstateJSON(), nil). Times(1) @@ -1096,10 +1096,10 @@ func TestValidNextStages(t *testing.T) { controllerutils.SetIPRollbackStatusInProgress(ipc, "in progress") // validNextStages queries rpm-ostree even for rollback stages. - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Empty(t, stages) }) @@ -1113,10 +1113,10 @@ func TestValidNextStages(t *testing.T) { controllerutils.SetIPRollbackStatusFailed(ipc, "failed") // validNextStages queries rpm-ostree even for rollback stages. - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Empty(t, stages) }) @@ -1131,10 +1131,10 @@ func TestValidNextStages(t *testing.T) { ipc.Spec.DNSFilterOutFamily = common.IPv6FamilyName controllerutils.SetIPConfigStatusInProgress(ipc, "in progress") - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Rollback}, stages) }) @@ -1148,10 +1148,10 @@ func TestValidNextStages(t *testing.T) { ipc.Spec.IPv4 = &ipcv1.IPv4Config{Address: "192.0.2.10"} controllerutils.SetIPConfigStatusInProgress(ipc, "in progress") - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Idle}, stages) }) @@ -1164,10 +1164,10 @@ func TestValidNextStages(t *testing.T) { ipc := &ipcv1.IPConfig{} controllerutils.SetIPConfigStatusCompleted(ipc, "done") - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Idle, ipcv1.IPStages.Rollback}, stages) }) @@ -1180,10 +1180,10 @@ func TestValidNextStages(t *testing.T) { ipc := &ipcv1.IPConfig{} controllerutils.SetIPRollbackStatusCompleted(ipc, "done") - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Idle}, stages) }) @@ -1198,10 +1198,10 @@ func TestValidNextStages(t *testing.T) { Conditions: []metav1.Condition{}, }, } - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(false, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Idle}, stages) }) @@ -1211,7 +1211,7 @@ func TestValidNextStages(t *testing.T) { ipc.Spec.IPv4 = &ipcv1.IPv4Config{Address: "192.0.2.10"} controllerutils.SetIPConfigStatusInProgress(ipc, "in progress") - _, err := validNextStages(ipc, nil) + _, err := validNextStages(context.Background(), ipc, nil) assert.Error(t, err) assert.Contains(t, err.Error(), "rpmOstreeClient is nil") }) @@ -1225,10 +1225,10 @@ func TestValidNextStages(t *testing.T) { ipc.Spec.IPv4 = &ipcv1.IPv4Config{Address: "192.0.2.10"} controllerutils.SetIPConfigStatusInProgress(ipc, "in progress") - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", nil).Times(1) - stages, err := validNextStages(ipc, mockRPM) + stages, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, []ipcv1.IPConfigStage{ipcv1.IPStages.Idle}, stages) }) @@ -1315,10 +1315,10 @@ func TestValidNextStages_DoesNotMutateIPC(t *testing.T) { controllerutils.SetIPConfigStatusCompleted(ipc, "done") before := ipc.DeepCopy() - mockRPM.EXPECT().IsStaterootBooted(buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) - mockRPM.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) + mockRPM.EXPECT().IsStaterootBooted(gomock.Any(), buildIPConfigStaterootName(ipc)).Return(true, nil).Times(1) + mockRPM.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) - _, err := validNextStages(ipc, mockRPM) + _, err := validNextStages(context.Background(), ipc, mockRPM) assert.NoError(t, err) assert.Equal(t, before, ipc, "validNextStages should be pure and not mutate IPC") } diff --git a/controllers/ipc_idle_handlers.go b/controllers/ipc_idle_handlers.go index 25dfc5e2b0..0a35ec7e50 100644 --- a/controllers/ipc_idle_handlers.go +++ b/controllers/ipc_idle_handlers.go @@ -117,7 +117,7 @@ func (h *IPCIdleStageHandler) Handle( } } - if err := h.cleanup(logger); err != nil { + if err := h.cleanup(ctx, logger); err != nil { controllerutils.SetIPIdleStatusFalse( ipc, controllerutils.ConditionReasons.Failed, @@ -146,12 +146,12 @@ func (h *IPCIdleStageHandler) Handle( return doNotRequeue(), nil } -func (h *IPCIdleStageHandler) cleanup(logger logr.Logger) error { - if err := h.ChrootOps.RemountSysroot(); err != nil { +func (h *IPCIdleStageHandler) cleanup(ctx context.Context, logger logr.Logger) error { + if err := h.ChrootOps.RemountSysroot(ctx); err != nil { return fmt.Errorf("failed to remount sysroot: %w", err) } - if err := h.cleanuoUnbootedStateroots(logger); err != nil { + if err := h.cleanuoUnbootedStateroots(ctx, logger); err != nil { return fmt.Errorf("failed to clean up unbooted stateroots: %w", err) } @@ -192,14 +192,14 @@ func (h *IPCIdleStageHandler) checkIPManualCleanup( return false, nil } -func (h *IPCIdleStageHandler) cleanuoUnbootedStateroots(logger logr.Logger) error { - staterootsToRemove, err := getStaterootsToRemove(h.RPMOstreeClient) +func (h *IPCIdleStageHandler) cleanuoUnbootedStateroots(ctx context.Context, logger logr.Logger) error { + staterootsToRemove, err := getStaterootsToRemove(ctx, h.RPMOstreeClient) if err != nil { return fmt.Errorf("failed to determine stateroots to remove: %w", err) } logger.Info("Stateroots to remove", "stateroots", staterootsToRemove) - if err := h.ChrootOps.RemountBoot(); err != nil { + if err := h.ChrootOps.RemountBoot(ctx); err != nil { return fmt.Errorf("failed to remount boot: %w", err) } @@ -207,7 +207,7 @@ func (h *IPCIdleStageHandler) cleanuoUnbootedStateroots(logger logr.Logger) erro return err } - if err := CleanupUnbootedStateroots(logger, h.ChrootOps, h.OstreeClient, h.RPMOstreeClient); err != nil { + if err := CleanupUnbootedStateroots(ctx, logger, h.ChrootOps, h.OstreeClient, h.RPMOstreeClient); err != nil { return fmt.Errorf("failed to clean up unbooted stateroots: %w", err) } @@ -250,8 +250,8 @@ func removeBootDirsByStaterootPrefixes( return nil } -func getStaterootsToRemove(rpmOstreeClient rpmostreeclient.IClient) ([]string, error) { - status, err := rpmOstreeClient.QueryStatus() +func getStaterootsToRemove(ctx context.Context, rpmOstreeClient rpmostreeclient.IClient) ([]string, error) { + status, err := rpmOstreeClient.QueryStatus(ctx) if err != nil { return nil, fmt.Errorf("failed to query status with rpmostree: %w", err) } diff --git a/controllers/ipc_idle_handlers_test.go b/controllers/ipc_idle_handlers_test.go index 657c20ff9a..13e5f09895 100644 --- a/controllers/ipc_idle_handlers_test.go +++ b/controllers/ipc_idle_handlers_test.go @@ -194,7 +194,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { t.Fatalf("CheckHealth should not be called when stage validation fails") return nil } - mockOps.EXPECT().RemountSysroot().Times(0) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Times(0) res, err := h.Handle(ctx, ipc) assert.NoError(t, err) @@ -248,7 +248,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { } // Since status update fails before we can proceed, we should not attempt any cleanup. - mockOps.EXPECT().RemountSysroot().Times(0) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Times(0) res, err := h.Handle(ctx, ipc) assert.Error(t, err) @@ -323,7 +323,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { } // If health checks are skipped, handler should proceed to cleanup and hit RemountSysroot. - mockOps.EXPECT().RemountSysroot().Return(errors.New("remount failed")).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(errors.New("remount failed")).Times(1) res, err := h.Handle(ctx, ipc) assert.Error(t, err) @@ -361,7 +361,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return nil } - mockOps.EXPECT().RemountSysroot().Return(errors.New("remount failed")).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(errors.New("remount failed")).Times(1) res, err := h.Handle(ctx, ipc) assert.Error(t, err) @@ -398,8 +398,8 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { defer func() { CheckHealth = oldHC }() CheckHealth = func(ctx context.Context, c client.Reader, l logr.Logger) error { return nil } - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) - mockRpm.EXPECT().QueryStatus().Return(nil, errors.New("rpm error")).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) + mockRpm.EXPECT().QueryStatus(gomock.Any()).Return(nil, errors.New("rpm error")).Times(1) res, err := h.Handle(ctx, ipc) assert.Error(t, err) @@ -440,12 +440,12 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { Deployments: []rpmostreeclient.Deployment{{OSName: "rhcos", Booted: true}}, } - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) // getStaterootsToRemove + CleanupUnbootedStateroots - mockRpm.EXPECT().QueryStatus().Return(status, nil).Times(2) - mockOps.EXPECT().RemountBoot().Return(nil).Times(1) + mockRpm.EXPECT().QueryStatus(gomock.Any()).Return(status, nil).Times(2) + mockOps.EXPECT().RemountBoot(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().ReadDir(gomock.Any()).Return([]os.DirEntry{}, nil).Times(1) - mockRpm.EXPECT().RpmOstreeCleanup().Return(nil).Times(1) + mockRpm.EXPECT().RpmOstreeCleanup(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().StatFile(gomock.Any()).Return(fakeFileInfo{}, nil).Times(1) mockOps.EXPECT().RemoveAllFiles(gomock.Any()).Return(errors.New("rm failed")).Times(1) @@ -505,11 +505,11 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { Deployments: []rpmostreeclient.Deployment{{OSName: "rhcos", Booted: true}}, } - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) - mockRpm.EXPECT().QueryStatus().Return(status, nil).Times(2) // getStaterootsToRemove + CleanupUnbootedStateroots - mockOps.EXPECT().RemountBoot().Return(nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) + mockRpm.EXPECT().QueryStatus(gomock.Any()).Return(status, nil).Times(2) // getStaterootsToRemove + CleanupUnbootedStateroots + mockOps.EXPECT().RemountBoot(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().ReadDir(gomock.Any()).Return([]os.DirEntry{}, nil).Times(1) - mockRpm.EXPECT().RpmOstreeCleanup().Return(nil).Times(1) + mockRpm.EXPECT().RpmOstreeCleanup(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().StatFile(gomock.Any()).Return(nil, errors.New("not exist")).Times(1) mockOps.EXPECT().RemoveAllFiles(gomock.Any()).Times(0) @@ -656,7 +656,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { return nil } - mockOps.EXPECT().RemountSysroot().Times(0) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Times(0) res, err := h.Handle(ctx, ipc) assert.NoError(t, err) @@ -703,7 +703,7 @@ func TestIPCIdleStageHandler_Handle(t *testing.T) { return nil } - mockOps.EXPECT().RemountSysroot().Times(0) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Times(0) res, err := h.Handle(ctx, ipc) assert.Error(t, err) @@ -737,7 +737,7 @@ func TestGetStaterootsToRemove(t *testing.T) { mockRPM := rpmostreeclient.NewMockIClient(gc) - mockRPM.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + mockRPM.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: []rpmostreeclient.Deployment{ {OSName: "booted", Booted: true}, {OSName: "old-1", Booted: false}, @@ -745,7 +745,7 @@ func TestGetStaterootsToRemove(t *testing.T) { }, }, nil).Times(1) - got, err := getStaterootsToRemove(mockRPM) + got, err := getStaterootsToRemove(context.Background(), mockRPM) assert.NoError(t, err) // Reverse order because code walks deployments from end to start. assert.Equal(t, []string{"old-2", "old-1"}, got) diff --git a/controllers/ipc_rollback_handlers.go b/controllers/ipc_rollback_handlers.go index f5dfd60e7b..ae3b52e8de 100644 --- a/controllers/ipc_rollback_handlers.go +++ b/controllers/ipc_rollback_handlers.go @@ -89,7 +89,7 @@ func (h *IPCRollbackStageHandler) Handle( return doNotRequeue(), nil } - if err := h.validateRollbackStart(); err != nil { + if err := h.validateRollbackStart(ctx); err != nil { controllerutils.SetIPRollbackStatusFailed( ipc, fmt.Sprintf("rollback validation failed: %s", err.Error()), @@ -122,7 +122,7 @@ func (h *IPCRollbackStageHandler) Handle( return doNotRequeue(), nil } - targetStaterootBooted, err := isTargetStaterootBooted(ipc, h.RPMOstreeClient) + targetStaterootBooted, err := isTargetStaterootBooted(ctx, ipc, h.RPMOstreeClient) if err != nil { logger.Error(err, "Failed to determine whether target stateroot is booted") return requeueWithError(fmt.Errorf("failed to check if target stateroot is booted: %w", err)) @@ -175,7 +175,7 @@ func (r *IPCRollbackTwoPhaseHandler) PrePivot( controllerutils.StartIPPhase(r.Client, logger, ipc, IPConfigRollbackPhasePrepivot) logger.Info("Starting pre-pivot phase") - stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName() + stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { controllerutils.SetIPRollbackStatusFailed( ipc, @@ -187,7 +187,7 @@ func (r *IPCRollbackTwoPhaseHandler) PrePivot( return requeueWithError(fmt.Errorf("failed to determine unbooted stateroot: %w", err)) } - if err := r.Ops.RemountSysroot(); err != nil { + if err := r.Ops.RemountSysroot(ctx); err != nil { controllerutils.SetIPRollbackStatusFailed( ipc, fmt.Errorf("failed to remount sysroot: %w", err).Error(), @@ -229,7 +229,7 @@ func (r *IPCRollbackTwoPhaseHandler) PrePivot( return requeueWithError(fmt.Errorf("failed to save IPConfig CR before pivot: %w", err)) } - if err := r.scheduleIPConfigRollback(logger, stateroot); err != nil { + if err := r.scheduleIPConfigRollback(ctx, logger, stateroot); err != nil { controllerutils.SetIPRollbackStatusFailed( ipc, fmt.Errorf("failed to schedule ip-config rollback: %w", err).Error(), @@ -283,6 +283,7 @@ func (r *IPCRollbackTwoPhaseHandler) PostPivot( } func (r *IPCRollbackTwoPhaseHandler) scheduleIPConfigRollback( + ctx context.Context, logger logr.Logger, stateroot string, ) error { @@ -296,23 +297,23 @@ func (r *IPCRollbackTwoPhaseHandler) scheduleIPConfigRollback( controllerutils.LcaCliBinaryName, "ip-config", "rollback", "--stateroot", stateroot, } - if _, err := r.Ops.RunSystemdAction(args...); err != nil { + if _, err := r.Ops.RunSystemdAction(ctx, args...); err != nil { return fmt.Errorf("failed to schedule ip-config rollback: %w", err) } return nil } -func (h *IPCRollbackStageHandler) validateRollbackStart() error { - if err := h.validateUnbootedStaterootAvailable(); err != nil { +func (h *IPCRollbackStageHandler) validateRollbackStart(ctx context.Context) error { + if err := h.validateUnbootedStaterootAvailable(ctx); err != nil { return err } return nil } -func (h *IPCRollbackStageHandler) validateUnbootedStaterootAvailable() error { - isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(h.RPMOstreeClient) +func (h *IPCRollbackStageHandler) validateUnbootedStaterootAvailable(ctx context.Context) error { + isUnbootedStaterootAvailable, err := isUnbootedStaterootAvailable(ctx, h.RPMOstreeClient) if err != nil { return fmt.Errorf("failed to check if unbooted stateroot is available: %w", err) } diff --git a/controllers/ipc_rollback_handlers_test.go b/controllers/ipc_rollback_handlers_test.go index 8188ac9412..f53bd44532 100644 --- a/controllers/ipc_rollback_handlers_test.go +++ b/controllers/ipc_rollback_handlers_test.go @@ -150,8 +150,8 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).Times(1) - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(filename string, data []byte, perm os.FileMode) error { assert.True(t, strings.Contains(filename, "stateroot-old"), "expected save path to include stateroot name, got %q", filename) @@ -162,12 +162,12 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { }).Times(1) // scheduleIPConfigRollback passes 13 args to RunSystemdAction: // --wait --collect --property ExitType=cgroup --unit ... --description ... lca-cli ip-config rollback --stateroot - mockOps.EXPECT().RunSystemdAction( + mockOps.EXPECT().RunSystemdAction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ). - DoAndReturn(func(args ...string) (string, error) { + DoAndReturn(func(_ context.Context, args ...string) (string, error) { assert.Contains(t, args, "--unit") assert.Contains(t, args, controllerutils.IPConfigRollbackUnit) assert.Contains(t, args, controllerutils.LcaCliBinaryName) @@ -206,7 +206,7 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("", errors.New("boom")).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", errors.New("boom")).Times(1) h := &IPCRollbackTwoPhaseHandler{ Client: k8sClient, @@ -232,8 +232,8 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).Times(1) - mockOps.EXPECT().RemountSysroot().Return(errors.New("remount failed")).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(errors.New("remount failed")).Times(1) h := &IPCRollbackTwoPhaseHandler{ Client: k8sClient, @@ -259,8 +259,8 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).Times(1) - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("write failed")).Times(1) h := &IPCRollbackTwoPhaseHandler{ @@ -287,10 +287,10 @@ func TestIPCRollbackTwoPhaseHandler_PrePivot(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).Times(1) - mockOps.EXPECT().RemountSysroot().Return(nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).Times(1) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(nil).Times(1) mockOps.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockOps.EXPECT().RunSystemdAction( + mockOps.EXPECT().RunSystemdAction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), @@ -457,8 +457,8 @@ func TestIPCRollbackStageHandler_Handle(t *testing.T) { updated.Status.ValidNextStages = []v1.IPConfigStage{v1.IPStages.Rollback} assert.NoError(t, k8sClient.Status().Update(ctx, updated)) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("some-unbooted", nil).Times(1) - mockRpm.EXPECT().IsStaterootBooted(gomock.Any()).Return(true, nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("some-unbooted", nil).Times(1) + mockRpm.EXPECT().IsStaterootBooted(gomock.Any(), gomock.Any()).Return(true, nil).Times(1) tph.EXPECT(). PrePivot(gomock.Any(), gomock.Any(), gomock.Any()). Return(requeueWithShortInterval(), nil). @@ -485,8 +485,8 @@ func TestIPCRollbackStageHandler_Handle(t *testing.T) { k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("", errors.New("no unbooted stateroot")).Times(1) - mockRpm.EXPECT().IsStaterootBooted(gomock.Any()).Times(0) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("", errors.New("no unbooted stateroot")).Times(1) + mockRpm.EXPECT().IsStaterootBooted(gomock.Any(), gomock.Any()).Times(0) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) stageHandler := NewIPCRollbackStageHandler(k8sClient, mockRpm, mockOps, tph) @@ -527,8 +527,8 @@ func TestIPCRollbackStageHandler_Handle(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).AnyTimes() - mockRpm.EXPECT().IsStaterootBooted("rhcos").Return(true, nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).AnyTimes() + mockRpm.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(true, nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). @@ -563,7 +563,7 @@ func TestIPCRollbackStageHandler_Handle(t *testing.T) { k8sClient := newFakeClientWithIPC(t, scheme, ipc) // With PostPivot mocked, only Handle() performs the isTargetStaterootBooted check. - mockRpm.EXPECT().IsStaterootBooted("rhcos").Return(false, nil).Times(1) + mockRpm.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(false, nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). @@ -602,8 +602,8 @@ func TestIPCRollbackStageHandler_Handle(t *testing.T) { ipc := mkRollbackIPC(t, true) k8sClient := newFakeClientWithIPC(t, scheme, ipc) - mockRpm.EXPECT().GetUnbootedStaterootName().Return("stateroot-old", nil).AnyTimes() - mockRpm.EXPECT().IsStaterootBooted("rhcos").Return(true, nil).Times(1) + mockRpm.EXPECT().GetUnbootedStaterootName(gomock.Any()).Return("stateroot-old", nil).AnyTimes() + mockRpm.EXPECT().IsStaterootBooted(gomock.Any(), "rhcos").Return(true, nil).Times(1) tph := NewMockIPConfigTwoPhaseHandlerInterface(gc) tph.EXPECT(). diff --git a/controllers/prep_handlers.go b/controllers/prep_handlers.go index 97bddd7ca8..2d1a345cf8 100644 --- a/controllers/prep_handlers.go +++ b/controllers/prep_handlers.go @@ -68,7 +68,7 @@ func GetSeedImage(c client.Client, ctx context.Context, ibu *ibuv1.ImageBasedUpg defer func() { _ = os.Remove(common.PathOutsideChroot(pullSecretFilename)) }() } - if _, err := ops.Execute("podman", "pull", "--authfile", pullSecretFilename, ibu.Spec.SeedImageRef.Image); err != nil { + if _, err := ops.Execute(ctx, "podman", "pull", "--authfile", pullSecretFilename, ibu.Spec.SeedImageRef.Image); err != nil { return fmt.Errorf("failed to pull image: %w", err) } log.Info("Successfully pulled seed image", "image", ibu.Spec.SeedImageRef.Image) @@ -206,8 +206,7 @@ func (r *ImageBasedUpgradeReconciler) getLabelsForSeedImage(ctx context.Context, Labels map[string]string `json:"Labels"` } - // TODO: use the context when execute supports it - if inspectRaw, err := r.Executor.Execute("skopeo", inspectArgs...); err != nil || inspectRaw == "" { + if inspectRaw, err := r.Executor.Execute(ctx, "skopeo", inspectArgs...); err != nil || inspectRaw == "" { return nil, fmt.Errorf("failed to inspect image: %w", err) } else { if err := json.Unmarshal([]byte(inspectRaw), &inspect); err != nil { @@ -496,7 +495,7 @@ func initIBUWorkspaceDir() error { } // Cleanup container storage, if needed -func (r *ImageBasedUpgradeReconciler) containerStorageCleanup(ibu *ibuv1.ImageBasedUpgrade) error { +func (r *ImageBasedUpgradeReconciler) containerStorageCleanup(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) error { // Check whether image cleanup is disabled using annotation if val, exists := ibu.GetAnnotations()[common.ImageCleanupOnPrepAnnotation]; exists { if val == common.ImageCleanupDisabledValue { @@ -526,7 +525,7 @@ func (r *ImageBasedUpgradeReconciler) containerStorageCleanup(ibu *ibuv1.ImageBa } r.Log.Info("Performing automatic image cleanup to reduce container storage disk usage to be within threshold") - if err := r.ImageMgmtClient.CleanupUnusedImages(thresholdPercent); err != nil { + if err := r.ImageMgmtClient.CleanupUnusedImages(ctx, thresholdPercent); err != nil { return fmt.Errorf("failed during image cleanup: %w", err) } @@ -576,7 +575,7 @@ func (r *ImageBasedUpgradeReconciler) handlePrep(ctx context.Context, ibu *ibuv1 } r.Log.Info("Checking container storage disk space") - if err := r.containerStorageCleanup(ibu); err != nil { + if err := r.containerStorageCleanup(ctx, ibu); err != nil { return requeueWithError(fmt.Errorf("failed container storage cleanup: %w", err)) } diff --git a/controllers/rollback_handlers.go b/controllers/rollback_handlers.go index a75fff56ce..cec666b44c 100644 --- a/controllers/rollback_handlers.go +++ b/controllers/rollback_handlers.go @@ -31,8 +31,8 @@ import ( corev1 "k8s.io/api/core/v1" ) -func (r *ImageBasedUpgradeReconciler) getRollbackAvailabilityExpiration() (time.Time, error) { - stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName() +func (r *ImageBasedUpgradeReconciler) getRollbackAvailabilityExpiration(ctx context.Context) (time.Time, error) { + stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { return time.Time{}, fmt.Errorf("unable to determine onbooted stateroot path for rollback: %w", err) } @@ -49,19 +49,19 @@ func (r *ImageBasedUpgradeReconciler) getRollbackAvailabilityExpiration() (time. func (r *ImageBasedUpgradeReconciler) startRollback(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) (ctrl.Result, error) { utils.SetRollbackStatusInProgress(ibu, "Initiating rollback") - stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName() + stateroot, err := r.RPMOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { utils.SetRollbackStatusFailed(ibu, err.Error()) return doNotRequeue(), nil } - if err := r.Ops.RemountSysroot(); err != nil { + if err := r.Ops.RemountSysroot(ctx); err != nil { utils.SetRollbackStatusFailed(ibu, err.Error()) return doNotRequeue(), nil } r.Log.Info("Finding unbooted deployment") - deploymentIndex, err := r.RPMOstreeClient.GetUnbootedDeploymentIndex() + deploymentIndex, err := r.RPMOstreeClient.GetUnbootedDeploymentIndex(ctx) if err != nil { utils.SetRollbackStatusFailed(ibu, err.Error()) return doNotRequeue(), nil @@ -70,10 +70,13 @@ func (r *ImageBasedUpgradeReconciler) startRollback(ctx context.Context, ibu *ib // Set the new default deployment r.Log.Info("Checking for set-default feature") - if r.OstreeClient.IsOstreeAdminSetDefaultFeatureEnabled() { + if enabled, err := r.OstreeClient.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + utils.SetRollbackStatusFailed(ibu, fmt.Sprintf("failed to check ostree set-default feature: %s", err.Error())) + return doNotRequeue(), nil + } else if enabled { r.Log.Info("set-default feature available") - if err = r.OstreeClient.SetDefaultDeployment(deploymentIndex); err != nil { + if err = r.OstreeClient.SetDefaultDeployment(ctx, deploymentIndex); err != nil { utils.SetRollbackStatusFailed(ibu, err.Error()) return doNotRequeue(), nil } @@ -108,7 +111,7 @@ func (r *ImageBasedUpgradeReconciler) startRollback(ctx context.Context, ibu *ib // Write an event to indicate reboot attempt r.Recorder.Event(ibu, corev1.EventTypeNormal, "Reboot", "System will now reboot for rollback") - err = r.RebootClient.RebootToNewStateRoot("rollback") + err = r.RebootClient.RebootToNewStateRoot(ctx, "rollback") if err != nil { r.Log.Error(err, "") utils.SetRollbackStatusFailed(ibu, err.Error()) @@ -126,7 +129,7 @@ func (r *ImageBasedUpgradeReconciler) finishRollback(ibu *ibuv1.ImageBasedUpgrad //nolint:unparam func (r *ImageBasedUpgradeReconciler) handleRollback(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) (ctrl.Result, error) { - origStaterootBooted, err := r.RebootClient.IsOrigStaterootBooted(ibu.Spec.SeedImageRef.Version) + origStaterootBooted, err := r.RebootClient.IsOrigStaterootBooted(ctx, ibu.Spec.SeedImageRef.Version) if err != nil { utils.SetRollbackStatusFailed(ibu, err.Error()) return doNotRequeue(), nil diff --git a/controllers/seedgen_controller.go b/controllers/seedgen_controller.go index ad850daed4..47a0ffeb79 100644 --- a/controllers/seedgen_controller.go +++ b/controllers/seedgen_controller.go @@ -432,8 +432,8 @@ func (r *SeedGeneratorReconciler) getLcaImage(ctx context.Context) (image string } // Delete the previous imager container, if it exists -func (r *SeedGeneratorReconciler) rmPreviousImagerContainer() error { - _, err := r.Executor.Execute("podman", "rm", "-i", "-f", imagerContainerName) +func (r *SeedGeneratorReconciler) rmPreviousImagerContainer(ctx context.Context) error { + _, err := r.Executor.Execute(ctx, "podman", "rm", "-i", "-f", imagerContainerName) if err != nil { return fmt.Errorf("failed to run podman rm command: %w", err) } @@ -458,10 +458,10 @@ func (r *SeedGeneratorReconciler) getRecertImagePullSpec(seedgen *seedgenv1.Seed return } -func (r *SeedGeneratorReconciler) pullRecertImagePullSpec(seedgen *seedgenv1.SeedGenerator) error { +func (r *SeedGeneratorReconciler) pullRecertImagePullSpec(ctx context.Context, seedgen *seedgenv1.SeedGenerator) error { recertImage := r.getRecertImagePullSpec(seedgen) - _, err := r.Executor.Execute("podman", "pull", "--authfile", common.ImageRegistryAuthFile, recertImage) + _, err := r.Executor.Execute(ctx, "podman", "pull", "--authfile", common.ImageRegistryAuthFile, recertImage) if err != nil { return fmt.Errorf("failed to pull recertImage (%s): %w", recertImage, err) } @@ -470,7 +470,7 @@ func (r *SeedGeneratorReconciler) pullRecertImagePullSpec(seedgen *seedgenv1.See } // Launch a container to run the imager -func (r *SeedGeneratorReconciler) launchImager(seedgen *seedgenv1.SeedGenerator) error { +func (r *SeedGeneratorReconciler) launchImager(ctx context.Context, seedgen *seedgenv1.SeedGenerator) error { r.Log.Info("Launching imager") recertImage := r.getRecertImagePullSpec(seedgen) @@ -533,7 +533,7 @@ func (r *SeedGeneratorReconciler) launchImager(seedgen *seedgenv1.SeedGenerator) "--setenv", "NO_PROXY", } - if _, err := r.Executor.Execute("systemd-run", append(systemdRunOpts, imagerCmdArgs...)...); err != nil { + if _, err := r.Executor.Execute(ctx, "systemd-run", append(systemdRunOpts, imagerCmdArgs...)...); err != nil { return fmt.Errorf("failed to run imager container: %w", err) } @@ -542,7 +542,7 @@ func (r *SeedGeneratorReconciler) launchImager(seedgen *seedgenv1.SeedGenerator) } // checkImagerStatus examines the lca_cli container, returning nil if it exited successfully -func (r *SeedGeneratorReconciler) checkImagerStatus() error { +func (r *SeedGeneratorReconciler) checkImagerStatus(ctx context.Context) error { type ContainerState struct { Status string `json:"Status"` ExitCode int `json:"ExitCode"` @@ -557,7 +557,7 @@ func (r *SeedGeneratorReconciler) checkImagerStatus() error { r.Log.Info("Checking status of lca_cli container") - output, err := r.Executor.Execute("podman", "inspect", "--format", "json", "--log-level", "error", imagerContainerName) + output, err := r.Executor.Execute(ctx, "podman", "inspect", "--format", "json", "--log-level", "error", imagerContainerName) if err != nil { return fmt.Errorf("failed to run podman inspect command: %w", err) } @@ -587,7 +587,10 @@ func (r *SeedGeneratorReconciler) checkImagerStatus() error { // Check whether the system can be used for seed generation func (r *SeedGeneratorReconciler) validateSystem(ctx context.Context) (msg string) { // Check that the "ostree admin set-default" feature is available - if !ostreeclient.NewClient(r.Executor, false).IsOstreeAdminSetDefaultFeatureEnabled() { + if enabled, err := ostreeclient.NewClient(r.Executor, false).IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + msg = fmt.Sprintf("Failure occurred during ostree admin set-default feature check: %v", err) + return + } else if !enabled { msg = "Rejected: Installed release does not support \"ostree admin set-default\" feature" return } @@ -734,12 +737,12 @@ func (r *SeedGeneratorReconciler) wipeExistingWorkspace() error { return nil } -func (r *SeedGeneratorReconciler) setupWorkspace() error { +func (r *SeedGeneratorReconciler) setupWorkspace(ctx context.Context) error { if err := r.wipeExistingWorkspace(); err != nil { return fmt.Errorf("failed to wipe previous workspace: %w", err) } - if err := r.rmPreviousImagerContainer(); err != nil { + if err := r.rmPreviousImagerContainer(ctx); err != nil { return fmt.Errorf("failed to delete previous imager container: %w", err) } @@ -769,7 +772,7 @@ func (r *SeedGeneratorReconciler) generateSeedImage(ctx context.Context, seedgen } nextReconcile = doNotRequeue() - if err := r.setupWorkspace(); err != nil { + if err := r.setupWorkspace(ctx); err != nil { rc = err setSeedGenStatusFailed(seedgen, rc.Error()) return @@ -782,7 +785,7 @@ func (r *SeedGeneratorReconciler) generateSeedImage(ctx context.Context, seedgen return } - if err := r.pullRecertImagePullSpec(seedgen); err != nil { + if err := r.pullRecertImagePullSpec(ctx, seedgen); err != nil { rc = fmt.Errorf("failed to pull recert image: %w", err) setSeedGenStatusFailed(seedgen, rc.Error()) return @@ -927,7 +930,7 @@ func (r *SeedGeneratorReconciler) generateSeedImage(ctx context.Context, seedgen return } - if err := r.launchImager(seedgen); err != nil { + if err := r.launchImager(ctx, seedgen); err != nil { rc = fmt.Errorf("imager failed: %w", err) setSeedGenStatusFailed(seedgen, rc.Error()) return @@ -940,9 +943,9 @@ func (r *SeedGeneratorReconciler) generateSeedImage(ctx context.Context, seedgen } // finishSeedgen runs after the imager container completes and restores kubelet, once the LCA operator restarts -func (r *SeedGeneratorReconciler) finishSeedgen() error { +func (r *SeedGeneratorReconciler) finishSeedgen(ctx context.Context) error { // Check exit status of lca_cli container - if err := r.checkImagerStatus(); err != nil { + if err := r.checkImagerStatus(ctx); err != nil { return fmt.Errorf("imager container status check failed: %w", err) } @@ -1039,7 +1042,7 @@ func (r *SeedGeneratorReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.Log.Error(err, "failed to update seedgen CR status") } - if err = r.finishSeedgen(); err != nil { + if err = r.finishSeedgen(ctx); err != nil { r.Log.Error(err, "Seed generation failed") setSeedGenStatusFailed(seedgen, fmt.Sprintf("Seed generation failed: %s", err)) } else { diff --git a/controllers/upgrade_handlers.go b/controllers/upgrade_handlers.go index c59ce7fd64..c8a3c274d3 100644 --- a/controllers/upgrade_handlers.go +++ b/controllers/upgrade_handlers.go @@ -76,7 +76,7 @@ var ( func (r *ImageBasedUpgradeReconciler) handleUpgrade(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) (ctrl.Result, error) { r.Log.Info("Starting handleUpgrade") - origStaterootBooted, err := r.RebootClient.IsOrigStaterootBooted(ibu.Spec.SeedImageRef.Version) + origStaterootBooted, err := r.RebootClient.IsOrigStaterootBooted(ctx, ibu.Spec.SeedImageRef.Version) if err != nil { utils.SetUpgradeStatusFailed(ibu, err.Error()) @@ -93,7 +93,7 @@ func (r *ImageBasedUpgradeReconciler) handleUpgrade(ctx context.Context, ibu *ib } else { if ibu.Status.RollbackAvailabilityExpiration.IsZero() { // Set the rollback availability expiration field - if expiry, err := r.getRollbackAvailabilityExpiration(); err == nil { + if expiry, err := r.getRollbackAvailabilityExpiration(ctx); err == nil { ibu.Status.RollbackAvailabilityExpiration.Time = expiry } else { r.Log.Error(err, "unable to determine rollback availability expiration") @@ -162,7 +162,7 @@ func (u *UpgHandler) PrePivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) } u.Log.Info("Remounting sysroot") - if err := u.Ops.RemountSysroot(); err != nil { + if err := u.Ops.RemountSysroot(ctx); err != nil { return requeueWithError(fmt.Errorf("error while remounting sysroot: %w", err)) } @@ -238,13 +238,13 @@ func (u *UpgHandler) PrePivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade) return requeueWithError(fmt.Errorf("error while exporting for uncontrolled rollback: %w", err)) } - if err := u.setDefaultDeploymentToNewStateroot(stateroot); err != nil { + if err := u.setDefaultDeploymentToNewStateroot(ctx, stateroot); err != nil { return requeueWithError(fmt.Errorf("error while setting default deployment: %w", err)) } // Write an event to indicate reboot attempt u.Recorder.Event(ibu, v1.EventTypeNormal, "Reboot", "System will now reboot for upgrade") - err = u.RebootClient.RebootToNewStateRoot("upgrade") + err = u.RebootClient.RebootToNewStateRoot(ctx, "upgrade") if err != nil { u.Log.Error(err, "Failed to reboot to new stateroot") utils.SetUpgradeStatusFailed(ibu, err.Error()) @@ -299,14 +299,16 @@ func (u *UpgHandler) extractAndExportExtraManifests(ctx context.Context, ibu *ib return nil } -func (u *UpgHandler) setDefaultDeploymentToNewStateroot(stateroot string) error { +func (u *UpgHandler) setDefaultDeploymentToNewStateroot(ctx context.Context, stateroot string) error { // Set the new default deployment - if u.OstreeClient.IsOstreeAdminSetDefaultFeatureEnabled() { - deploymentIndex, err := u.RPMOstreeClient.GetDeploymentIndex(stateroot) + if enabled, err := u.OstreeClient.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree set-default feature: %w", err) + } else if enabled { + deploymentIndex, err := u.RPMOstreeClient.GetDeploymentIndex(ctx, stateroot) if err != nil { return fmt.Errorf("failed to get deployment index for stateroot %s: %w", stateroot, err) } - if err := u.OstreeClient.SetDefaultDeployment(deploymentIndex); err != nil { + if err := u.OstreeClient.SetDefaultDeployment(ctx, deploymentIndex); err != nil { return fmt.Errorf("failed to set default deployment at index %d: %w", deploymentIndex, err) } } @@ -349,7 +351,7 @@ var getStaterootVarPath = func(stateroot string) string { // CheckHealth helper func to call HealthChecks var CheckHealth = healthcheck.HealthChecks -func (u *UpgHandler) autoRollbackIfEnabled(ibu *ibuv1.ImageBasedUpgrade, msg string) { +func (u *UpgHandler) autoRollbackIfEnabled(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade, msg string) { // Check whether auto-rollback is disabled using annotation if val, exists := ibu.GetAnnotations()[common.AutoRollbackOnFailureUpgradeCompletionAnnotation]; exists { if val == common.AutoRollbackDisableValue { @@ -360,7 +362,7 @@ func (u *UpgHandler) autoRollbackIfEnabled(ibu *ibuv1.ImageBasedUpgrade, msg str u.Log.Info("Automatically rolling back due to failure") - if err := u.RebootClient.InitiateRollback(msg); err != nil { + if err := u.RebootClient.InitiateRollback(ctx, msg); err != nil { u.Log.Info(fmt.Sprintf("Unable to auto rollback: %s", err)) return } @@ -387,7 +389,7 @@ func (u *UpgHandler) PostPivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade if backuprestore.IsBRStorageBackendUnavailableError(err) { u.Log.Error(err, "Failed to ensure OADP configuration") utils.SetUpgradeStatusFailed(ibu, err.Error()) - u.autoRollbackIfEnabled(ibu, fmt.Sprintf("Rollback due to missing DataProtectionApplication: %s", err)) + u.autoRollbackIfEnabled(ctx, ibu, fmt.Sprintf("Rollback due to missing DataProtectionApplication: %s", err)) return doNotRequeue(), nil } utils.SetUpgradeStatusInProgress(ibu, fmt.Sprintf("Checking Application Configuration: Failure occurred: %s", err.Error())) @@ -405,7 +407,7 @@ func (u *UpgHandler) PostPivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade if extramanifest.IsEMFailedError(err) { u.Log.Error(err, "Failed to apply policy manifests") utils.SetUpgradeStatusFailed(ibu, err.Error()) - u.autoRollbackIfEnabled(ibu, fmt.Sprintf("Rollback due to failure applying policy manifests: %s", err)) + u.autoRollbackIfEnabled(ctx, ibu, fmt.Sprintf("Rollback due to failure applying policy manifests: %s", err)) return doNotRequeue(), nil } utils.SetUpgradeStatusInProgress(ibu, fmt.Sprintf("Applying Policy Manifests: Failure occurred: %s", err.Error())) @@ -422,7 +424,7 @@ func (u *UpgHandler) PostPivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade if extramanifest.IsEMFailedError(err) { u.Log.Error(err, "Failed to apply config manifests") utils.SetUpgradeStatusFailed(ibu, err.Error()) - u.autoRollbackIfEnabled(ibu, fmt.Sprintf("Rollback due to failure applying config manifests: %s", err)) + u.autoRollbackIfEnabled(ctx, ibu, fmt.Sprintf("Rollback due to failure applying config manifests: %s", err)) return doNotRequeue(), nil } utils.SetUpgradeStatusInProgress(ibu, fmt.Sprintf("Applying Config Manifests: Failure occurred: %s", err.Error())) @@ -441,7 +443,7 @@ func (u *UpgHandler) PostPivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade if backuprestore.IsBRFailedError(err) { u.Log.Error(err, "Failed to handle restore") utils.SetUpgradeStatusFailed(ibu, err.Error()) - u.autoRollbackIfEnabled(ibu, fmt.Sprintf("Rollback due to restore failure: %s", err)) + u.autoRollbackIfEnabled(ctx, ibu, fmt.Sprintf("Rollback due to restore failure: %s", err)) return doNotRequeue(), nil } utils.SetUpgradeStatusInProgress(ibu, fmt.Sprintf("Restoring Application Data: Failure occurred: %s", err)) @@ -453,7 +455,7 @@ func (u *UpgHandler) PostPivot(ctx context.Context, ibu *ibuv1.ImageBasedUpgrade return result, nil } - if err := u.RebootClient.DisableInitMonitor(); err != nil { + if err := u.RebootClient.DisableInitMonitor(ctx); err != nil { // Don't fail the upgrade on failure here, just log it u.Log.Error(err, "Unable to disable LCA init monitor") } diff --git a/controllers/upgrade_handlers_test.go b/controllers/upgrade_handlers_test.go index 5a681e1174..8332ca1b92 100644 --- a/controllers/upgrade_handlers_test.go +++ b/controllers/upgrade_handlers_test.go @@ -764,7 +764,7 @@ func TestImageBasedUpgradeReconciler_prePivot(t *testing.T) { tt.args.ibu.Spec.OADPContent = []ibuv1.ConfigMapRef{{Name: "atleast-one-oadp-to-proceed-with-export"}} } if tt.remountSysrootReturn != nil { - mockOps.EXPECT().RemountSysroot().Return(tt.remountSysrootReturn()) + mockOps.EXPECT().RemountSysroot(gomock.Any()).Return(tt.remountSysrootReturn()) } if tt.exportRestoresToDirReturn != nil { mockBackuprestore.EXPECT().ExportRestoresToDir(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.exportRestoresToDirReturn()).Times(1) @@ -829,10 +829,10 @@ func TestImageBasedUpgradeReconciler_prePivot(t *testing.T) { ibuPreStaterootPath = filepath.Join(ibuTempDirOrig, utils.IBUFilePath) } if tt.isOstreeAdminSetDefaultFeatureEnabledReturn != nil { - ostreeclientMock.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(*tt.isOstreeAdminSetDefaultFeatureEnabledReturn).Times(1) + ostreeclientMock.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(*tt.isOstreeAdminSetDefaultFeatureEnabledReturn, nil).Times(1) } if tt.rebootToNewStateRootReturn != nil { - mockRebootClient.EXPECT().RebootToNewStateRoot(gomock.Any()).Return(tt.rebootToNewStateRootReturn()).Times(1) + mockRebootClient.EXPECT().RebootToNewStateRoot(gomock.Any(), gomock.Any()).Return(tt.rebootToNewStateRootReturn()).Times(1) } uh := &UpgHandler{ @@ -1121,10 +1121,10 @@ func TestImageBasedUpgradeReconciler_postPivot(t *testing.T) { mockBackuprestore.EXPECT().RestorePVsReclaimPolicy(gomock.Any()).Return(tt.restorePVsReclaimPolicyReturn()).Times(1) } if tt.initiateRollbackReturn != nil { - mockRebootClient.EXPECT().InitiateRollback(gomock.Any()).Return(tt.initiateRollbackReturn()).Times(1) + mockRebootClient.EXPECT().InitiateRollback(gomock.Any(), gomock.Any()).Return(tt.initiateRollbackReturn()).Times(1) } if tt.disableInitMonitorReturn != nil { - mockRebootClient.EXPECT().DisableInitMonitor().Return(tt.disableInitMonitorReturn()).Times(1) + mockRebootClient.EXPECT().DisableInitMonitor(gomock.Any()).Return(tt.disableInitMonitorReturn()).Times(1) } got, err := uh.PostPivot(tt.args.ctx, tt.args.ibu) diff --git a/internal/imagemgmt/imagemgmt.go b/internal/imagemgmt/imagemgmt.go index ccdf19a8d6..20d05398ed 100644 --- a/internal/imagemgmt/imagemgmt.go +++ b/internal/imagemgmt/imagemgmt.go @@ -1,6 +1,7 @@ package imagemgmt import ( + "context" "encoding/json" "fmt" "sort" @@ -18,10 +19,10 @@ import ( //go:generate mockgen -source=imagemgmt.go -package=imagemgmt -destination=mock_imagemgmt.go type ImageMgmtIntf interface { CheckDiskUsageAgainstThreshold(thresholdPercent int) (bool, error) - GetInuseImages() (inUse []string, rc error) - GetPinnedImages() (pinned []string, rc error) - GetRemovalCandidates() (removalCandidates []imageMgmtImageInfo, rc error) - CleanupUnusedImages(thresholdPercent int) error + GetInuseImages(ctx context.Context) (inUse []string, rc error) + GetPinnedImages(ctx context.Context) (pinned []string, rc error) + GetRemovalCandidates(ctx context.Context) (removalCandidates []imageMgmtImageInfo, rc error) + CleanupUnusedImages(ctx context.Context, thresholdPercent int) error } type ImageMgmtClient struct { @@ -93,9 +94,9 @@ func (c *ImageMgmtClient) CheckDiskUsageAgainstThreshold(thresholdPercent int) ( } // GetInuseImages gets the list of images in use by cri-o or podman containers -func (c *ImageMgmtClient) GetInuseImages() (inUse []string, rc error) { +func (c *ImageMgmtClient) GetInuseImages(ctx context.Context) (inUse []string, rc error) { // Get the list of containers via crictl - output, err := c.Executor.Execute("crictl", "ps", "-a", "-o", "json") + output, err := c.Executor.Execute(ctx, "crictl", "ps", "-a", "-o", "json") if err != nil { rc = fmt.Errorf("failed to run crictl ps: %w", err) return @@ -112,7 +113,7 @@ func (c *ImageMgmtClient) GetInuseImages() (inUse []string, rc error) { } // Get the list of containers via podman - output, err = c.Executor.Execute("podman", "ps", "-a", "--format", "json", "--log-level", "error") + output, err = c.Executor.Execute(ctx, "podman", "ps", "-a", "--format", "json", "--log-level", "error") if err != nil { rc = fmt.Errorf("failed to run podman ps: %w", err) return @@ -132,9 +133,9 @@ func (c *ImageMgmtClient) GetInuseImages() (inUse []string, rc error) { } // GetPinnedImages gets the list of pinned images, from crictl -func (c *ImageMgmtClient) GetPinnedImages() (pinned []string, rc error) { +func (c *ImageMgmtClient) GetPinnedImages(ctx context.Context) (pinned []string, rc error) { // Get the list of containers via crictl - output, err := c.Executor.Execute("crictl", "images", "-o", "json") + output, err := c.Executor.Execute(ctx, "crictl", "images", "-o", "json") if err != nil { rc = fmt.Errorf("failed to run crictl images: %w", err) return @@ -183,14 +184,14 @@ func isImageUsed(inUse []string, image imageMgmtImageInfo) bool { } // GetRemovalCandidates gets the list of images sorted by creation timestamp, filtering out in-use and pinned images -func (c *ImageMgmtClient) GetRemovalCandidates() (removalCandidates []imageMgmtImageInfo, rc error) { - inUse, err := c.GetInuseImages() +func (c *ImageMgmtClient) GetRemovalCandidates(ctx context.Context) (removalCandidates []imageMgmtImageInfo, rc error) { + inUse, err := c.GetInuseImages(ctx) if err != nil { rc = fmt.Errorf("failure getting list of in-use images: %w", err) return } - pinned, err := c.GetPinnedImages() + pinned, err := c.GetPinnedImages(ctx) if err != nil { rc = fmt.Errorf("failure getting list of pinned images: %w", err) return @@ -200,7 +201,7 @@ func (c *ImageMgmtClient) GetRemovalCandidates() (removalCandidates []imageMgmtI var images []imageMgmtImageInfo - output, err := c.Executor.Execute("podman", "images", "--format", "json", "--log-level", "error") + output, err := c.Executor.Execute(ctx, "podman", "images", "--format", "json", "--log-level", "error") if err != nil { rc = fmt.Errorf("failed to run podman images command: %w", err) return @@ -236,8 +237,8 @@ func (c *ImageMgmtClient) GetRemovalCandidates() (removalCandidates []imageMgmtI // CleanupUnusedImages iterates through the image removal candidates, // deleting in sets of 5 until the container storage disk usage threshold is met -func (c *ImageMgmtClient) CleanupUnusedImages(thresholdPercent int) error { - removalCandidates, err := c.GetRemovalCandidates() +func (c *ImageMgmtClient) CleanupUnusedImages(ctx context.Context, thresholdPercent int) error { + removalCandidates, err := c.GetRemovalCandidates(ctx) if err != nil { return fmt.Errorf("failed to get removal candidates: %w", err) } @@ -255,8 +256,10 @@ func (c *ImageMgmtClient) CleanupUnusedImages(thresholdPercent int) error { args := []string{"rmi"} args = append(args, toRemove...) - // Ignore errors, as images may be in use by transient containers or other images - output, _ := c.Executor.Execute("podman", args...) + output, err := c.Executor.Execute(ctx, "podman", args...) + if err != nil { + c.Log.Info("Some images could not be removed (may be in use)", "error", err) + } c.Log.Info("Deleted unused images", "output", output) // Check container storage disk usage diff --git a/internal/imagemgmt/imagemgmt_test.go b/internal/imagemgmt/imagemgmt_test.go index 5bdeb6d019..687bdfc266 100644 --- a/internal/imagemgmt/imagemgmt_test.go +++ b/internal/imagemgmt/imagemgmt_test.go @@ -1,6 +1,7 @@ package imagemgmt import ( + "context" "reflect" "syscall" "testing" @@ -1188,10 +1189,10 @@ func Test_GetInuseImages(t *testing.T) { t.Run("test GetInUseImages", func(t *testing.T) { imageMgmtClient := NewImageMgmtClient(&log, mockExec, common.PathOutsideChroot(common.ContainerStoragePath)) - mockExec.EXPECT().Execute("crictl", "ps", "-a", "-o", "json").Return(test_CrictlPsOutput, nil) - mockExec.EXPECT().Execute("podman", "ps", "-a", "--format", "json", "--log-level", "error").Return(test_PodmanPsOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "crictl", "ps", "-a", "-o", "json").Return(test_CrictlPsOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "podman", "ps", "-a", "--format", "json", "--log-level", "error").Return(test_PodmanPsOutput, nil) - inUse, err := imageMgmtClient.GetInuseImages() + inUse, err := imageMgmtClient.GetInuseImages(context.Background()) if err != nil { t.Errorf("GetInuseImages() error = %v", err) return @@ -1227,9 +1228,9 @@ func Test_GetPinnedImages(t *testing.T) { t.Run("test GetPinnedImages", func(t *testing.T) { imageMgmtClient := NewImageMgmtClient(&log, mockExec, common.PathOutsideChroot(common.ContainerStoragePath)) - mockExec.EXPECT().Execute("crictl", "images", "-o", "json").Return(test_CrictlImagesOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "crictl", "images", "-o", "json").Return(test_CrictlImagesOutput, nil) - pinned, err := imageMgmtClient.GetPinnedImages() + pinned, err := imageMgmtClient.GetPinnedImages(context.Background()) if err != nil { t.Errorf("GetPinnedImages() error = %v", err) return @@ -1265,12 +1266,12 @@ func Test_GetRemovalCandidates(t *testing.T) { t.Run("test GetRemovalCandidates", func(t *testing.T) { imageMgmtClient := NewImageMgmtClient(&log, mockExec, common.PathOutsideChroot(common.ContainerStoragePath)) - mockExec.EXPECT().Execute("crictl", "ps", "-a", "-o", "json").Return(test_CrictlPsOutput, nil) - mockExec.EXPECT().Execute("podman", "ps", "-a", "--format", "json", "--log-level", "error").Return(test_PodmanPsOutput, nil) - mockExec.EXPECT().Execute("crictl", "images", "-o", "json").Return(test_CrictlImagesOutput, nil) - mockExec.EXPECT().Execute("podman", "images", "--format", "json", "--log-level", "error").Return(test_PodmanImagesOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "crictl", "ps", "-a", "-o", "json").Return(test_CrictlPsOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "podman", "ps", "-a", "--format", "json", "--log-level", "error").Return(test_PodmanPsOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "crictl", "images", "-o", "json").Return(test_CrictlImagesOutput, nil) + mockExec.EXPECT().Execute(gomock.Any(), "podman", "images", "--format", "json", "--log-level", "error").Return(test_PodmanImagesOutput, nil) - removalCandidates, err := imageMgmtClient.GetRemovalCandidates() + removalCandidates, err := imageMgmtClient.GetRemovalCandidates(context.Background()) if err != nil { t.Errorf("GetRemovalCandidates() error = %v", err) return diff --git a/internal/imagemgmt/mock_imagemgmt.go b/internal/imagemgmt/mock_imagemgmt.go index d870ef950e..188fe9234d 100644 --- a/internal/imagemgmt/mock_imagemgmt.go +++ b/internal/imagemgmt/mock_imagemgmt.go @@ -9,6 +9,7 @@ package imagemgmt import ( + context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -53,60 +54,60 @@ func (mr *MockImageMgmtIntfMockRecorder) CheckDiskUsageAgainstThreshold(threshol } // CleanupUnusedImages mocks base method. -func (m *MockImageMgmtIntf) CleanupUnusedImages(thresholdPercent int) error { +func (m *MockImageMgmtIntf) CleanupUnusedImages(ctx context.Context, thresholdPercent int) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CleanupUnusedImages", thresholdPercent) + ret := m.ctrl.Call(m, "CleanupUnusedImages", ctx, thresholdPercent) ret0, _ := ret[0].(error) return ret0 } // CleanupUnusedImages indicates an expected call of CleanupUnusedImages. -func (mr *MockImageMgmtIntfMockRecorder) CleanupUnusedImages(thresholdPercent any) *gomock.Call { +func (mr *MockImageMgmtIntfMockRecorder) CleanupUnusedImages(ctx, thresholdPercent any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupUnusedImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).CleanupUnusedImages), thresholdPercent) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupUnusedImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).CleanupUnusedImages), ctx, thresholdPercent) } // GetInuseImages mocks base method. -func (m *MockImageMgmtIntf) GetInuseImages() ([]string, error) { +func (m *MockImageMgmtIntf) GetInuseImages(ctx context.Context) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetInuseImages") + ret := m.ctrl.Call(m, "GetInuseImages", ctx) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInuseImages indicates an expected call of GetInuseImages. -func (mr *MockImageMgmtIntfMockRecorder) GetInuseImages() *gomock.Call { +func (mr *MockImageMgmtIntfMockRecorder) GetInuseImages(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInuseImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetInuseImages)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInuseImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetInuseImages), ctx) } // GetPinnedImages mocks base method. -func (m *MockImageMgmtIntf) GetPinnedImages() ([]string, error) { +func (m *MockImageMgmtIntf) GetPinnedImages(ctx context.Context) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPinnedImages") + ret := m.ctrl.Call(m, "GetPinnedImages", ctx) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPinnedImages indicates an expected call of GetPinnedImages. -func (mr *MockImageMgmtIntfMockRecorder) GetPinnedImages() *gomock.Call { +func (mr *MockImageMgmtIntfMockRecorder) GetPinnedImages(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPinnedImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetPinnedImages)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPinnedImages", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetPinnedImages), ctx) } // GetRemovalCandidates mocks base method. -func (m *MockImageMgmtIntf) GetRemovalCandidates() ([]imageMgmtImageInfo, error) { +func (m *MockImageMgmtIntf) GetRemovalCandidates(ctx context.Context) ([]imageMgmtImageInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRemovalCandidates") + ret := m.ctrl.Call(m, "GetRemovalCandidates", ctx) ret0, _ := ret[0].([]imageMgmtImageInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemovalCandidates indicates an expected call of GetRemovalCandidates. -func (mr *MockImageMgmtIntfMockRecorder) GetRemovalCandidates() *gomock.Call { +func (mr *MockImageMgmtIntfMockRecorder) GetRemovalCandidates(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemovalCandidates", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetRemovalCandidates)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemovalCandidates", reflect.TypeOf((*MockImageMgmtIntf)(nil).GetRemovalCandidates), ctx) } diff --git a/internal/ostreeclient/mock_ostreeclient.go b/internal/ostreeclient/mock_ostreeclient.go index f320b4a7f9..b0c128473a 100644 --- a/internal/ostreeclient/mock_ostreeclient.go +++ b/internal/ostreeclient/mock_ostreeclient.go @@ -9,6 +9,7 @@ package ostreeclient import ( + context "context" reflect "reflect" rpmostreeclient "github.com/openshift-kni/lifecycle-agent/lca-cli/ostreeclient" @@ -39,115 +40,116 @@ func (m *MockIClient) EXPECT() *MockIClientMockRecorder { } // Deploy mocks base method. -func (m *MockIClient) Deploy(osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error { +func (m *MockIClient) Deploy(ctx context.Context, osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Deploy", osname, refsepc, kargs, rpmOstreeClient, ibi) + ret := m.ctrl.Call(m, "Deploy", ctx, osname, refsepc, kargs, rpmOstreeClient, ibi) ret0, _ := ret[0].(error) return ret0 } // Deploy indicates an expected call of Deploy. -func (mr *MockIClientMockRecorder) Deploy(osname, refsepc, kargs, rpmOstreeClient, ibi any) *gomock.Call { +func (mr *MockIClientMockRecorder) Deploy(ctx, osname, refsepc, kargs, rpmOstreeClient, ibi any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockIClient)(nil).Deploy), osname, refsepc, kargs, rpmOstreeClient, ibi) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockIClient)(nil).Deploy), ctx, osname, refsepc, kargs, rpmOstreeClient, ibi) } // GetDeployment mocks base method. -func (m *MockIClient) GetDeployment(osname string) (string, error) { +func (m *MockIClient) GetDeployment(ctx context.Context, osname string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeployment", osname) + ret := m.ctrl.Call(m, "GetDeployment", ctx, osname) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeployment indicates an expected call of GetDeployment. -func (mr *MockIClientMockRecorder) GetDeployment(osname any) *gomock.Call { +func (mr *MockIClientMockRecorder) GetDeployment(ctx, osname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockIClient)(nil).GetDeployment), osname) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockIClient)(nil).GetDeployment), ctx, osname) } // GetDeploymentDir mocks base method. -func (m *MockIClient) GetDeploymentDir(osname string) (string, error) { +func (m *MockIClient) GetDeploymentDir(ctx context.Context, osname string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeploymentDir", osname) + ret := m.ctrl.Call(m, "GetDeploymentDir", ctx, osname) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeploymentDir indicates an expected call of GetDeploymentDir. -func (mr *MockIClientMockRecorder) GetDeploymentDir(osname any) *gomock.Call { +func (mr *MockIClientMockRecorder) GetDeploymentDir(ctx, osname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentDir", reflect.TypeOf((*MockIClient)(nil).GetDeploymentDir), osname) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentDir", reflect.TypeOf((*MockIClient)(nil).GetDeploymentDir), ctx, osname) } // IsOstreeAdminSetDefaultFeatureEnabled mocks base method. -func (m *MockIClient) IsOstreeAdminSetDefaultFeatureEnabled() bool { +func (m *MockIClient) IsOstreeAdminSetDefaultFeatureEnabled(ctx context.Context) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsOstreeAdminSetDefaultFeatureEnabled") + ret := m.ctrl.Call(m, "IsOstreeAdminSetDefaultFeatureEnabled", ctx) ret0, _ := ret[0].(bool) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // IsOstreeAdminSetDefaultFeatureEnabled indicates an expected call of IsOstreeAdminSetDefaultFeatureEnabled. -func (mr *MockIClientMockRecorder) IsOstreeAdminSetDefaultFeatureEnabled() *gomock.Call { +func (mr *MockIClientMockRecorder) IsOstreeAdminSetDefaultFeatureEnabled(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOstreeAdminSetDefaultFeatureEnabled", reflect.TypeOf((*MockIClient)(nil).IsOstreeAdminSetDefaultFeatureEnabled)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOstreeAdminSetDefaultFeatureEnabled", reflect.TypeOf((*MockIClient)(nil).IsOstreeAdminSetDefaultFeatureEnabled), ctx) } // OSInit mocks base method. -func (m *MockIClient) OSInit(osname string) error { +func (m *MockIClient) OSInit(ctx context.Context, osname string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OSInit", osname) + ret := m.ctrl.Call(m, "OSInit", ctx, osname) ret0, _ := ret[0].(error) return ret0 } // OSInit indicates an expected call of OSInit. -func (mr *MockIClientMockRecorder) OSInit(osname any) *gomock.Call { +func (mr *MockIClientMockRecorder) OSInit(ctx, osname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OSInit", reflect.TypeOf((*MockIClient)(nil).OSInit), osname) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OSInit", reflect.TypeOf((*MockIClient)(nil).OSInit), ctx, osname) } // PullLocal mocks base method. -func (m *MockIClient) PullLocal(repoPath string) error { +func (m *MockIClient) PullLocal(ctx context.Context, repoPath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PullLocal", repoPath) + ret := m.ctrl.Call(m, "PullLocal", ctx, repoPath) ret0, _ := ret[0].(error) return ret0 } // PullLocal indicates an expected call of PullLocal. -func (mr *MockIClientMockRecorder) PullLocal(repoPath any) *gomock.Call { +func (mr *MockIClientMockRecorder) PullLocal(ctx, repoPath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PullLocal", reflect.TypeOf((*MockIClient)(nil).PullLocal), repoPath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PullLocal", reflect.TypeOf((*MockIClient)(nil).PullLocal), ctx, repoPath) } // SetDefaultDeployment mocks base method. -func (m *MockIClient) SetDefaultDeployment(index int) error { +func (m *MockIClient) SetDefaultDeployment(ctx context.Context, index int) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetDefaultDeployment", index) + ret := m.ctrl.Call(m, "SetDefaultDeployment", ctx, index) ret0, _ := ret[0].(error) return ret0 } // SetDefaultDeployment indicates an expected call of SetDefaultDeployment. -func (mr *MockIClientMockRecorder) SetDefaultDeployment(index any) *gomock.Call { +func (mr *MockIClientMockRecorder) SetDefaultDeployment(ctx, index any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDefaultDeployment", reflect.TypeOf((*MockIClient)(nil).SetDefaultDeployment), index) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDefaultDeployment", reflect.TypeOf((*MockIClient)(nil).SetDefaultDeployment), ctx, index) } // Undeploy mocks base method. -func (m *MockIClient) Undeploy(ostreeIndex int) error { +func (m *MockIClient) Undeploy(ctx context.Context, ostreeIndex int) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Undeploy", ostreeIndex) + ret := m.ctrl.Call(m, "Undeploy", ctx, ostreeIndex) ret0, _ := ret[0].(error) return ret0 } // Undeploy indicates an expected call of Undeploy. -func (mr *MockIClientMockRecorder) Undeploy(ostreeIndex any) *gomock.Call { +func (mr *MockIClientMockRecorder) Undeploy(ctx, ostreeIndex any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Undeploy", reflect.TypeOf((*MockIClient)(nil).Undeploy), ostreeIndex) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Undeploy", reflect.TypeOf((*MockIClient)(nil).Undeploy), ctx, ostreeIndex) } diff --git a/internal/ostreeclient/ostreeclient.go b/internal/ostreeclient/ostreeclient.go index b5931e258e..1f9fdf8592 100644 --- a/internal/ostreeclient/ostreeclient.go +++ b/internal/ostreeclient/ostreeclient.go @@ -1,6 +1,7 @@ package ostreeclient import ( + "context" "fmt" "path/filepath" "strconv" @@ -13,14 +14,14 @@ import ( //go:generate mockgen -source=ostreeclient.go -package=ostreeclient -destination=mock_ostreeclient.go type IClient interface { - PullLocal(repoPath string) error - OSInit(osname string) error - Deploy(osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error - Undeploy(ostreeIndex int) error - SetDefaultDeployment(index int) error - IsOstreeAdminSetDefaultFeatureEnabled() bool - GetDeployment(osname string) (string, error) - GetDeploymentDir(osname string) (string, error) + PullLocal(ctx context.Context, repoPath string) error + OSInit(ctx context.Context, osname string) error + Deploy(ctx context.Context, osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error + Undeploy(ctx context.Context, ostreeIndex int) error + SetDefaultDeployment(ctx context.Context, index int) error + IsOstreeAdminSetDefaultFeatureEnabled(ctx context.Context) (bool, error) + GetDeployment(ctx context.Context, osname string) (string, error) + GetDeploymentDir(ctx context.Context, osname string) (string, error) } type Client struct { @@ -35,44 +36,46 @@ func NewClient(executor ops.Execute, ibi bool) IClient { } } -func (c *Client) PullLocal(repoPath string) error { +func (c *Client) PullLocal(ctx context.Context, repoPath string) error { args := []string{"pull-local"} if c.ibi { args = append(args, "--repo", "/mnt/ostree/repo") } - if _, err := c.executor.Execute("ostree", append(args, repoPath)...); err != nil { + if _, err := c.executor.Execute(ctx, "ostree", append(args, repoPath)...); err != nil { return fmt.Errorf("failed to pull local ostree with args %s, %w", args, err) } return nil } -func (c *Client) OSInit(osname string) error { +func (c *Client) OSInit(ctx context.Context, osname string) error { args := []string{"admin", "os-init"} if c.ibi { args = append(args, "--sysroot", "/mnt") } - if _, err := c.executor.Execute("ostree", append(args, osname)...); err != nil { + if _, err := c.executor.Execute(ctx, "ostree", append(args, osname)...); err != nil { return fmt.Errorf("failed to run OSInit with args %s: %w", args, err) } return nil } -func (c *Client) Deploy(osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error { +func (c *Client) Deploy(ctx context.Context, osname, refsepc string, kargs []string, rpmOstreeClient rpmostreeclient.IClient, ibi bool) error { args := []string{"admin", "deploy", "--os", osname, "--no-prune"} if c.ibi { args = append(args, "--sysroot", "/mnt") } args = append(args, kargs...) args = append(args, refsepc) - if !c.ibi && c.IsOstreeAdminSetDefaultFeatureEnabled() { + if enabled, err := c.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree admin set-default feature: %w", err) + } else if !c.ibi && enabled { args = append(args, "--not-as-default") } // Run the command in bash to preserve the quoted kargs args = append([]string{"ostree"}, args...) - if _, err := c.executor.Execute("bash", "-c", strings.Join(args, " ")); err != nil { + if _, err := c.executor.Execute(ctx, "bash", "-c", strings.Join(args, " ")); err != nil { return fmt.Errorf("failed to run OSInit with args %s: %w", args, err) } @@ -81,7 +84,7 @@ func (c *Client) Deploy(osname, refsepc string, kargs []string, rpmOstreeClient // unique commit IDs (due to import from seed), but the same checksum. In order to avoid pruning the original parent // commit and corrupting the ostree, the previous "ostree admin deploy" command was called with the "--no-prune" option. // This must also be followed up with a call to "rpm-ostree cleanup -b" to update the base refs. - if err := rpmOstreeClient.RpmOstreeCleanup(); err != nil { + if err := rpmOstreeClient.RpmOstreeCleanup(ctx); err != nil { return fmt.Errorf("failed rpm-ostree cleanup -b: %w", err) } } @@ -89,49 +92,49 @@ func (c *Client) Deploy(osname, refsepc string, kargs []string, rpmOstreeClient return nil } -func (c *Client) Undeploy(ostreeIndex int) error { +func (c *Client) Undeploy(ctx context.Context, ostreeIndex int) error { args := []string{"admin", "undeploy"} if c.ibi { args = append(args, "--sysroot", "/mnt") } args = append(args, fmt.Sprint(ostreeIndex)) - if _, err := c.executor.Execute("ostree", args...); err != nil { + if _, err := c.executor.Execute(ctx, "ostree", args...); err != nil { return fmt.Errorf("failed to run Undeploy with args %s: %w", args, err) } return nil } -func (c *Client) IsOstreeAdminSetDefaultFeatureEnabled() bool { +func (c *Client) IsOstreeAdminSetDefaultFeatureEnabled(ctx context.Context) (bool, error) { // Quick check to see if the "ostree admin set-default" feature is available - output, err := c.executor.Execute("ostree", "admin", "--help") + output, err := c.executor.Execute(ctx, "ostree", "admin", "--help") if err != nil { - return false + return false, fmt.Errorf("failed to probe ostree admin capabilities: %w", err) } - return strings.Contains(output, "set-default") + return strings.Contains(output, "set-default"), nil } -func (c *Client) SetDefaultDeployment(index int) error { +func (c *Client) SetDefaultDeployment(ctx context.Context, index int) error { if index == 0 { // Already set as default deployment return nil } args := []string{"admin", "set-default", strconv.Itoa(index)} - if _, err := c.executor.Execute("ostree", args...); err != nil { + if _, err := c.executor.Execute(ctx, "ostree", args...); err != nil { return fmt.Errorf("failed run ostree set-default with args %s: %w", args, err) } return nil } -func (c *Client) GetDeployment(stateroot string) (string, error) { +func (c *Client) GetDeployment(ctx context.Context, stateroot string) (string, error) { args := []string{"admin", "status"} if c.ibi { args = append(args, "--sysroot", common.OstreeDeployPathPrefix) } - output, err := c.executor.Execute("ostree", args...) + output, err := c.executor.Execute(ctx, "ostree", args...) if err != nil { return "", fmt.Errorf("unable to get deployment, ostree command failed: %w", err) } @@ -166,8 +169,8 @@ func (c *Client) GetDeployment(stateroot string) (string, error) { return "", nil } -func (c *Client) GetDeploymentDir(stateroot string) (string, error) { - deployment, err := c.GetDeployment(stateroot) +func (c *Client) GetDeploymentDir(ctx context.Context, stateroot string) (string, error) { + deployment, err := c.GetDeployment(ctx, stateroot) if err != nil { return "", fmt.Errorf("unable to get determine deployment dir: %w", err) } diff --git a/internal/precache/workload/pullImages.go b/internal/precache/workload/pullImages.go index 47801909c7..89c3237568 100644 --- a/internal/precache/workload/pullImages.go +++ b/internal/precache/workload/pullImages.go @@ -17,6 +17,7 @@ package workload import ( + "context" "fmt" "os" "strconv" @@ -46,28 +47,28 @@ var ( ) // CheckPodman verifies that podman is running by checking the version of podman -func CheckPodman() bool { - if _, err := Executor.ExecuteWithLiveLogger("podman", []string{"version"}...); err != nil { +func CheckPodman(ctx context.Context) bool { + if _, err := Executor.ExecuteWithLiveLogger(ctx, "podman", []string{"version"}...); err != nil { return false } return true } // podmanImgPull pulls the specified image via podman CLI -func podmanImgPull(image, authFile string) error { +func podmanImgPull(ctx context.Context, image, authFile string) error { args := []string{"pull", image} if authFile != "" { args = append(args, []string{"--authfile", authFile}...) } - if _, err := Executor.Execute("podman", args...); err != nil { + if _, err := Executor.Execute(ctx, "podman", args...); err != nil { return fmt.Errorf("failed podman pull with args %s: %w", args, err) } return nil } -func podmanImgExists(image string) bool { +func podmanImgExists(ctx context.Context, image string) bool { args := []string{"image", "exists", image} - _, err := Executor.Execute("podman", args...) + _, err := Executor.Execute(ctx, "podman", args...) if err != nil { log.Errorf("failed podman image check with args %s: %v", args, err) } @@ -75,11 +76,11 @@ func podmanImgExists(image string) bool { } // pullImage attempts to pull an image via podman CLI -func pullImage(image, authFile string, progress *precache.Progress) error { +func pullImage(ctx context.Context, image, authFile string, progress *precache.Progress) error { var err error for i := 0; i < MaxRetries; i++ { - err = podmanImgPull(image, authFile) + err = podmanImgPull(ctx, image, authFile) if err == nil { log.Infof("Successfully pulled image: %s", image) break @@ -119,7 +120,7 @@ func GetAuthFile() (string, error) { } // PullImages pulls a list of images using podman -func PullImages(precacheSpec []string, authFile string) *precache.Progress { +func PullImages(ctx context.Context, precacheSpec []string, authFile string) *precache.Progress { // Initialize progress tracking progress := &precache.Progress{ @@ -148,7 +149,7 @@ func PullImages(precacheSpec []string, authFile string) *precache.Progress { <-threads wg.Done() }() - err := pullImage(image, authFile, progress) + err := pullImage(ctx, image, authFile, progress) if err != nil { log.Errorf("Failed to pull image: %s, error: %v", image, err) @@ -170,13 +171,13 @@ func PullImages(precacheSpec []string, authFile string) *precache.Progress { return progress } -func ValidatePrecache(status *precache.Progress, bestEffort bool) error { +func ValidatePrecache(ctx context.Context, status *precache.Progress, bestEffort bool) error { // Check pre-caching execution status if status.Failed != 0 { var imagesFound []string log.Info("Failed to pre-cache the following images:") for _, image := range status.FailedPullList { - if podmanImgExists(image) { + if podmanImgExists(ctx, image) { log.Infof("%s, but found locally after downloading other images", image) imagesFound = append(imagesFound, image) } else { @@ -196,12 +197,12 @@ func ValidatePrecache(status *precache.Progress, bestEffort bool) error { return nil } -func Precache(precacheSpec []string, authFile string, bestEffort bool) error { +func Precache(ctx context.Context, precacheSpec []string, authFile string, bestEffort bool) error { // Pre-cache images - status := PullImages(precacheSpec, authFile) + status := PullImages(ctx, precacheSpec, authFile) log.Info("Completed executing pre-caching") - if err := ValidatePrecache(status, bestEffort); err != nil { + if err := ValidatePrecache(ctx, status, bestEffort); err != nil { return fmt.Errorf("failed to pre-cache one or more images") } diff --git a/internal/prep/prep.go b/internal/prep/prep.go index fef8454a01..01ffff082e 100644 --- a/internal/prep/prep.go +++ b/internal/prep/prep.go @@ -2,6 +2,7 @@ package prep import ( "bufio" + "context" "encoding/json" "fmt" "os" @@ -90,11 +91,11 @@ func getDeploymentFromDeploymentID(deploymentID string) (string, error) { return splitted[len(splitted)-1], nil } -func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.IClient, +func SetupStateroot(ctx context.Context, log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.IClient, rpmOstreeClient rpmostreeclient.IClient, seedImage, expectedVersion string, ibi bool) error { log.Info("Start setupstateroot") - defer func() { _ = ops.UnmountAndRemoveImage(seedImage) }() + defer func() { _ = ops.UnmountAndRemoveImage(context.WithoutCancel(ctx), seedImage) }() workspaceOutsideChroot, err := os.MkdirTemp(common.PathOutsideChroot("/var/tmp"), "") if err != nil { @@ -114,13 +115,13 @@ func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.ICli log.Info("workspace:" + workspace) if !ibi { - if err = ops.RemountSysroot(); err != nil { + if err = ops.RemountSysroot(ctx); err != nil { return fmt.Errorf("failed to remount /sysroot: %w", err) } } - mountpoint, err := ops.MountImage(seedImage) + mountpoint, err := ops.MountImage(ctx, seedImage) if err != nil { return fmt.Errorf("failed to mount seed image: %w", err) } @@ -130,7 +131,7 @@ func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.ICli return fmt.Errorf("failed to create ostree repo directory: %w", err) } - if err := ops.ExtractTarWithSELinux( + if err := ops.ExtractTarWithSELinux(ctx, fmt.Sprintf("%s/ostree.tgz", mountpoint), ostreeRepo, ); err != nil { return fmt.Errorf("failed to extract ostree.tgz: %w", err) @@ -162,11 +163,11 @@ func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.ICli osname := common.GetStaterootName(expectedVersion) - if err = ostreeClient.PullLocal(ostreeRepo); err != nil { + if err = ostreeClient.PullLocal(ctx, ostreeRepo); err != nil { return fmt.Errorf("failed ostree pull-local: %w", err) } - if err = ostreeClient.OSInit(osname); err != nil { + if err = ostreeClient.OSInit(ctx, osname); err != nil { return fmt.Errorf("failed ostree admin os-init: %w", err) } @@ -180,11 +181,11 @@ func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.ICli kargs = append(kargs, "--karg", "ibu="+expectedVersion) } - if err = ostreeClient.Deploy(osname, seedBootedRef, kargs, rpmOstreeClient, ibi); err != nil { + if err = ostreeClient.Deploy(ctx, osname, seedBootedRef, kargs, rpmOstreeClient, ibi); err != nil { return fmt.Errorf("failed ostree admin deploy: %w", err) } - deploymentDir, err := ostreeClient.GetDeploymentDir(osname) + deploymentDir, err := ostreeClient.GetDeploymentDir(ctx, osname) if err != nil { return fmt.Errorf("failed to get deployment dir: %w", err) } @@ -196,14 +197,14 @@ func SetupStateroot(log logr.Logger, ops ops.Ops, ostreeClient ostreeclient.ICli return fmt.Errorf("failed to restore origin file: %w", err) } - if err = ops.ExtractTarWithSELinux( + if err = ops.ExtractTarWithSELinux(ctx, filepath.Join(mountpoint, "var.tgz"), common.GetStaterootPath(osname), ); err != nil { return fmt.Errorf("failed to restore var directory: %w", err) } - if err := ops.ExtractTarWithSELinux( + if err := ops.ExtractTarWithSELinux(ctx, filepath.Join(mountpoint, "etc.tgz"), deploymentDir, ); err != nil { diff --git a/internal/reboot/mock_reboot.go b/internal/reboot/mock_reboot.go index 5818d76802..b165001a0e 100644 --- a/internal/reboot/mock_reboot.go +++ b/internal/reboot/mock_reboot.go @@ -9,6 +9,7 @@ package reboot import ( + context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -38,58 +39,58 @@ func (m *MockRebootIntf) EXPECT() *MockRebootIntfMockRecorder { } // AutoRollbackIfEnabled mocks base method. -func (m *MockRebootIntf) AutoRollbackIfEnabled(component, msg string) { +func (m *MockRebootIntf) AutoRollbackIfEnabled(ctx context.Context, component, msg string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AutoRollbackIfEnabled", component, msg) + m.ctrl.Call(m, "AutoRollbackIfEnabled", ctx, component, msg) } // AutoRollbackIfEnabled indicates an expected call of AutoRollbackIfEnabled. -func (mr *MockRebootIntfMockRecorder) AutoRollbackIfEnabled(component, msg any) *gomock.Call { +func (mr *MockRebootIntfMockRecorder) AutoRollbackIfEnabled(ctx, component, msg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoRollbackIfEnabled", reflect.TypeOf((*MockRebootIntf)(nil).AutoRollbackIfEnabled), component, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoRollbackIfEnabled", reflect.TypeOf((*MockRebootIntf)(nil).AutoRollbackIfEnabled), ctx, component, msg) } // DisableInitMonitor mocks base method. -func (m *MockRebootIntf) DisableInitMonitor() error { +func (m *MockRebootIntf) DisableInitMonitor(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DisableInitMonitor") + ret := m.ctrl.Call(m, "DisableInitMonitor", ctx) ret0, _ := ret[0].(error) return ret0 } // DisableInitMonitor indicates an expected call of DisableInitMonitor. -func (mr *MockRebootIntfMockRecorder) DisableInitMonitor() *gomock.Call { +func (mr *MockRebootIntfMockRecorder) DisableInitMonitor(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableInitMonitor", reflect.TypeOf((*MockRebootIntf)(nil).DisableInitMonitor)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableInitMonitor", reflect.TypeOf((*MockRebootIntf)(nil).DisableInitMonitor), ctx) } // InitiateRollback mocks base method. -func (m *MockRebootIntf) InitiateRollback(msg string) error { +func (m *MockRebootIntf) InitiateRollback(ctx context.Context, msg string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InitiateRollback", msg) + ret := m.ctrl.Call(m, "InitiateRollback", ctx, msg) ret0, _ := ret[0].(error) return ret0 } // InitiateRollback indicates an expected call of InitiateRollback. -func (mr *MockRebootIntfMockRecorder) InitiateRollback(msg any) *gomock.Call { +func (mr *MockRebootIntfMockRecorder) InitiateRollback(ctx, msg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiateRollback", reflect.TypeOf((*MockRebootIntf)(nil).InitiateRollback), msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiateRollback", reflect.TypeOf((*MockRebootIntf)(nil).InitiateRollback), ctx, msg) } // IsOrigStaterootBooted mocks base method. -func (m *MockRebootIntf) IsOrigStaterootBooted(identifier string) (bool, error) { +func (m *MockRebootIntf) IsOrigStaterootBooted(ctx context.Context, identifier string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsOrigStaterootBooted", identifier) + ret := m.ctrl.Call(m, "IsOrigStaterootBooted", ctx, identifier) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsOrigStaterootBooted indicates an expected call of IsOrigStaterootBooted. -func (mr *MockRebootIntfMockRecorder) IsOrigStaterootBooted(identifier any) *gomock.Call { +func (mr *MockRebootIntfMockRecorder) IsOrigStaterootBooted(ctx, identifier any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOrigStaterootBooted", reflect.TypeOf((*MockRebootIntf)(nil).IsOrigStaterootBooted), identifier) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOrigStaterootBooted", reflect.TypeOf((*MockRebootIntf)(nil).IsOrigStaterootBooted), ctx, identifier) } // ReadAutoRollbackConfigFile mocks base method. @@ -108,29 +109,29 @@ func (mr *MockRebootIntfMockRecorder) ReadAutoRollbackConfigFile() *gomock.Call } // Reboot mocks base method. -func (m *MockRebootIntf) Reboot(rationale string) error { +func (m *MockRebootIntf) Reboot(ctx context.Context, rationale string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Reboot", rationale) + ret := m.ctrl.Call(m, "Reboot", ctx, rationale) ret0, _ := ret[0].(error) return ret0 } // Reboot indicates an expected call of Reboot. -func (mr *MockRebootIntfMockRecorder) Reboot(rationale any) *gomock.Call { +func (mr *MockRebootIntfMockRecorder) Reboot(ctx, rationale any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reboot", reflect.TypeOf((*MockRebootIntf)(nil).Reboot), rationale) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reboot", reflect.TypeOf((*MockRebootIntf)(nil).Reboot), ctx, rationale) } // RebootToNewStateRoot mocks base method. -func (m *MockRebootIntf) RebootToNewStateRoot(rationale string) error { +func (m *MockRebootIntf) RebootToNewStateRoot(ctx context.Context, rationale string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RebootToNewStateRoot", rationale) + ret := m.ctrl.Call(m, "RebootToNewStateRoot", ctx, rationale) ret0, _ := ret[0].(error) return ret0 } // RebootToNewStateRoot indicates an expected call of RebootToNewStateRoot. -func (mr *MockRebootIntfMockRecorder) RebootToNewStateRoot(rationale any) *gomock.Call { +func (mr *MockRebootIntfMockRecorder) RebootToNewStateRoot(ctx, rationale any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RebootToNewStateRoot", reflect.TypeOf((*MockRebootIntf)(nil).RebootToNewStateRoot), rationale) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RebootToNewStateRoot", reflect.TypeOf((*MockRebootIntf)(nil).RebootToNewStateRoot), ctx, rationale) } diff --git a/internal/reboot/reboot.go b/internal/reboot/reboot.go index 47c53d156f..07797f2e3e 100644 --- a/internal/reboot/reboot.go +++ b/internal/reboot/reboot.go @@ -1,6 +1,7 @@ package reboot import ( + "context" "encoding/json" "fmt" "os" @@ -41,12 +42,12 @@ type AutoRollbackConfig struct { //go:generate mockgen -source=reboot.go -package=reboot -destination=mock_reboot.go type RebootIntf interface { ReadAutoRollbackConfigFile() (*AutoRollbackConfig, error) - DisableInitMonitor() error - Reboot(rationale string) error - RebootToNewStateRoot(rationale string) error - IsOrigStaterootBooted(identifier string) (bool, error) - InitiateRollback(msg string) error - AutoRollbackIfEnabled(component, msg string) + DisableInitMonitor(ctx context.Context) error + Reboot(ctx context.Context, rationale string) error + RebootToNewStateRoot(ctx context.Context, rationale string) error + IsOrigStaterootBooted(ctx context.Context, identifier string) (bool, error) + InitiateRollback(ctx context.Context, msg string) error + AutoRollbackIfEnabled(ctx context.Context, component, msg string) } type IBURebootClient struct { @@ -143,19 +144,19 @@ func (c *IBURebootClient) ReadAutoRollbackConfigFile() (*AutoRollbackConfig, err return rollbackCfg, nil } -func (c *IBURebootClient) DisableInitMonitor() error { +func (c *IBURebootClient) DisableInitMonitor(ctx context.Context) error { // Check whether service-unit is active before stopping. The "stop" command will exit with 0 if already stopped, // but would return a failure if the service-unit doesn't exist (for whatever reason). - if _, err := c.hostCommandsExecutor.Execute("systemctl", "is-active", common.IBUInitMonitorService); err == nil { - if _, err := c.hostCommandsExecutor.Execute("systemctl", "stop", common.IBUInitMonitorService); err != nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "is-active", common.IBUInitMonitorService); err == nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "stop", common.IBUInitMonitorService); err != nil { return fmt.Errorf("failed to stop %s: %w", common.IBUInitMonitorService, err) } } // Check whether service-unit is enabled before dsiabling. The "disable" command will exit with 0 if already disabled, // but would return a failure if the service-unit doesn't exist (for whatever reason). - if _, err := c.hostCommandsExecutor.Execute("systemctl", "is-enabled", common.IBUInitMonitorService); err == nil { - if _, err := c.hostCommandsExecutor.Execute("systemctl", "disable", common.IBUInitMonitorService); err != nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "is-enabled", common.IBUInitMonitorService); err == nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "disable", common.IBUInitMonitorService); err != nil { return fmt.Errorf("failed to disable %s: %w", common.IBUInitMonitorService, err) } } @@ -164,20 +165,20 @@ func (c *IBURebootClient) DisableInitMonitor() error { return fmt.Errorf("failed to delete %s: %w", common.IBUInitMonitorServiceFile, err) } - if _, err := c.hostCommandsExecutor.Execute("systemctl", "daemon-reload"); err != nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "daemon-reload"); err != nil { return fmt.Errorf("systemctl daemon-reload failed after deleting %s: %w", common.IBUInitMonitorServiceFile, err) } return nil } -func (c *IBURebootClient) RebootToNewStateRoot(rationale string) error { +func (c *IBURebootClient) RebootToNewStateRoot(ctx context.Context, rationale string) error { c.log.Info(fmt.Sprintf("rebooting to a new stateroot: %s", rationale)) - return c.Reboot(rationale) + return c.Reboot(ctx, rationale) } -func (c *IBURebootClient) Reboot(rationale string) error { - _, err := c.hostCommandsExecutor.Execute("systemd-run", "--unit", "lifecycle-agent-reboot", +func (c *IBURebootClient) Reboot(ctx context.Context, rationale string) error { + _, err := c.hostCommandsExecutor.Execute(ctx, "systemd-run", "--unit", "lifecycle-agent-reboot", "--description", fmt.Sprintf("\"lifecycle-agent: %s\"", rationale), "systemctl", "--message=\"Image Based Upgrade\"", "reboot") if err != nil { @@ -190,8 +191,8 @@ func (c *IBURebootClient) Reboot(rationale string) error { return fmt.Errorf("failed to reboot. This should never happen! Please check the system") } -func (c *IBURebootClient) IsOrigStaterootBooted(identifier string) (bool, error) { - currentStaterootName, err := c.rpmOstreeClient.GetCurrentStaterootName() +func (c *IBURebootClient) IsOrigStaterootBooted(ctx context.Context, identifier string) (bool, error) { + currentStaterootName, err := c.rpmOstreeClient.GetCurrentStaterootName(ctx) if err != nil { return false, fmt.Errorf("failed to get current stateroot name: %w", err) } @@ -204,18 +205,20 @@ func (c *IBURebootClient) IsOrigStaterootBooted(identifier string) (bool, error) return currentStaterootName != common.GetStaterootName(identifier), nil } -func (c *IBURebootClient) InitiateRollback(msg string) error { - if !c.ostreeClient.IsOstreeAdminSetDefaultFeatureEnabled() { +func (c *IBURebootClient) InitiateRollback(ctx context.Context, msg string) error { + if enabled, err := c.ostreeClient.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree set-default feature: %w", err) + } else if !enabled { return fmt.Errorf("automatic rollback not supported in this release") } c.log.Info("Updating saved IBU CR with status msg for rollback") - stateroot, err := c.rpmOstreeClient.GetUnbootedStaterootName() + stateroot, err := c.rpmOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { return fmt.Errorf("unable to determine stateroot path for rollback: %w", err) } - if err := c.ops.RemountSysroot(); err != nil { + if err := c.ops.RemountSysroot(ctx); err != nil { return fmt.Errorf("unable to remount sysroot: %w", err) } @@ -234,20 +237,20 @@ func (c *IBURebootClient) InitiateRollback(msg string) error { c.log.Info("Iniating rollback") - deploymentIndex, err := c.rpmOstreeClient.GetUnbootedDeploymentIndex() + deploymentIndex, err := c.rpmOstreeClient.GetUnbootedDeploymentIndex(ctx) if err != nil { return fmt.Errorf("unable to get unbooted deployment for automatic rollback: %w", err) } - if err = c.ostreeClient.SetDefaultDeployment(deploymentIndex); err != nil { + if err = c.ostreeClient.SetDefaultDeployment(ctx, deploymentIndex); err != nil { return fmt.Errorf("unable to get set deployment for automatic rollback: %w", err) } - err = c.RebootToNewStateRoot("rollback") + err = c.RebootToNewStateRoot(ctx, "rollback") return fmt.Errorf("unable to get set deployment for automatic rollback: %w", err) } -func (c *IBURebootClient) AutoRollbackIfEnabled(component, msg string) { +func (c *IBURebootClient) AutoRollbackIfEnabled(ctx context.Context, component, msg string) { rollbackCfg, err := c.ReadAutoRollbackConfigFile() if err != nil { if os.IsNotExist(err) { @@ -265,7 +268,7 @@ func (c *IBURebootClient) AutoRollbackIfEnabled(component, msg string) { } c.log.Info(fmt.Sprintf("Auto-rollback is enabled for component: %s", component)) - if err = c.InitiateRollback(msg); err != nil { + if err = c.InitiateRollback(ctx, msg); err != nil { c.log.Info(fmt.Sprintf("Unable to initiate rollback: %s", err)) } @@ -368,9 +371,9 @@ func (c *IPCRebootClient) ReadAutoRollbackConfigFile() (*AutoRollbackConfig, err } // DisableInitMonitor stops the transient init-monitor unit for IP config if running. -func (c *IPCRebootClient) DisableInitMonitor() error { - if _, err := c.hostCommandsExecutor.Execute("systemctl", "is-active", common.IPCInitMonitorService); err == nil { - if _, err := c.hostCommandsExecutor.Execute("systemctl", "stop", common.IPCInitMonitorService); err != nil { +func (c *IPCRebootClient) DisableInitMonitor(ctx context.Context) error { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "is-active", common.IPCInitMonitorService); err == nil { + if _, err := c.hostCommandsExecutor.Execute(ctx, "systemctl", "stop", common.IPCInitMonitorService); err != nil { return fmt.Errorf("failed to stop %s: %w", common.IPCInitMonitorService, err) } } @@ -378,27 +381,29 @@ func (c *IPCRebootClient) DisableInitMonitor() error { } // InitiateRollback for IP config simply sets default to the unbooted deployment and reboots. -func (c *IPCRebootClient) InitiateRollback(msg string) error { - if !c.ostreeClient.IsOstreeAdminSetDefaultFeatureEnabled() { +func (c *IPCRebootClient) InitiateRollback(ctx context.Context, msg string) error { + if enabled, err := c.ostreeClient.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree set-default feature: %w", err) + } else if !enabled { return fmt.Errorf("automatic rollback not supported in this release") } c.log.Info("Initiating IPConfig rollback", "reason", msg) - deploymentIndex, err := c.rpmOstreeClient.GetUnbootedDeploymentIndex() + deploymentIndex, err := c.rpmOstreeClient.GetUnbootedDeploymentIndex(ctx) if err != nil { return fmt.Errorf("unable to get unbooted deployment for automatic IPConfig rollback: %w", err) } - if err = c.ostreeClient.SetDefaultDeployment(deploymentIndex); err != nil { + if err = c.ostreeClient.SetDefaultDeployment(ctx, deploymentIndex); err != nil { return fmt.Errorf("unable to set default deployment for automatic IPConfig rollback: %w", err) } - if err := c.ops.RemountSysroot(); err != nil { + if err := c.ops.RemountSysroot(ctx); err != nil { return fmt.Errorf("failed to remount sysroot: %w", err) } - oldStateroot, err := c.rpmOstreeClient.GetUnbootedStaterootName() + oldStateroot, err := c.rpmOstreeClient.GetUnbootedStaterootName(ctx) if err != nil { return fmt.Errorf("failed to get unbooted stateroot name: %w", err) } @@ -415,11 +420,11 @@ func (c *IPCRebootClient) InitiateRollback(msg string) error { return fmt.Errorf("unable to save updated IPC CR to %s: %w", savedIPCPath, err) } - return c.RebootToNewStateRoot("ip-config rollback") + return c.RebootToNewStateRoot(ctx, "ip-config rollback") } // AutoRollbackIfEnabled reads the IPC config and triggers rollback if enabled for the component. -func (c *IPCRebootClient) AutoRollbackIfEnabled(component, msg string) { +func (c *IPCRebootClient) AutoRollbackIfEnabled(ctx context.Context, component, msg string) { rollbackCfg, err := c.ReadAutoRollbackConfigFile() // reads IPC path if err != nil { if os.IsNotExist(err) { @@ -435,13 +440,13 @@ func (c *IPCRebootClient) AutoRollbackIfEnabled(component, msg string) { } c.log.Info(fmt.Sprintf("IPConfig auto-rollback is enabled for component: %s", component)) - if err = c.InitiateRollback(msg); err != nil { + if err = c.InitiateRollback(ctx, msg); err != nil { c.log.Info(fmt.Sprintf("Unable to initiate IPConfig rollback: %s", err)) } } -func (c *IPCRebootClient) IsOrigStaterootBooted(identifier string) (bool, error) { - currentStaterootName, err := c.rpmOstreeClient.GetCurrentStaterootName() +func (c *IPCRebootClient) IsOrigStaterootBooted(ctx context.Context, identifier string) (bool, error) { + currentStaterootName, err := c.rpmOstreeClient.GetCurrentStaterootName(ctx) if err != nil { return false, fmt.Errorf("failed to get current stateroot name: %w", err) } @@ -454,13 +459,13 @@ func (c *IPCRebootClient) IsOrigStaterootBooted(identifier string) (bool, error) return currentStaterootName != common.GetStaterootName(identifier), nil } -func (c *IPCRebootClient) RebootToNewStateRoot(rationale string) error { +func (c *IPCRebootClient) RebootToNewStateRoot(ctx context.Context, rationale string) error { c.log.Info(fmt.Sprintf("rebooting to a new stateroot: %s", rationale)) - return c.Reboot(rationale) + return c.Reboot(ctx, rationale) } -func (c *IPCRebootClient) Reboot(rationale string) error { - _, err := c.hostCommandsExecutor.Execute("systemd-run", "--unit", "lifecycle-agent-reboot", +func (c *IPCRebootClient) Reboot(ctx context.Context, rationale string) error { + _, err := c.hostCommandsExecutor.Execute(ctx, "systemd-run", "--unit", "lifecycle-agent-reboot", "--description", fmt.Sprintf("\"lifecycle-agent: %s\"", rationale), "systemctl", "--message=\"IP Config\"", "reboot") if err != nil { diff --git a/internal/reboot/reboot_test.go b/internal/reboot/reboot_test.go index b1f0e02915..a27fb46367 100644 --- a/internal/reboot/reboot_test.go +++ b/internal/reboot/reboot_test.go @@ -1,6 +1,7 @@ package reboot import ( + "context" "testing" "github.com/go-logr/logr" @@ -59,8 +60,8 @@ func TestIsOrigStaterootBooted(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rebootClient := NewIBURebootClient(&tt.args.log, tt.args.executor, tt.args.r, tt.args.ostreeClient, tt.args.ops) - mockRpmostreeclient.EXPECT().GetCurrentStaterootName().Return(tt.currentStateRoot, nil).Times(1) - got, err := rebootClient.IsOrigStaterootBooted(tt.args.ibu.Spec.SeedImageRef.Version) + mockRpmostreeclient.EXPECT().GetCurrentStaterootName(gomock.Any()).Return(tt.currentStateRoot, nil).Times(1) + got, err := rebootClient.IsOrigStaterootBooted(context.Background(), tt.args.ibu.Spec.SeedImageRef.Version) if (err != nil) != tt.wantErr { t.Errorf("IsOrigStaterootBooted() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/lca-cli/cmd/create.go b/lca-cli/cmd/create.go index 9946de9237..b13f9b6a94 100644 --- a/lca-cli/cmd/create.go +++ b/lca-cli/cmd/create.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "context" "fmt" ibuv1 "github.com/openshift-kni/lifecycle-agent/api/imagebasedupgrade/v1" @@ -69,7 +70,7 @@ var createCmd = &cobra.Command{ Use: "create", Short: "Create OCI image and push it to a container registry.", Run: func(cmd *cobra.Command, args []string) { - if err := create(); err != nil { + if err := create(cmd.Context()); err != nil { log.Fatalf("Error executing create command: %v", err) } }, @@ -84,7 +85,7 @@ func init() { addCommonFlags(createCmd) } -func create() error { +func create(ctx context.Context) error { var err error log.Info("OCI image creation has started") @@ -96,7 +97,7 @@ func create() error { if !skipCleanup { defer func() { if err = seedrestoration.NewSeedRestoration(log, op, common.BackupDir, containerRegistry, - authFile, recertContainerImage, recertSkipValidation).RestoreSeedCluster(); err != nil { + authFile, recertContainerImage, recertSkipValidation).RestoreSeedCluster(context.Background()); err != nil { log.Fatalf("Failed to restore seed cluster: %v", err) } log.Info("Seed cluster restored successfully!") @@ -115,7 +116,7 @@ func create() error { seedCreator := seedcreator.NewSeedCreator(client, log, op, rpmOstreeClient, common.BackupDir, common.KubeconfigFile, containerRegistry, authFile, recertContainerImage, recertSkipValidation) - if err = seedCreator.CreateSeedImage(); err != nil { + if err = seedCreator.CreateSeedImage(ctx); err != nil { err = fmt.Errorf("failed to create seed image: %w", err) log.Error(err) return err diff --git a/lca-cli/cmd/ibi.go b/lca-cli/cmd/ibi.go index 980dc57a8c..c4350d8acd 100644 --- a/lca-cli/cmd/ibi.go +++ b/lca-cli/cmd/ibi.go @@ -17,6 +17,8 @@ limitations under the License. package cmd import ( + "context" + "fmt" "os" preinstallUtils "github.com/rh-ecosystem-edge/preinstall-utils/pkg" @@ -35,7 +37,7 @@ var ibi = &cobra.Command{ Use: "ibi", Short: "prepare ibi", Run: func(cmd *cobra.Command, args []string) { - runIBI() + runIBI(cmd.Context()) }, } @@ -52,7 +54,7 @@ func init() { _ = rootCmd.MarkFlagRequired("configuration-file") } -func runIBI() { +func runIBI(ctx context.Context) { config, err := utils.ReadIBIConfigFile(configurationFile) if err != nil { @@ -68,15 +70,29 @@ func runIBI() { hostCommandsExecutor = ops.NewRegularExecutor(log, true) } - cleanupDevice := preinstallUtils.NewCleanupDevice(log, preinstallUtils.NewDiskOps(log, hostCommandsExecutor)) + cleanupDevice := preinstallUtils.NewCleanupDevice(log, preinstallUtils.NewDiskOps(log, &preinstallExecutorAdapter{exec: hostCommandsExecutor})) rpmOstreeClient := ostree.NewClient("lca-cli", hostCommandsExecutor) ostreeClient := ostreeclient.NewClient(hostCommandsExecutor, true) ibiRunner := ibipreparation.NewIBIPrepare(log, ops.NewOps(log, hostCommandsExecutor), rpmOstreeClient, ostreeClient, cleanupDevice, config) - if err := ibiRunner.Run(); err != nil { + if err := ibiRunner.Run(ctx); err != nil { log.Fatal(err) } log.Info("IBI preparation process finished successfully!") } + +// preinstallExecutorAdapter wraps ops.Execute (with context.Context) to satisfy +// the preinstall-utils shared_ops.Execute interface (without context.Context). +type preinstallExecutorAdapter struct { + exec ops.Execute +} + +func (a *preinstallExecutorAdapter) Execute(command string, args ...string) (string, error) { + out, err := a.exec.Execute(context.Background(), command, args...) + if err != nil { + return out, fmt.Errorf("execute %s: %w", command, err) + } + return out, nil +} diff --git a/lca-cli/cmd/ibuPrecacheWorkload.go b/lca-cli/cmd/ibuPrecacheWorkload.go index d78b31906b..6eeba5b1e0 100644 --- a/lca-cli/cmd/ibuPrecacheWorkload.go +++ b/lca-cli/cmd/ibuPrecacheWorkload.go @@ -22,6 +22,8 @@ import ( "strings" "syscall" + "context" + "github.com/openshift-kni/lifecycle-agent/internal/common" "github.com/openshift-kni/lifecycle-agent/internal/precache" "github.com/openshift-kni/lifecycle-agent/internal/precache/workload" @@ -36,7 +38,7 @@ var ibuPrecacheWorkloadCmd = &cobra.Command{ Short: "Start precache during IBU", Long: `Start precache during IBU. This is to be called from a k8s job!`, Run: func(cmd *cobra.Command, args []string) { - ibuPrecacheWorkloadRun() + ibuPrecacheWorkloadRun(cmd.Context()) }, } @@ -86,7 +88,7 @@ func readPrecacheSpecFile() (precacheSpec []string, err error) { return precacheSpec, nil } -func ibuPrecacheWorkloadRun() { +func ibuPrecacheWorkloadRun(ctx context.Context) { log.Info("Starting to execute pre-cache workload") bestEffort := false @@ -111,7 +113,7 @@ func ibuPrecacheWorkloadRun() { log.Infof("chroot %s successful", common.Host) // Pre-check: Verify podman is running - if !workload.CheckPodman() { + if !workload.CheckPodman(ctx) { terminateOnError(fmt.Errorf("failed to execute podman command")) } log.Info("podman is running, proceeding to pre-cache images!") @@ -120,7 +122,7 @@ func ibuPrecacheWorkloadRun() { if err != nil { terminateOnError(err) } - if err := workload.Precache(precacheSpec, authFile, bestEffort); err != nil { + if err := workload.Precache(ctx, precacheSpec, authFile, bestEffort); err != nil { terminateOnError(err) } } diff --git a/lca-cli/cmd/ibuStaterootSetup.go b/lca-cli/cmd/ibuStaterootSetup.go index ec30159ccc..a545941009 100644 --- a/lca-cli/cmd/ibuStaterootSetup.go +++ b/lca-cli/cmd/ibuStaterootSetup.go @@ -37,7 +37,7 @@ var ibuStaterootSetupCmd = &cobra.Command{ Short: "Setup a new stateroot during IBU", Long: `Setup stateroot during IBU. This is meant to be used as k8s job!`, Run: func(cmd *cobra.Command, args []string) { - if err := ibuStaterootSetupRun(); err != nil { + if err := ibuStaterootSetupRun(cmd.Context()); err != nil { log.Error(err) os.Exit(1) } @@ -49,13 +49,13 @@ func init() { } // ibuStaterootSetupRun main function to do stateroot setup -func ibuStaterootSetupRun() error { +func ibuStaterootSetupRun(parentCtx context.Context) error { var ( hostCommandsExecutor = ops.NewChrootExecutor(log, true, common.Host) opsClient = ops.NewOps(log, hostCommandsExecutor) rpmOstreeClient = ostree.NewClient("lca-cli-stateroot-setup", hostCommandsExecutor) ostreeClient = ostreeclient.NewClient(hostCommandsExecutor, false) - ctx, cancelCtx = context.WithCancel(context.Background()) + ctx, cancelCtx = context.WithCancel(parentCtx) ) // additional logger setup @@ -86,7 +86,7 @@ func ibuStaterootSetupRun() error { } logger.Info("Setting up stateroot") - if err := prep.SetupStateroot(logger, opsClient, ostreeClient, rpmOstreeClient, ibu.Spec.SeedImageRef.Image, ibu.Spec.SeedImageRef.Version, false); err != nil { + if err := prep.SetupStateroot(ctx, logger, opsClient, ostreeClient, rpmOstreeClient, ibu.Spec.SeedImageRef.Image, ibu.Spec.SeedImageRef.Version, false); err != nil { return fmt.Errorf("failed to complete stateroot setup: %w", err) } @@ -117,7 +117,9 @@ func initStaterootSetupSigHandler(logger logr.Logger, opsClient ops.Ops, seedIma logger.Error(fmt.Errorf("unexpected SIGTERM received to stop stateroot setup"), "") // we consider all the steps after image pull as critical and must be allowed to proceed until SIGKILL arrives. The time between SIGTERM and SIGKILL is controlled with StaterootSetupTerminationGracePeriodSeconds - if seedImageExists, _ := opsClient.ImageExists(seedImage); seedImageExists { + probeCtx, probeCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer probeCancel() + if seedImageExists, _ := opsClient.ImageExists(probeCtx, seedImage); seedImageExists { sigkillArrivesIn := time.Duration(prep.StaterootSetupTerminationGracePeriodSeconds) * time.Second logger.Error(fmt.Errorf("seed image exists. prepare to shut down in at most %s", sigkillArrivesIn.String()), "") time.Sleep(sigkillArrivesIn) // likely anything beyond this point won't be executed since job should be done before this wakes up diff --git a/lca-cli/cmd/ipconfig/postpivot.go b/lca-cli/cmd/ipconfig/postpivot.go index e285a24335..1e627b6226 100644 --- a/lca-cli/cmd/ipconfig/postpivot.go +++ b/lca-cli/cmd/ipconfig/postpivot.go @@ -75,6 +75,8 @@ func runIPConfigPostPivot() (retErr error) { return err } + ctx := context.Background() + rpmClient := rpmOstree.NewClient("lca-cli-ip-config-post-pivot", hostCommandsExecutor) ostreeClient := intOstree.NewClient(hostCommandsExecutor, false) rbClient := reboot.NewIPCRebootClient(&logr.Logger{}, hostCommandsExecutor, rpmClient, ostreeClient, opsInterface) @@ -84,7 +86,7 @@ func runIPConfigPostPivot() (retErr error) { return } - rbClient.AutoRollbackIfEnabled( + rbClient.AutoRollbackIfEnabled(ctx, reboot.IPConfigRunComponent, fmt.Sprintf("automatic rollback: ip-config post-pivot failed: %v", retErr), ) @@ -108,8 +110,6 @@ func runIPConfigPostPivot() (retErr error) { recertImageFlag: recertImage, }).Info("IP config post-pivot flags") - ctx := context.Background() - postPivotHandler := ipconfig.NewIPConfigPostPivotHandler( pkgLog, opsInterface, diff --git a/lca-cli/cmd/ipconfig/prepivot.go b/lca-cli/cmd/ipconfig/prepivot.go index 42a2d17546..5add86ebbc 100644 --- a/lca-cli/cmd/ipconfig/prepivot.go +++ b/lca-cli/cmd/ipconfig/prepivot.go @@ -107,7 +107,7 @@ var ipConfigPrePivotCmd = &cobra.Command{ Use: prePivotCmd, Short: "Execute IP configuration pre-pivot", Run: func(cmd *cobra.Command, args []string) { - if err := runIPConfigPrePivot(); err != nil { + if err := runIPConfigPrePivot(cmd.Context()); err != nil { pkgLog.Fatalf("Error executing ip-config pre-pivot: %v", err) } }, @@ -118,7 +118,7 @@ var ipConfigPrePivotCmd = &cobra.Command{ // - gathers OSTree state for old/new stateroots // - writes progress status, runs pre-pivot actions, optionally installs an init monitor // - finalizes status and reboots into the new stateroot -func runIPConfigPrePivot() (retErr error) { +func runIPConfigPrePivot(ctx context.Context) (retErr error) { opsInterface, hostCommandsExecutor, client, err := buildOpsAndK8sClient(ipPrePivotScheme) if err != nil { return err @@ -136,7 +136,7 @@ func runIPConfigPrePivot() (retErr error) { defer func() { if retErr == nil { - if err := rbClient.RebootToNewStateRoot("ip-config pre-pivot"); err != nil { + if err := rbClient.RebootToNewStateRoot(ctx, "ip-config pre-pivot"); err != nil { retErr = fmt.Errorf("failed to reboot to new stateroot: %w", err) return } @@ -189,8 +189,6 @@ func runIPConfigPrePivot() (retErr error) { dnsIPFamilyFlag: dnsIPFamily, }).Info("ip-config pre-pivot flags") - ctx := context.Background() - if err := validatePrePivotFlags(ctx, client); err != nil { return fmt.Errorf("pre-pivot flags validation failed: %w", err) } @@ -207,7 +205,7 @@ func runIPConfigPrePivot() (retErr error) { ) // Build ostree data only after we have the final new stateroot name. - ostreeData, err := getOstreeData(newStaterootName, rpmClient, ostreeClient, pkgLog) + ostreeData, err := getOstreeData(ctx, newStaterootName, rpmClient, ostreeClient, pkgLog) if err != nil { return fmt.Errorf("failed to get ostree data: %w", err) } @@ -237,14 +235,14 @@ func runIPConfigPrePivot() (retErr error) { if installInitMonitor { if err := installMonitorInitializationServiceInNewStateroot( - opsInterface, ostreeData, pkgLog, + ctx, opsInterface, ostreeData, pkgLog, ); err != nil { return fmt.Errorf("failed to install monitor initialization service: %w", err) } } if installIpConfigurationService { - if err := installIpConfigurationServiceInNewStateroot(ostreeData, opsInterface, pkgLog); err != nil { + if err := installIpConfigurationServiceInNewStateroot(ctx, ostreeData, opsInterface, pkgLog); err != nil { return fmt.Errorf("failed to install ip configuration service: %w", err) } @@ -264,6 +262,7 @@ func runIPConfigPrePivot() (retErr error) { // installMonitorInitializationServiceInNewStateroot installs and enables the IPC init monitor service // within the new stateroot deployment. func installMonitorInitializationServiceInNewStateroot( + ctx context.Context, ops ops.Ops, ostreeData *ipconfig.OstreeData, logger *logrus.Logger, @@ -279,7 +278,7 @@ func installMonitorInitializationServiceInNewStateroot( } logger.Infof("Enabling service %s", common.IPCInitMonitorService) - if _, err := ops.SystemctlAction( + if _, err := ops.SystemctlAction(ctx, "enable", "--root", ostreeData.NewStateroot.DeploymentDir, @@ -299,6 +298,7 @@ func installMonitorInitializationServiceInNewStateroot( // getOstreeData returns information about the current (old) and target (new) // stateroots, including names, paths, deployment names, and deployment dirs. func getOstreeData( + ctx context.Context, newStaterootName string, rpmOstree rpmOstree.IClient, ostree intOstree.IClient, @@ -307,13 +307,13 @@ func getOstreeData( common.OstreeDeployPathPrefix = sysrootPath ostreeData := &ipconfig.OstreeData{} - currentStaterootData, err := getCurrentStaterootData(rpmOstree, ostree) + currentStaterootData, err := getCurrentStaterootData(ctx, rpmOstree, ostree) if err != nil { return nil, fmt.Errorf("failed to get current stateroot data: %w", err) } ostreeData.OldStateroot = currentStaterootData - newStaterootData, err := getNewStaterootData(newStaterootName, ostree, logger) + newStaterootData, err := getNewStaterootData(ctx, newStaterootName, ostree, logger) if err != nil { return nil, fmt.Errorf("failed to get new stateroot data: %w", err) } @@ -323,10 +323,10 @@ func getOstreeData( } // getCurrentStaterootData queries the system for the current stateroot details. -func getCurrentStaterootData(rpmOstree rpmOstree.IClient, ostree intOstree.IClient) (*ipconfig.StaterootData, error) { +func getCurrentStaterootData(ctx context.Context, rpmOstree rpmOstree.IClient, ostree intOstree.IClient) (*ipconfig.StaterootData, error) { staterootData := &ipconfig.StaterootData{} - currentStaterootName, err := rpmOstree.GetCurrentStaterootName() + currentStaterootName, err := rpmOstree.GetCurrentStaterootName(ctx) if err != nil { return nil, fmt.Errorf("failed to get current stateroot name: %w", err) } @@ -334,13 +334,13 @@ func getCurrentStaterootData(rpmOstree rpmOstree.IClient, ostree intOstree.IClie staterootData.Path = common.GetStaterootPath(currentStaterootName) - oldDeploymentName, err := ostree.GetDeployment(currentStaterootName) + oldDeploymentName, err := ostree.GetDeployment(ctx, currentStaterootName) if err != nil { return nil, fmt.Errorf("failed to get deployment for %s: %w", currentStaterootName, err) } staterootData.DeploymentName = oldDeploymentName - oldDeploymentDir, err := ostree.GetDeploymentDir(currentStaterootName) + oldDeploymentDir, err := ostree.GetDeploymentDir(ctx, currentStaterootName) if err != nil { return nil, fmt.Errorf("failed to get deployment dir for %s: %w", currentStaterootName, err) } @@ -351,6 +351,7 @@ func getCurrentStaterootData(rpmOstree rpmOstree.IClient, ostree intOstree.IClie // getNewStaterootData resolves the metadata for the target new stateroot. func getNewStaterootData( + ctx context.Context, newStaterootName string, ostree intOstree.IClient, logger *logrus.Logger, @@ -360,7 +361,7 @@ func getNewStaterootData( Path: common.GetStaterootPath(newStaterootName), } - newDeploymentName, err := ostree.GetDeployment(newStaterootName) + newDeploymentName, err := ostree.GetDeployment(ctx, newStaterootName) if err != nil { return nil, fmt.Errorf("failed to get deployment name for %s: %w", newStaterootName, err) } @@ -371,7 +372,7 @@ func getNewStaterootData( return staterootData, nil } - newDeploymentDir, err := ostree.GetDeploymentDir(newStaterootName) + newDeploymentDir, err := ostree.GetDeploymentDir(ctx, newStaterootName) if err != nil { return nil, fmt.Errorf("failed to get deployment dir for %s: %w", newStaterootName, err) } @@ -608,6 +609,7 @@ func validateDNSServers(servers []string) error { } func installIpConfigurationServiceInNewStateroot( + ctx context.Context, ostreeData *ipconfig.OstreeData, ops ops.Ops, logger *logrus.Logger, @@ -628,7 +630,7 @@ func installIpConfigurationServiceInNewStateroot( } logger.Infof("Enabling service %s", common.IPConfigurationService) - if _, err := ops.SystemctlAction( + if _, err := ops.SystemctlAction(ctx, "enable", "--root", ostreeData.NewStateroot.DeploymentDir, diff --git a/lca-cli/cmd/ipconfig/rollback.go b/lca-cli/cmd/ipconfig/rollback.go index bc73697d6a..505a644b94 100644 --- a/lca-cli/cmd/ipconfig/rollback.go +++ b/lca-cli/cmd/ipconfig/rollback.go @@ -17,6 +17,7 @@ limitations under the License. package ipconfigcmd import ( + "context" "fmt" "os" @@ -49,13 +50,13 @@ var ipConfigRollbackCmd = &cobra.Command{ Use: rollbackCmd, Short: "Execute IP configuration rollback", Run: func(cmd *cobra.Command, args []string) { - if err := runIPConfigRollback(); err != nil { + if err := runIPConfigRollback(cmd.Context()); err != nil { pkgLog.Fatalf("Error executing ip-config rollback: %v", err) } }, } -func runIPConfigRollback() error { +func runIPConfigRollback(ctx context.Context) error { var hostCommandsExecutor ops.Execute if _, err := os.Stat(common.Host); err == nil { hostCommandsExecutor = ops.NewChrootExecutor(pkgLog, true, common.Host) @@ -65,15 +66,14 @@ func runIPConfigRollback() error { opsInterface := ops.NewOps(pkgLog, hostCommandsExecutor) rpmClient := rpmOstree.NewClient("lca-cli-ip-config-rollback", hostCommandsExecutor) ostreeClient := intOstree.NewClient(hostCommandsExecutor, false) - rb := reboot.NewIPCRebootClient(&logr.Logger{}, hostCommandsExecutor, rpmClient, ostreeClient, opsInterface) exec := ipconfig.NewRollbackHandler(pkgLog, opsInterface, ostreeClient, rpmClient) - if err := exec.Run(rollbackStateroot); err != nil { + if err := exec.Run(ctx, rollbackStateroot); err != nil { return fmt.Errorf("ip config rollback handler failed: %w", err) } - if err := rb.RebootToNewStateRoot("ip-config rollback"); err != nil { + if err := rb.RebootToNewStateRoot(ctx, "ip-config rollback"); err != nil { return fmt.Errorf("failed to reboot: %w", err) } diff --git a/lca-cli/cmd/postpivotcmd.go b/lca-cli/cmd/postpivotcmd.go index 56d5695a82..694a334efc 100644 --- a/lca-cli/cmd/postpivotcmd.go +++ b/lca-cli/cmd/postpivotcmd.go @@ -36,7 +36,7 @@ var postPivotCmd = &cobra.Command{ Use: "post-pivot", Short: "post pivot configuration", Run: func(cmd *cobra.Command, args []string) { - postPivot() + postPivot(cmd.Context()) }, } @@ -50,7 +50,7 @@ func init() { postPivotCmd.Flags().BoolVarP(&inContainer, "in-container", "", false, "Use this flag if this command is being ran inside a container") } -func postPivot() { +func postPivot(ctx context.Context) { log.Info("Post pivot operation has started") var hostCommandsExecutor ops.Execute if inContainer { @@ -65,9 +65,9 @@ func postPivot() { postPivotRunner := postpivot.NewPostPivot(scheme, log, opsClient, common.ImageRegistryAuthFile, common.OptOpenshift, common.KubeconfigFile) - if err := postPivotRunner.PostPivotConfiguration(context.TODO()); err != nil { + if err := postPivotRunner.PostPivotConfiguration(ctx); err != nil { log.Error(err) - rebootClient.AutoRollbackIfEnabled(reboot.PostPivotComponent, fmt.Sprintf("Rollback due to postpivot failure: %s", err)) + rebootClient.AutoRollbackIfEnabled(ctx, reboot.PostPivotComponent, fmt.Sprintf("Rollback due to postpivot failure: %s", err)) log.Fatal("Post pivot operation failed") } diff --git a/lca-cli/cmd/restore.go b/lca-cli/cmd/restore.go index f0168843e6..3c533020b2 100644 --- a/lca-cli/cmd/restore.go +++ b/lca-cli/cmd/restore.go @@ -17,6 +17,8 @@ limitations under the License. package cmd import ( + "context" + "github.com/spf13/cobra" "github.com/openshift-kni/lifecycle-agent/internal/common" @@ -28,7 +30,7 @@ var restoreCmd = &cobra.Command{ Use: "restore", Short: "Restore seed cluster configurations", Run: func(cmd *cobra.Command, args []string) { - restore() + restore(cmd.Context()) }, } @@ -41,7 +43,7 @@ func init() { addCommonFlags(restoreCmd) } -func restore() { +func restore(ctx context.Context) { log.Info("Restore operation has started") hostCommandsExecutor := ops.NewNsenterExecutor(log, true) @@ -50,7 +52,7 @@ func restore() { seedRestore := seedrestoration.NewSeedRestoration(log, op, common.BackupDir, containerRegistry, authFile, recertContainerImage, recertSkipValidation) - if err := seedRestore.RestoreSeedCluster(); err != nil { + if err := seedRestore.RestoreSeedCluster(ctx); err != nil { log.Fatalf("Failed to restore seed cluster: %v", err) } diff --git a/lca-cli/ibi-preparation/ibipreparation.go b/lca-cli/ibi-preparation/ibipreparation.go index 0b9d37f9b4..f4da01a966 100644 --- a/lca-cli/ibi-preparation/ibipreparation.go +++ b/lca-cli/ibi-preparation/ibipreparation.go @@ -1,6 +1,7 @@ package ibi_preparation import ( + "context" "fmt" "os" "path/filepath" @@ -50,14 +51,14 @@ func NewIBIPrepare(log *logrus.Logger, ops ops.Ops, rpmostreeClient rpmostreecli } } -func (i *IBIPrepare) Run() error { +func (i *IBIPrepare) Run(ctx context.Context) error { // Pull seed image - if err := i.diskPreparation(); err != nil { + if err := i.diskPreparation(ctx); err != nil { return fmt.Errorf("failed to prepare disk: %w", err) } i.log.Info("Pulling seed image") - if _, err := i.ops.RunInHostNamespace("podman", "pull", "--authfile", common.IBIPullSecretFilePath, i.config.SeedImage); err != nil { + if _, err := i.ops.RunInHostNamespace(ctx, "podman", "pull", "--authfile", common.IBIPullSecretFilePath, i.config.SeedImage); err != nil { return fmt.Errorf("failed to pull image: %w", err) } @@ -65,7 +66,7 @@ func (i *IBIPrepare) Run() error { log := logr.Logger{} common.OstreeDeployPathPrefix = "/mnt/" // Setup state root - if err := prep.SetupStateroot(log, i.ops, i.ostreeClient, i.rpmostreeClient, + if err := prep.SetupStateroot(ctx, log, i.ops, i.ostreeClient, i.rpmostreeClient, i.config.SeedImage, i.config.SeedVersion, true); err != nil { return fmt.Errorf("failed to setup stateroot: %w", err) } @@ -74,15 +75,15 @@ func (i *IBIPrepare) Run() error { return fmt.Errorf("failed to precache: %w", err) } - if err := i.postDeployment(common.IBIPostDeploymentScriptPath); err != nil { + if err := i.postDeployment(ctx, common.IBIPostDeploymentScriptPath); err != nil { return fmt.Errorf("failed to run post deployment: %w", err) } - if err := i.cleanupRhcosSysroot(); err != nil { + if err := i.cleanupRhcosSysroot(ctx); err != nil { return fmt.Errorf("failed to cleanup rhcos sysroot: %w", err) } - return i.shutdownNode() + return i.shutdownNode(ctx) } func (i *IBIPrepare) precacheFlow(imageListFile, seedInfoFile, registriesConfFile string) error { @@ -129,20 +130,20 @@ func (i *IBIPrepare) precacheFlow(imageListFile, seedInfoFile, registriesConfFil return fmt.Errorf("failed to create status file dir, err %w", err) } - if err := workload.Precache(imageList, common.IBIPullSecretFilePath, i.config.PrecacheBestEffort); err != nil { + if err := workload.Precache(context.Background(), imageList, common.IBIPullSecretFilePath, i.config.PrecacheBestEffort); err != nil { return fmt.Errorf("failed to start precache: %w", err) } return unchroot() } -func (i *IBIPrepare) shutdownNode() error { +func (i *IBIPrepare) shutdownNode(ctx context.Context) error { if !i.config.Shutdown { i.log.Info("Skipping shutdown") return nil } i.log.Info("Shutting down the host") - if _, err := i.ops.RunInHostNamespace("shutdown", "now"); err != nil { + if _, err := i.ops.RunInHostNamespace(ctx, "shutdown", "now"); err != nil { return fmt.Errorf("failed to shutdown the host: %w", err) } return nil @@ -179,23 +180,23 @@ func (i *IBIPrepare) cleanupDisk() { } } -func (i *IBIPrepare) diskPreparation() error { +func (i *IBIPrepare) diskPreparation(ctx context.Context) error { i.log.Info("Start preparing disk") i.cleanupDisk() i.log.Info("Writing image to disk") - if _, err := i.ops.RunInHostNamespace("coreos-installer", + if _, err := i.ops.RunInHostNamespace(ctx, "coreos-installer", append([]string{"install", i.config.InstallationDisk}, i.config.CoreosInstallerArgs...)...); err != nil { return fmt.Errorf("failed to write image to disk: %w", err) } if i.config.UseContainersFolder { - if err := i.ops.SetupContainersFolderCommands(); err != nil { + if err := i.ops.SetupContainersFolderCommands(ctx); err != nil { return fmt.Errorf("failed to setup containers folder: %w", err) } } else { - if err := i.ops.CreateExtraPartition(i.config.InstallationDisk, i.config.ExtraPartitionLabel, + if err := i.ops.CreateExtraPartition(ctx, i.config.InstallationDisk, i.config.ExtraPartitionLabel, i.config.ExtraPartitionStart, i.config.ExtraPartitionNumber); err != nil { return fmt.Errorf("failed to create extra partition: %w", err) } @@ -206,10 +207,10 @@ func (i *IBIPrepare) diskPreparation() error { return nil } -func (i *IBIPrepare) postDeployment(scriptPath string) error { +func (i *IBIPrepare) postDeployment(ctx context.Context, scriptPath string) error { if _, err := os.Stat(scriptPath); err == nil { i.log.Info("Running post deployment script") - if _, err := i.ops.RunBashInHostNamespace(scriptPath); err != nil { + if _, err := i.ops.RunBashInHostNamespace(ctx, scriptPath); err != nil { return fmt.Errorf("failed to run post deployment script: %w", err) } } @@ -219,8 +220,8 @@ func (i *IBIPrepare) postDeployment(scriptPath string) error { // cleanupRhcosSysroot cleanups ostree that was written by us as part of diskPreparation // as each new deployed ostree takes index 0, we know that the one we created earlier will be "1" // that's why we can set it hardcoded -func (i *IBIPrepare) cleanupRhcosSysroot() error { - if err := i.ostreeClient.Undeploy(rhcosOstreeIndex); err != nil { +func (i *IBIPrepare) cleanupRhcosSysroot(ctx context.Context) error { + if err := i.ostreeClient.Undeploy(ctx, rhcosOstreeIndex); err != nil { return fmt.Errorf("failed to undeploy sysroot written by coreos installer command with index %d: %w", 1, err) } // We set hardcoded value here cause we know exact path for written sysroot @@ -229,7 +230,7 @@ func (i *IBIPrepare) cleanupRhcosSysroot() error { i.log.Warnf("rhcos sysroot %s doesn't exists but it was expected", rhcosysrootPath) return nil } - if _, err := i.ops.RunBashInHostNamespace("rm", "-rf", rhcosysrootPath); err != nil { + if _, err := i.ops.RunBashInHostNamespace(ctx, "rm", "-rf", rhcosysrootPath); err != nil { return fmt.Errorf("removing sysroot %s failed: %w", rhcosysrootPath, err) } return nil diff --git a/lca-cli/ibi-preparation/ibipreparation_test.go b/lca-cli/ibi-preparation/ibipreparation_test.go index 6e79f2e303..909ac99536 100644 --- a/lca-cli/ibi-preparation/ibipreparation_test.go +++ b/lca-cli/ibi-preparation/ibipreparation_test.go @@ -1,6 +1,7 @@ package ibi_preparation import ( + "context" "fmt" "os" "path" @@ -110,29 +111,29 @@ func TestDiskPreparation(t *testing.T) { } } args := append([]string{"install", "/dev/sda"}, tc.CoreosInstallerArgs...) - mockOps.EXPECT().RunInHostNamespace("coreos-installer", args).Return("", nil).Times(1) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "coreos-installer", args).Return("", nil).Times(1) if !tc.UseContainersFolder { if !tc.partitionError { - mockOps.EXPECT().CreateExtraPartition(installationDisk, extraPartitionLabel, + mockOps.EXPECT().CreateExtraPartition(gomock.Any(), installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber).Return(nil).Times(1) } else { - mockOps.EXPECT().CreateExtraPartition(installationDisk, extraPartitionLabel, + mockOps.EXPECT().CreateExtraPartition(gomock.Any(), installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber).Return(fmt.Errorf("dummy")).Times(1) } - mockOps.EXPECT().SetupContainersFolderCommands().Return(nil).Times(0) + mockOps.EXPECT().SetupContainersFolderCommands(gomock.Any()).Return(nil).Times(0) } else { - mockOps.EXPECT().CreateExtraPartition(gomock.Any(), gomock.Any(), + mockOps.EXPECT().CreateExtraPartition(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(0) if !tc.setupFolderError { - mockOps.EXPECT().SetupContainersFolderCommands().Return(nil).Times(1) + mockOps.EXPECT().SetupContainersFolderCommands(gomock.Any()).Return(nil).Times(1) } else { - mockOps.EXPECT().SetupContainersFolderCommands().Return(fmt.Errorf("dummy")).Times(1) + mockOps.EXPECT().SetupContainersFolderCommands(gomock.Any()).Return(fmt.Errorf("dummy")).Times(1) } } - err := ibi.diskPreparation() + err := ibi.diskPreparation(context.Background()) assert.Equal(t, err != nil, tc.partitionError || tc.setupFolderError) }) } @@ -148,21 +149,21 @@ func TestPostDeployment(t *testing.T) { // Test case when the post deployment script does not exist tmpDir := t.TempDir() postSH := path.Join(tmpDir, "post.sh") - mockOps.EXPECT().RunBashInHostNamespace(gomock.Any()).Return("", nil).Times(0) - err := ibi.postDeployment(postSH) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), gomock.Any()).Return("", nil).Times(0) + err := ibi.postDeployment(context.Background(), postSH) assert.Nil(t, err) file, err := os.Create(postSH) assert.Nil(t, err) defer func() { _ = file.Close() }() // Test case when the post deployment script exists and executes without error - mockOps.EXPECT().RunBashInHostNamespace(postSH).Return("", nil).Times(1) - err = ibi.postDeployment(postSH) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), postSH).Return("", nil).Times(1) + err = ibi.postDeployment(context.Background(), postSH) assert.Nil(t, err) // Test case when the post deployment script exists but fails to execute - mockOps.EXPECT().RunBashInHostNamespace(postSH).Return("", fmt.Errorf("dummy")).Times(1) - err = ibi.postDeployment(postSH) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), postSH).Return("", fmt.Errorf("dummy")).Times(1) + err = ibi.postDeployment(context.Background(), postSH) assert.NotNil(t, err) } @@ -177,28 +178,28 @@ func TestCleanupRhcosSysroot(t *testing.T) { common.OstreeDeployPathPrefix = tmpDir // Test case when rhcosOstreePath doesn't exists, we still should succeed - ostreeClientMock.EXPECT().Undeploy(rhcosOstreeIndex).Return(nil).Times(1) - mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).Times(0) - err := ibi.cleanupRhcosSysroot() + ostreeClientMock.EXPECT().Undeploy(gomock.Any(), rhcosOstreeIndex).Return(nil).Times(1) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).Times(0) + err := ibi.cleanupRhcosSysroot(context.Background()) assert.Nil(t, err) // failed to undeploy - ostreeClientMock.EXPECT().Undeploy(rhcosOstreeIndex).Return(fmt.Errorf("dummy")).Times(1) - err = ibi.cleanupRhcosSysroot() + ostreeClientMock.EXPECT().Undeploy(gomock.Any(), rhcosOstreeIndex).Return(fmt.Errorf("dummy")).Times(1) + err = ibi.cleanupRhcosSysroot(context.Background()) assert.NotNil(t, err) // Happy flow if err := os.MkdirAll(filepath.Join(tmpDir, rhcosOstreePath), 0o700); err != nil { t.Errorf("unexpected error: %v", err) } - ostreeClientMock.EXPECT().Undeploy(rhcosOstreeIndex).Return(nil).Times(1) - mockOps.EXPECT().RunBashInHostNamespace("rm", "-rf", filepath.Join(tmpDir, rhcosOstreePath)).Return("", nil).Times(1) - err = ibi.cleanupRhcosSysroot() + ostreeClientMock.EXPECT().Undeploy(gomock.Any(), rhcosOstreeIndex).Return(nil).Times(1) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), "rm", "-rf", filepath.Join(tmpDir, rhcosOstreePath)).Return("", nil).Times(1) + err = ibi.cleanupRhcosSysroot(context.Background()) assert.Nil(t, err) // Failed to remove folder - ostreeClientMock.EXPECT().Undeploy(rhcosOstreeIndex).Return(nil).Times(1) - mockOps.EXPECT().RunBashInHostNamespace("rm", "-rf", filepath.Join(tmpDir, rhcosOstreePath)).Return("", fmt.Errorf("dummy")).Times(1) - err = ibi.cleanupRhcosSysroot() + ostreeClientMock.EXPECT().Undeploy(gomock.Any(), rhcosOstreeIndex).Return(nil).Times(1) + mockOps.EXPECT().RunBashInHostNamespace(gomock.Any(), "rm", "-rf", filepath.Join(tmpDir, rhcosOstreePath)).Return("", fmt.Errorf("dummy")).Times(1) + err = ibi.cleanupRhcosSysroot(context.Background()) assert.NotNil(t, err) } diff --git a/lca-cli/initmonitor/initmonitor.go b/lca-cli/initmonitor/initmonitor.go index 6e67e18088..0219a5a1b0 100644 --- a/lca-cli/initmonitor/initmonitor.go +++ b/lca-cli/initmonitor/initmonitor.go @@ -1,6 +1,7 @@ package initmonitor import ( + "context" "errors" "fmt" "os" @@ -88,7 +89,7 @@ func (m *InitMonitor) RunInitMonitor() error { msg := fmt.Sprintf("Rollback due to LCA Init Monitor timeout, after %s", timeout) m.log.Info(msg) - if err := m.rebootClient.InitiateRollback(msg); err != nil { + if err := m.rebootClient.InitiateRollback(context.Background(), msg); err != nil { return fmt.Errorf("unable to auto rollback: %w", err) } @@ -134,7 +135,7 @@ func (m *InitMonitor) RunExitStopPostCheck() error { msg := fmt.Sprintf("Rollback due to service-unit failure: component %s", m.component) m.log.Info(msg) - if err := m.rebootClient.InitiateRollback(msg); err != nil { + if err := m.rebootClient.InitiateRollback(context.Background(), msg); err != nil { return fmt.Errorf("unable to auto rollback: %w", err) } } diff --git a/lca-cli/ipconfig/postpivot.go b/lca-cli/ipconfig/postpivot.go index 88358e1ce4..8fa6f4b363 100644 --- a/lca-cli/ipconfig/postpivot.go +++ b/lca-cli/ipconfig/postpivot.go @@ -67,16 +67,16 @@ func (p *IPConfigPostPivotHandler) Run(ctx context.Context) error { p.log.Info("IP config post-pivot started") p.log.Info("Applying network configuration") - if err := utils.RunOnce("apply-network-configuration", p.workspaceDir, p.log, p.applyNetworkConfiguration); err != nil { + if err := utils.RunOnce("apply-network-configuration", p.workspaceDir, p.log, p.applyNetworkConfiguration, ctx); err != nil { return fmt.Errorf("failed to run once apply-network-configuration: %w", err) } p.log.Info("Running recert") - if err := utils.RunOnce("run-recert", p.workspaceDir, p.log, p.runRecert); err != nil { + if err := utils.RunOnce("run-recert", p.workspaceDir, p.log, p.runRecert, ctx); err != nil { return fmt.Errorf("failed to run once run-recert: %w", err) } - if err := p.enableAndStartKubelet(); err != nil { + if err := p.enableAndStartKubelet(ctx); err != nil { return fmt.Errorf("failed to enable and start kubelet: %w", err) } @@ -92,7 +92,7 @@ func (p *IPConfigPostPivotHandler) Run(ctx context.Context) error { utils.WaitForApi(ctx, client, p.log) - if _, err = p.ops.SystemctlAction("disable", "ip-configuration.service"); err != nil { + if _, err = p.ops.SystemctlAction(ctx, "disable", "ip-configuration.service"); err != nil { return fmt.Errorf("failed to disable ip-configuration.service, err: %w", err) } @@ -103,9 +103,9 @@ func (p *IPConfigPostPivotHandler) Run(ctx context.Context) error { // runRecert writes a recert configuration and executes the full recert flow // to regenerate certificates and manifests for the new IP configuration. -func (p *IPConfigPostPivotHandler) runRecert() error { +func (p *IPConfigPostPivotHandler) runRecert(ctx context.Context) error { pullSecretPath := filepath.Join(p.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)) - if err := p.ops.RecertFullFlow( + if err := p.ops.RecertFullFlow(ctx, p.recertImage, pullSecretPath, filepath.Join(p.workspaceDir, recert.RecertConfigFile), @@ -119,17 +119,17 @@ func (p *IPConfigPostPivotHandler) runRecert() error { return nil } -func (p *IPConfigPostPivotHandler) applyNetworkConfiguration() error { +func (p *IPConfigPostPivotHandler) applyNetworkConfiguration(ctx context.Context) error { nmstateConfigFile := filepath.Join(p.workspaceDir, common.NmstateConfigFileName) - if _, err := p.ops.RunInHostNamespace("nmstatectl", "apply", nmstateConfigFile); err != nil { + if _, err := p.ops.RunInHostNamespace(ctx, "nmstatectl", "apply", nmstateConfigFile); err != nil { return fmt.Errorf("failed to apply network configuration: %w", err) } return nil } -func (p *IPConfigPostPivotHandler) enableAndStartKubelet() error { - if _, err := p.ops.SystemctlAction("enable", "kubelet", "--now"); err != nil { +func (p *IPConfigPostPivotHandler) enableAndStartKubelet(ctx context.Context) error { + if _, err := p.ops.SystemctlAction(ctx, "enable", "kubelet", "--now"); err != nil { return fmt.Errorf("failed to enable kubelet: %w", err) } return nil diff --git a/lca-cli/ipconfig/postpivot_test.go b/lca-cli/ipconfig/postpivot_test.go index d914142461..8719bd00da 100644 --- a/lca-cli/ipconfig/postpivot_test.go +++ b/lca-cli/ipconfig/postpivot_test.go @@ -92,8 +92,8 @@ func TestRunHappyPath(t *testing.T) { handler, opsMock := newPostPivotHandler(t) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -101,9 +101,9 @@ func TestRunHappyPath(t *testing.T) { nil, "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil), - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", nil), opsMock.EXPECT().ReadFile(handler.kubeconfig).Return([]byte(kubeconfigForLocalhost()), nil), - opsMock.EXPECT().SystemctlAction("disable", "ip-configuration.service").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "disable", "ip-configuration.service").Return("", nil), ) // WaitForApi polls until ctx cancellation; keep test fast. @@ -116,7 +116,7 @@ func TestRunHappyPath(t *testing.T) { func TestRunApplyNetworkConfigurationError(t *testing.T) { handler, opsMock := newPostPivotHandler(t) - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)). + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)). Return("", errors.New("nmstate-fail")) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) @@ -130,8 +130,8 @@ func TestRunRecertError(t *testing.T) { handler, opsMock := newPostPivotHandler(t) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -152,8 +152,8 @@ func TestRunEnableKubeletError(t *testing.T) { handler, opsMock := newPostPivotHandler(t) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -161,7 +161,7 @@ func TestRunEnableKubeletError(t *testing.T) { nil, "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil), - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", errors.New("enable-fail")), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", errors.New("enable-fail")), ) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) @@ -177,8 +177,8 @@ func TestRunCreateKubeClientError(t *testing.T) { opsMock.EXPECT().ReadFile(handler.kubeconfig).Return([]byte("{not a kubeconfig"), nil) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -186,7 +186,7 @@ func TestRunCreateKubeClientError(t *testing.T) { nil, "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil), - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", nil), ) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) @@ -200,8 +200,8 @@ func TestRunDNSIPFamilyIsIgnoredInPostPivot(t *testing.T) { handler, opsMock := newPostPivotHandler(t) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -209,9 +209,9 @@ func TestRunDNSIPFamilyIsIgnoredInPostPivot(t *testing.T) { nil, "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil), - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", nil), opsMock.EXPECT().ReadFile(handler.kubeconfig).Return([]byte(kubeconfigForLocalhost()), nil), - opsMock.EXPECT().SystemctlAction("disable", "ip-configuration.service").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "disable", "ip-configuration.service").Return("", nil), ) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) @@ -224,8 +224,8 @@ func TestRunDisableServiceError(t *testing.T) { handler, opsMock := newPostPivotHandler(t) gomock.InOrder( - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", filepath.Join(handler.workspaceDir, common.NmstateConfigFileName)).Return("", nil), + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -233,9 +233,9 @@ func TestRunDisableServiceError(t *testing.T) { nil, "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil), - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", nil), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", nil), opsMock.EXPECT().ReadFile(handler.kubeconfig).Return([]byte(kubeconfigForLocalhost()), nil), - opsMock.EXPECT().SystemctlAction("disable", "ip-configuration.service").Return("", errors.New("disable-fail")), + opsMock.EXPECT().SystemctlAction(gomock.Any(), "disable", "ip-configuration.service").Return("", errors.New("disable-fail")), ) ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) @@ -248,7 +248,7 @@ func TestRunDisableServiceError(t *testing.T) { func TestRunRecert(t *testing.T) { handler, opsMock := newPostPivotHandler(t) - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -257,9 +257,9 @@ func TestRunRecert(t *testing.T) { "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(nil) - assert.NoError(t, handler.runRecert()) + assert.NoError(t, handler.runRecert(context.Background())) - opsMock.EXPECT().RecertFullFlow( + opsMock.EXPECT().RecertFullFlow(gomock.Any(), handler.recertImage, filepath.Join(handler.workspaceDir, filepath.Base(common.IPConfigPullSecretFile)), filepath.Join(handler.workspaceDir, recert.RecertConfigFile), @@ -268,26 +268,26 @@ func TestRunRecert(t *testing.T) { "-v", fmt.Sprintf("%s:%s", handler.workspaceDir, handler.workspaceDir), ).Return(errors.New("boom")) - assert.Error(t, handler.runRecert()) + assert.Error(t, handler.runRecert(context.Background())) } func TestApplyNetworkConfiguration(t *testing.T) { handler, opsMock := newPostPivotHandler(t) nmstateConfigFile := filepath.Join(handler.workspaceDir, common.NmstateConfigFileName) - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", nmstateConfigFile).Return("", nil) - assert.NoError(t, handler.applyNetworkConfiguration()) + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", nmstateConfigFile).Return("", nil) + assert.NoError(t, handler.applyNetworkConfiguration(context.Background())) - opsMock.EXPECT().RunInHostNamespace("nmstatectl", "apply", nmstateConfigFile).Return("", errors.New("apply-fail")) - assert.Error(t, handler.applyNetworkConfiguration()) + opsMock.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", nmstateConfigFile).Return("", errors.New("apply-fail")) + assert.Error(t, handler.applyNetworkConfiguration(context.Background())) } func TestEnableAndStartKubelet(t *testing.T) { handler, opsMock := newPostPivotHandler(t) - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", nil) - assert.NoError(t, handler.enableAndStartKubelet()) + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", nil) + assert.NoError(t, handler.enableAndStartKubelet(context.Background())) - opsMock.EXPECT().SystemctlAction("enable", "kubelet", "--now").Return("", errors.New("enable-fail")) - assert.Error(t, handler.enableAndStartKubelet()) + opsMock.EXPECT().SystemctlAction(gomock.Any(), "enable", "kubelet", "--now").Return("", errors.New("enable-fail")) + assert.Error(t, handler.enableAndStartKubelet(context.Background())) } diff --git a/lca-cli/ipconfig/prepivot.go b/lca-cli/ipconfig/prepivot.go index 84f3df527e..2bb45ed54b 100644 --- a/lca-cli/ipconfig/prepivot.go +++ b/lca-cli/ipconfig/prepivot.go @@ -169,7 +169,7 @@ func (p *PrePivotHandler) Run(ctx context.Context) (err error) { } p.log.Info("Preparing network configuration") - nmstateConfig, err := p.prepareNetworkConfiguration() + nmstateConfig, err := p.prepareNetworkConfiguration(ctx) if err != nil { return fmt.Errorf("failed to prepare network configuration: %w", err) } @@ -177,14 +177,14 @@ func (p *PrePivotHandler) Run(ctx context.Context) (err error) { // all functions above should run before we stop the cluster services p.log.Info("Stopping cluster services") - if err = p.ops.StopClusterServices(); err != nil { + if err = p.ops.StopClusterServices(ctx); err != nil { err = fmt.Errorf("failed to stop cluster services: %w", err) return } defer func() { p.log.Info("Enabling cluster services in old stateroot") - if internalErr := p.ops.EnableClusterServices(); internalErr != nil { + if internalErr := p.ops.EnableClusterServices(ctx); internalErr != nil { if err == nil { err = internalErr } else { @@ -195,7 +195,7 @@ func (p *PrePivotHandler) Run(ctx context.Context) (err error) { // should run after the cluster services stopped p.log.Info("Preparing new stateroot") - if err = p.prepareNewStateroot(p.ostreeData, kargs); err != nil { + if err = p.prepareNewStateroot(ctx, p.ostreeData, kargs); err != nil { return fmt.Errorf("failed to prepare new stateroot: %w", err) } @@ -232,7 +232,7 @@ func (p *PrePivotHandler) Run(ctx context.Context) (err error) { } p.log.Info("Restoring MCO-managed files under /var/lib/") - if err := p.restoreMCDManagedVarLibFiles(); err != nil { + if err := p.restoreMCDManagedVarLibFiles(ctx); err != nil { return fmt.Errorf("failed to restore MCO-managed var/lib files: %w", err) } @@ -244,22 +244,24 @@ func (p *PrePivotHandler) Run(ctx context.Context) (err error) { // prepareNewStateroot deploys the new stateroot if needed, copies data, and // sets it as default (when supported). func (p *PrePivotHandler) prepareNewStateroot( + ctx context.Context, ostreeData *OstreeData, kernelArgs []string, ) error { - if err := p.ensureSysrootWritable(); err != nil { + if err := p.ensureSysrootWritable(ctx); err != nil { return fmt.Errorf("failed to ensure sysroot writable: %w", err) } if ostreeData.NewStateroot.DeploymentName == "" { p.log.Infof("New stateroot %s is not deployed, deploying it", ostreeData.NewStateroot.Name) - bootedCommit, err := p.getBootedCommit() + bootedCommit, err := p.getBootedCommit(ctx) if err != nil { return fmt.Errorf("failed to get booted commit: %w", err) } if err = p.deployNewStateroot( + ctx, ostreeData, lo.FromPtr(bootedCommit), kernelArgs, @@ -270,27 +272,27 @@ func (p *PrePivotHandler) prepareNewStateroot( p.log.Infof("New stateroot %s is already deployed, skipping deploy", ostreeData.NewStateroot.Name) } - if err := p.copyStateRootData(ostreeData); err != nil { + if err := p.copyStateRootData(ctx, ostreeData); err != nil { return fmt.Errorf("failed to copy state root data: %w", err) } - if err := p.setDefaultDeploymentIfEnabled(ostreeData.NewStateroot.Name); err != nil { + if err := p.setDefaultDeploymentIfEnabled(ctx, ostreeData.NewStateroot.Name); err != nil { return fmt.Errorf("failed to set default deployment: %w", err) } return nil } -func (p *PrePivotHandler) ensureSysrootWritable() error { - if err := p.ops.RemountSysroot(); err != nil { +func (p *PrePivotHandler) ensureSysrootWritable(ctx context.Context) error { + if err := p.ops.RemountSysroot(ctx); err != nil { return fmt.Errorf("failed to remount /sysroot rw: %w", err) } return nil } // getBootedCommit returns the checksum of the currently booted rpm-ostree deployment. -func (p *PrePivotHandler) getBootedCommit() (*string, error) { - status, err := p.rpm.QueryStatus() +func (p *PrePivotHandler) getBootedCommit(ctx context.Context) (*string, error) { + status, err := p.rpm.QueryStatus(ctx) if err != nil { return nil, fmt.Errorf("failed to query rpm-ostree status: %w", err) } @@ -313,25 +315,26 @@ func (p *PrePivotHandler) getBootedCommit() (*string, error) { // deployNewStateroot initializes OSTree for the new stateroot and deploys the // booted commit with the provided kernel args, updating the ostreeData fields. func (p *PrePivotHandler) deployNewStateroot( + ctx context.Context, ostreeData *OstreeData, bootedCommit string, kargs []string, ) error { - if err := p.ostree.OSInit(ostreeData.NewStateroot.Name); err != nil { + if err := p.ostree.OSInit(ctx, ostreeData.NewStateroot.Name); err != nil { return fmt.Errorf("failed to initialize ostree for new stateroot %s: %w", ostreeData.NewStateroot.Name, err) } - if err := p.ostree.Deploy(ostreeData.NewStateroot.Name, bootedCommit, kargs, p.rpm, false); err != nil { + if err := p.ostree.Deploy(ctx, ostreeData.NewStateroot.Name, bootedCommit, kargs, p.rpm, false); err != nil { return fmt.Errorf("failed ostree admin deploy: %w", err) } - deploymentName, err := p.ostree.GetDeployment(ostreeData.NewStateroot.Name) + deploymentName, err := p.ostree.GetDeployment(ctx, ostreeData.NewStateroot.Name) if err != nil { return fmt.Errorf("failed to get deployment for %s: %w", ostreeData.NewStateroot.Name, err) } ostreeData.NewStateroot.DeploymentName = deploymentName - deploymentDir, err := p.ostree.GetDeploymentDir(ostreeData.NewStateroot.Name) + deploymentDir, err := p.ostree.GetDeploymentDir(ctx, ostreeData.NewStateroot.Name) if err != nil { return fmt.Errorf("failed to get deployment dir for %s: %w", ostreeData.NewStateroot.Name, err) } @@ -341,22 +344,22 @@ func (p *PrePivotHandler) deployNewStateroot( } // copyStateRootData copies important state from the old stateroot to the new one. -func (p *PrePivotHandler) copyStateRootData(ostreeData *OstreeData) error { - if err := p.copyVar( +func (p *PrePivotHandler) copyStateRootData(ctx context.Context, ostreeData *OstreeData) error { + if err := p.copyVar(ctx, ostreeData.OldStateroot.Path, ostreeData.NewStateroot.Path, ); err != nil { return err } - if err := p.copyEtc( + if err := p.copyEtc(ctx, ostreeData.OldStateroot.DeploymentDir, ostreeData.NewStateroot.DeploymentDir, ); err != nil { return err } - if err := p.copyDeploymentOrigin( + if err := p.copyDeploymentOrigin(ctx, ostreeData.OldStateroot.Path, ostreeData.NewStateroot.Path, ostreeData.OldStateroot.DeploymentName, @@ -369,17 +372,19 @@ func (p *PrePivotHandler) copyStateRootData(ostreeData *OstreeData) error { } // setDefaultDeploymentIfEnabled sets the given stateroot as default when supported. -func (p *PrePivotHandler) setDefaultDeploymentIfEnabled(newStateroot string) error { - if !p.ostree.IsOstreeAdminSetDefaultFeatureEnabled() { +func (p *PrePivotHandler) setDefaultDeploymentIfEnabled(ctx context.Context, newStateroot string) error { + if enabled, err := p.ostree.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree set-default feature: %w", err) + } else if !enabled { return fmt.Errorf("ostree admin set default feature is not enabled") } - idx, err := p.rpm.GetDeploymentIndex(newStateroot) + idx, err := p.rpm.GetDeploymentIndex(ctx, newStateroot) if err != nil { return fmt.Errorf("failed to get deployment index for %s: %w", newStateroot, err) } - if err := p.ostree.SetDefaultDeployment(idx); err != nil { + if err := p.ostree.SetDefaultDeployment(ctx, idx); err != nil { return fmt.Errorf("failed to set default deployment: %w", err) } @@ -387,9 +392,9 @@ func (p *PrePivotHandler) setDefaultDeploymentIfEnabled(newStateroot string) err } // copyVar copies the var directory preserving SELinux contexts and attributes -func (p *PrePivotHandler) copyVar(oldSRPath, newSRPath string) error { +func (p *PrePivotHandler) copyVar(ctx context.Context, oldSRPath, newSRPath string) error { // Copy var directory preserving SELinux contexts and attributes - if _, err := p.ops.RunInHostNamespace( + if _, err := p.ops.RunInHostNamespace(ctx, "bash", "-c", fmt.Sprintf( "cp -ar --preserve=context '%s/' '%s/'", @@ -403,8 +408,8 @@ func (p *PrePivotHandler) copyVar(oldSRPath, newSRPath string) error { } // copyEtc copies the deployment's etc directory preserving SELinux contexts -func (p *PrePivotHandler) copyEtc(oldDeploymentDir, newDeploymentDir string) error { - if _, err := p.ops.RunInHostNamespace( +func (p *PrePivotHandler) copyEtc(ctx context.Context, oldDeploymentDir, newDeploymentDir string) error { + if _, err := p.ops.RunInHostNamespace(ctx, "bash", "-c", fmt.Sprintf( "cp -ar --preserve=context '%s/' '%s/'", @@ -418,11 +423,11 @@ func (p *PrePivotHandler) copyEtc(oldDeploymentDir, newDeploymentDir string) err } // copyOrigin copies the .origin file between deployments preserving SELinux context -func (p *PrePivotHandler) copyDeploymentOrigin(oldSRPath, newSRPath, oldDeploymentName, newDeploymentName string) error { +func (p *PrePivotHandler) copyDeploymentOrigin(ctx context.Context, oldSRPath, newSRPath, oldDeploymentName, newDeploymentName string) error { oldOriginPath := filepath.Join(oldSRPath, "deploy", fmt.Sprintf("%s.origin", oldDeploymentName)) newOriginPath := filepath.Join(newSRPath, "deploy", fmt.Sprintf("%s.origin", newDeploymentName)) - if _, err := p.ops.RunInHostNamespace( + if _, err := p.ops.RunInHostNamespace(ctx, "bash", "-c", fmt.Sprintf( "cp -a --preserve=context '%s' '%s'", @@ -580,7 +585,7 @@ func (p *PrePivotHandler) writePullSecretToNewStateroot( return nil } -func (p *PrePivotHandler) prepareNetworkConfiguration() (*string, error) { +func (p *PrePivotHandler) prepareNetworkConfiguration(ctx context.Context) (*string, error) { if len(p.ipConfigs) == 0 { return nil, fmt.Errorf("no IP configurations provided") } @@ -615,7 +620,7 @@ func (p *PrePivotHandler) prepareNetworkConfiguration() (*string, error) { } } - iface, err := p.detectBrExNetworkInterface() + iface, err := p.detectBrExNetworkInterface(ctx) if err != nil { return nil, fmt.Errorf("failed to detect %s network interface: %w", BridgeExternalName, err) } @@ -675,10 +680,10 @@ func ipFamilyOfString(ip string) string { // so we must reliably identify it before generating network MachineConfigs. // To do this we query `ovs-vsctl list-ports br-ex` and ignore OVS-internal // patch ports, returning only the real NIC that backs `br-ex`. -func (p *PrePivotHandler) detectBrExNetworkInterface() (string, error) { +func (p *PrePivotHandler) detectBrExNetworkInterface(ctx context.Context) (string, error) { p.log.Infof("Detecting %s network interface", BridgeExternalName) - if output, err := p.ops.RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName); err == nil { + if output, err := p.ops.RunInHostNamespace(ctx, "ovs-vsctl", "list-ports", BridgeExternalName); err == nil { ports := strings.Fields(output) for _, port := range ports { // Skip OVS patch ports; we only care about the underlying host NIC @@ -864,7 +869,7 @@ func (p *PrePivotHandler) removeStaleFilesInNewStaterootForRegeneration() error // /var/lib/ovn-ic/etc/) that won't be regenerated by the owning component. // This mirrors the seed creation approach where all MCO-managed /var/lib/ // files are explicitly preserved. -func (p *PrePivotHandler) restoreMCDManagedVarLibFiles() error { +func (p *PrePivotHandler) restoreMCDManagedVarLibFiles(ctx context.Context) error { managedFiles, err := utils.GetMCDManagedVarLibFiles(p.mcdCurrentConfigPath) if err != nil { return fmt.Errorf("failed to get MCD managed files: %w", err) @@ -880,7 +885,7 @@ func (p *PrePivotHandler) restoreMCDManagedVarLibFiles() error { return fmt.Errorf("failed to create directory for %s: %w", dstPath, err) } - if _, err := p.ops.RunInHostNamespace( + if _, err := p.ops.RunInHostNamespace(ctx, "cp", "-a", "--preserve=context", srcPath, dstPath, ); err != nil { return fmt.Errorf("failed to copy MCO-managed file %s: %w", f, err) diff --git a/lca-cli/ipconfig/prepivot_test.go b/lca-cli/ipconfig/prepivot_test.go index 139e8d2478..340611451d 100644 --- a/lca-cli/ipconfig/prepivot_test.go +++ b/lca-cli/ipconfig/prepivot_test.go @@ -124,10 +124,10 @@ func TestGetDNSOverrideIP(t *testing.T) { func TestDetectBrExNetworkInterface(t *testing.T) { t.Run("returns_physical_interface", func(t *testing.T) { handler, ops, _, _ := newTestHandler(t) - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("ens3\npatch-br-ex-test", nil) - iface, err := handler.detectBrExNetworkInterface() + iface, err := handler.detectBrExNetworkInterface(context.Background()) if !assert.NoError(t, err) { return } @@ -136,19 +136,19 @@ func TestDetectBrExNetworkInterface(t *testing.T) { t.Run("errors_when_only_patch_ports", func(t *testing.T) { handler, ops, _, _ := newTestHandler(t) - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("patch-br-ex-test", nil) - _, err := handler.detectBrExNetworkInterface() + _, err := handler.detectBrExNetworkInterface(context.Background()) assert.Error(t, err) }) t.Run("errors_when_command_fails", func(t *testing.T) { handler, ops, _, _ := newTestHandler(t) - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("", errors.New("boom")) - _, err := handler.detectBrExNetworkInterface() + _, err := handler.detectBrExNetworkInterface(context.Background()) assert.Error(t, err) }) } @@ -172,10 +172,10 @@ func TestPrepareNetworkConfiguration(t *testing.T) { CurrentGateway: "2001::254", }, } - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("ens3\npatch-br-ex", nil) - nmstate, err := handler.prepareNetworkConfiguration() + nmstate, err := handler.prepareNetworkConfiguration(context.Background()) if !assert.NoError(t, err) { return } @@ -217,10 +217,10 @@ func TestPrepareNetworkConfiguration(t *testing.T) { t.Run(name, func(t *testing.T) { handler, ops, _, _ := newTestHandler(t) handler.ipConfigs = tc.configs - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName).AnyTimes(). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName).AnyTimes(). Return("", errors.New("not needed")) - _, err := handler.prepareNetworkConfiguration() + _, err := handler.prepareNetworkConfiguration(context.Background()) assert.Error(t, err) assert.Contains(t, err.Error(), tc.expect) }) @@ -230,10 +230,10 @@ func TestPrepareNetworkConfiguration(t *testing.T) { t.Run("propagates_interface_detection_error", func(t *testing.T) { handler, ops, _, _ := newTestHandler(t) handler.ipConfigs = []*NetworkIPConfig{{IP: "10.0.0.1", MachineNetwork: "10.0.0.0/24"}} - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("", errors.New("fail")) - _, err := handler.prepareNetworkConfiguration() + _, err := handler.prepareNetworkConfiguration(context.Background()) assert.Error(t, err) }) @@ -249,10 +249,10 @@ func TestPrepareNetworkConfiguration(t *testing.T) { CurrentGateway: "10.1.1.1", }, } - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName). + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName). Return("ens3\npatch-br-ex", nil) - nmstate, err := handler.prepareNetworkConfiguration() + nmstate, err := handler.prepareNetworkConfiguration(context.Background()) if !assert.NoError(t, err) { return } @@ -267,11 +267,11 @@ func TestSetDefaultDeploymentIfEnabled(t *testing.T) { handler.ostree = ostree handler.rpm = rpm - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(nil) - assert.NoError(t, handler.setDefaultDeploymentIfEnabled("new")) + assert.NoError(t, handler.setDefaultDeploymentIfEnabled(context.Background(), "new")) }) t.Run("feature_disabled", func(t *testing.T) { @@ -279,9 +279,9 @@ func TestSetDefaultDeploymentIfEnabled(t *testing.T) { handler.ostree = ostree handler.rpm = rpm - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(false) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(false, nil) - err := handler.setDefaultDeploymentIfEnabled("new") + err := handler.setDefaultDeploymentIfEnabled(context.Background(), "new") assert.Error(t, err) }) } @@ -293,12 +293,12 @@ func TestCopyVar(t *testing.T) { newPath := "/new" expectedCmd := fmt.Sprintf("cp -ar --preserve=context '%s/' '%s/'", filepath.Join(oldPath, "var"), newPath) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", nil) - assert.NoError(t, handler.copyVar(oldPath, newPath)) + assert.NoError(t, handler.copyVar(context.Background(), oldPath, newPath)) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", errors.New("fail")) - err := handler.copyVar(oldPath, newPath) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", errors.New("fail")) + err := handler.copyVar(context.Background(), oldPath, newPath) assert.Error(t, err) } @@ -309,11 +309,11 @@ func TestCopyEtc(t *testing.T) { newDir := "/new/deploy" expectedCmd := fmt.Sprintf("cp -ar --preserve=context '%s/' '%s/'", filepath.Join(oldDir, "etc"), newDir) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", nil) - assert.NoError(t, handler.copyEtc(oldDir, newDir)) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", nil) + assert.NoError(t, handler.copyEtc(context.Background(), oldDir, newDir)) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", errors.New("fail")) - assert.Error(t, handler.copyEtc(oldDir, newDir)) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", errors.New("fail")) + assert.Error(t, handler.copyEtc(context.Background(), oldDir, newDir)) } func TestCopyDeploymentOrigin(t *testing.T) { @@ -329,30 +329,30 @@ func TestCopyDeploymentOrigin(t *testing.T) { filepath.Join(newPath, "deploy", fmt.Sprintf("%s.origin", newName)), ) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", nil) - assert.NoError(t, handler.copyDeploymentOrigin(oldPath, newPath, oldName, newName)) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", nil) + assert.NoError(t, handler.copyDeploymentOrigin(context.Background(), oldPath, newPath, oldName, newName)) - ops.EXPECT().RunInHostNamespace("bash", "-c", expectedCmd).Return("", errors.New("fail")) - assert.Error(t, handler.copyDeploymentOrigin(oldPath, newPath, oldName, newName)) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", expectedCmd).Return("", errors.New("fail")) + assert.Error(t, handler.copyDeploymentOrigin(context.Background(), oldPath, newPath, oldName, newName)) } func TestEnsureSysrootWritable(t *testing.T) { handler, ops, _, _ := newTestHandler(t) - ops.EXPECT().RemountSysroot().Return(nil) - assert.NoError(t, handler.ensureSysrootWritable()) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(nil) + assert.NoError(t, handler.ensureSysrootWritable(context.Background())) - ops.EXPECT().RemountSysroot().Return(errors.New("ro")) - assert.Error(t, handler.ensureSysrootWritable()) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(errors.New("ro")) + assert.Error(t, handler.ensureSysrootWritable(context.Background())) } func TestGetBootedCommit(t *testing.T) { handler, _, _, rpm := newTestHandler(t) handler.rpm = rpm - rpm.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + rpm.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: []rpmostreeclient.Deployment{{Checksum: "abc", Booted: true}}, }, nil) - commit, err := handler.getBootedCommit() + commit, err := handler.getBootedCommit(context.Background()) if !assert.NoError(t, err) { return } @@ -361,10 +361,10 @@ func TestGetBootedCommit(t *testing.T) { } assert.Equal(t, "abc", *commit) - rpm.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + rpm.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: []rpmostreeclient.Deployment{{Checksum: "abc", Booted: false}}, }, nil) - _, err = handler.getBootedCommit() + _, err = handler.getBootedCommit(context.Background()) assert.Error(t, err) } @@ -375,12 +375,12 @@ func TestDeployNewStateroot(t *testing.T) { data := &OstreeData{NewStateroot: &StaterootData{Name: "new"}} - ostree.EXPECT().OSInit("new").Return(nil) - ostree.EXPECT().Deploy("new", "commit", []string{"karg"}, rpm, false).Return(nil) - ostree.EXPECT().GetDeployment("new").Return("deploy1", nil) - ostree.EXPECT().GetDeploymentDir("new").Return("/deployment/dir", nil) + ostree.EXPECT().OSInit(gomock.Any(), "new").Return(nil) + ostree.EXPECT().Deploy(gomock.Any(), "new", "commit", []string{"karg"}, rpm, false).Return(nil) + ostree.EXPECT().GetDeployment(gomock.Any(), "new").Return("deploy1", nil) + ostree.EXPECT().GetDeploymentDir(gomock.Any(), "new").Return("/deployment/dir", nil) - assert.NoError(t, handler.deployNewStateroot(data, "commit", []string{"karg"})) + assert.NoError(t, handler.deployNewStateroot(context.Background(), data, "commit", []string{"karg"})) assert.Equal(t, "deploy1", data.NewStateroot.DeploymentName) assert.Equal(t, "/deployment/dir", data.NewStateroot.DeploymentDir) } @@ -401,20 +401,20 @@ func TestPrepareNewStaterootDeploysWhenMissing(t *testing.T) { }, } - ops.EXPECT().RemountSysroot().Return(nil) - rpm.EXPECT().QueryStatus().Return(&rpmostreeclient.Status{ + ops.EXPECT().RemountSysroot(gomock.Any()).Return(nil) + rpm.EXPECT().QueryStatus(gomock.Any()).Return(&rpmostreeclient.Status{ Deployments: []rpmostreeclient.Deployment{{Checksum: "abc", Booted: true}}, }, nil) - ostree.EXPECT().OSInit("new").Return(nil) - ostree.EXPECT().Deploy("new", "abc", []string{"karg"}, rpm, false).Return(nil) - ostree.EXPECT().GetDeployment("new").Return("newdep", nil) - ostree.EXPECT().GetDeploymentDir("new").Return("/new/deploy", nil) - ops.EXPECT().RunInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("", nil) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(nil) - - err := handler.prepareNewStateroot(handler.ostreeData, []string{"karg"}) + ostree.EXPECT().OSInit(gomock.Any(), "new").Return(nil) + ostree.EXPECT().Deploy(gomock.Any(), "new", "abc", []string{"karg"}, rpm, false).Return(nil) + ostree.EXPECT().GetDeployment(gomock.Any(), "new").Return("newdep", nil) + ostree.EXPECT().GetDeploymentDir(gomock.Any(), "new").Return("/new/deploy", nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("", nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(nil) + + err := handler.prepareNewStateroot(context.Background(), handler.ostreeData, []string{"karg"}) assert.NoError(t, err) assert.Equal(t, "newdep", handler.ostreeData.NewStateroot.DeploymentName) } @@ -437,14 +437,14 @@ func TestPrepareNewStaterootSkipsDeployWhenAlreadyPresent(t *testing.T) { }, } - ops.EXPECT().RemountSysroot().Return(nil) - ostree.EXPECT().Deploy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) - ops.EXPECT().RunInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("", nil) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(nil) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(nil) + ostree.EXPECT().Deploy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + ops.EXPECT().RunInHostNamespace(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("", nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(nil) - assert.NoError(t, handler.prepareNewStateroot(handler.ostreeData, nil)) + assert.NoError(t, handler.prepareNewStateroot(context.Background(), handler.ostreeData, nil)) } func TestUpdateDNSMasqOverrideIPInNewStateroot(t *testing.T) { @@ -515,8 +515,8 @@ func TestPrepareNewStaterootErrorsPropagate(t *testing.T) { NewStateroot: &StaterootData{Name: "new", Path: "/new"}, } - ops.EXPECT().RemountSysroot().Return(errors.New("ro")) - err := handler.prepareNewStateroot(handler.ostreeData, nil) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(errors.New("ro")) + err := handler.prepareNewStateroot(context.Background(), handler.ostreeData, nil) assert.Error(t, err) } @@ -615,18 +615,18 @@ func TestRunStopsAndReenablesOnFailure(t *testing.T) { ops.EXPECT().MkdirAll(filepath.Join(handler.hostWorkspaceDir, common.KubeconfigCryptoDir), os.FileMode(0o755)).Return(nil) // Network config - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName).Return("ens3\npatch-br-ex", nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName).Return("ens3\npatch-br-ex", nil) // Stop/enable services - ops.EXPECT().StopClusterServices().Return(nil) - ops.EXPECT().EnableClusterServices().Return(nil) + ops.EXPECT().StopClusterServices(gomock.Any()).Return(nil) + ops.EXPECT().EnableClusterServices(gomock.Any()).Return(nil) // New stateroot prep (no deploy) - ops.EXPECT().RemountSysroot().Return(nil) - ops.EXPECT().RunInHostNamespace("bash", "-c", gomock.Any()).AnyTimes().Return("", nil) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(nil) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", gomock.Any()).AnyTimes().Return("", nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(nil) // Writes to new stateroot workspace ops.EXPECT().WriteFile( @@ -666,8 +666,8 @@ func TestRunFailsBeforeStoppingServices(t *testing.T) { } ops.EXPECT().ReadFile(handler.mcdCurrentConfigPath).Return(nil, os.ErrNotExist) - ops.EXPECT().StopClusterServices().Times(0) - ops.EXPECT().EnableClusterServices().Times(0) + ops.EXPECT().StopClusterServices(gomock.Any()).Times(0) + ops.EXPECT().EnableClusterServices(gomock.Any()).Times(0) err := handler.Run(context.Background()) assert.Error(t, err) @@ -716,16 +716,16 @@ func TestRunDoesNotPersistOrDeleteACMHubKubeconfigSecretWhenPresent(t *testing.T ops.EXPECT().ReadFile(common.ImageRegistryAuthFile).Return([]byte("ps"), nil) ops.EXPECT().MkdirAll(filepath.Join(handler.hostWorkspaceDir, common.KubeconfigCryptoDir), os.FileMode(0o755)).Return(nil) - ops.EXPECT().RunInHostNamespace("ovs-vsctl", "list-ports", BridgeExternalName).Return("ens3\npatch-br-ex", nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "ovs-vsctl", "list-ports", BridgeExternalName).Return("ens3\npatch-br-ex", nil) - ops.EXPECT().StopClusterServices().Return(nil) - ops.EXPECT().EnableClusterServices().Return(nil) + ops.EXPECT().StopClusterServices(gomock.Any()).Return(nil) + ops.EXPECT().EnableClusterServices(gomock.Any()).Return(nil) - ops.EXPECT().RemountSysroot().Return(nil) - ops.EXPECT().RunInHostNamespace("bash", "-c", gomock.Any()).AnyTimes().Return("", nil) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(nil) + ops.EXPECT().RemountSysroot(gomock.Any()).Return(nil) + ops.EXPECT().RunInHostNamespace(gomock.Any(), "bash", "-c", gomock.Any()).AnyTimes().Return("", nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(nil) ops.EXPECT().WriteFile( filepath.Join(handler.ostreeData.NewStateroot.Path, common.IPConfigPullSecretFile), @@ -918,8 +918,8 @@ func TestRestoreMCDManagedVarLibFiles(t *testing.T) { return os.MkdirAll(path, perm) }) - mockOps.EXPECT().RunInHostNamespace("cp", "-a", "--preserve=context", gomock.Any(), gomock.Any()). - DoAndReturn(func(command string, args ...string) (string, error) { + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "cp", "-a", "--preserve=context", gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, command string, args ...string) (string, error) { src := args[2] dst := args[3] data, err := os.ReadFile(src) @@ -929,7 +929,7 @@ func TestRestoreMCDManagedVarLibFiles(t *testing.T) { return "", os.WriteFile(dst, data, 0o644) }).Times(2) - err := handler.restoreMCDManagedVarLibFiles() + err := handler.restoreMCDManagedVarLibFiles(context.Background()) assert.NoError(t, err) // Verify both files were restored @@ -1033,8 +1033,8 @@ func TestRemoveStaleAndRestorePreservesOvnMCDFiles(t *testing.T) { srcPath := filepath.Join(oldSR, common.OvnIcEtcFolder, "enable_dynamic_cpu_affinity") dstPath := filepath.Join(newSR, common.OvnIcEtcFolder, "enable_dynamic_cpu_affinity") - mockOps.EXPECT().RunInHostNamespace("cp", "-a", "--preserve=context", srcPath, dstPath). - DoAndReturn(func(command string, args ...string) (string, error) { + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "cp", "-a", "--preserve=context", srcPath, dstPath). + DoAndReturn(func(_ context.Context, command string, args ...string) (string, error) { data, err := os.ReadFile(srcPath) if err != nil { return "", err @@ -1042,7 +1042,7 @@ func TestRemoveStaleAndRestorePreservesOvnMCDFiles(t *testing.T) { return "", os.WriteFile(dstPath, data, 0o644) }) - err = handler.restoreMCDManagedVarLibFiles() + err = handler.restoreMCDManagedVarLibFiles(context.Background()) assert.NoError(t, err) // Verify MCO-managed file was restored diff --git a/lca-cli/ipconfig/rollback.go b/lca-cli/ipconfig/rollback.go index 6d0a3f1bc0..cd4fd59e4d 100644 --- a/lca-cli/ipconfig/rollback.go +++ b/lca-cli/ipconfig/rollback.go @@ -17,6 +17,7 @@ limitations under the License. package ipconfig import ( + "context" "fmt" "github.com/sirupsen/logrus" @@ -41,15 +42,17 @@ func NewRollbackHandler(log *logrus.Logger, ops ops.Ops, ostree intOstree.IClien // Run performs the rollback by setting the default deployment to the one // associated with the provided stateroot, if the OSTree feature is available. -func (h *RollbackHandler) Run(stateroot string) error { +func (h *RollbackHandler) Run(ctx context.Context, stateroot string) error { h.log.Infof("IP config rollback started with stateroot: %s", stateroot) - if h.ostree.IsOstreeAdminSetDefaultFeatureEnabled() { - idx, err := h.rpm.GetDeploymentIndex(stateroot) + if enabled, err := h.ostree.IsOstreeAdminSetDefaultFeatureEnabled(ctx); err != nil { + return fmt.Errorf("failed to check ostree set-default feature: %w", err) + } else if enabled { + idx, err := h.rpm.GetDeploymentIndex(ctx, stateroot) if err != nil { return fmt.Errorf("failed to get deployment index for %s: %w", stateroot, err) } - if err := h.ostree.SetDefaultDeployment(idx); err != nil { + if err := h.ostree.SetDefaultDeployment(ctx, idx); err != nil { return fmt.Errorf("failed to set default deployment: %w", err) } } diff --git a/lca-cli/ipconfig/rollback_test.go b/lca-cli/ipconfig/rollback_test.go index b85835699c..e4632aad11 100644 --- a/lca-cli/ipconfig/rollback_test.go +++ b/lca-cli/ipconfig/rollback_test.go @@ -1,6 +1,7 @@ package ipconfig import ( + "context" "errors" "testing" @@ -29,21 +30,21 @@ func newRollbackHandler(t *testing.T) (*RollbackHandler, *opsmock.MockOps, *ostr func TestRollbackRunFeatureEnabledSuccess(t *testing.T) { handler, _, ostree, rpm := newRollbackHandler(t) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(3, nil) - ostree.EXPECT().SetDefaultDeployment(3).Return(nil) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(3, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 3).Return(nil) - assert.NoError(t, handler.Run("new")) + assert.NoError(t, handler.Run(context.Background(), "new")) } func TestRollbackRunGetDeploymentIndexError(t *testing.T) { handler, _, ostree, rpm := newRollbackHandler(t) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(0, errors.New("idx-fail")) - ostree.EXPECT().SetDefaultDeployment(gomock.Any()).Times(0) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(0, errors.New("idx-fail")) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), gomock.Any()).Times(0) - err := handler.Run("new") + err := handler.Run(context.Background(), "new") assert.Error(t, err) assert.Contains(t, err.Error(), "failed to get deployment index") } @@ -51,11 +52,11 @@ func TestRollbackRunGetDeploymentIndexError(t *testing.T) { func TestRollbackRunSetDefaultDeploymentError(t *testing.T) { handler, _, ostree, rpm := newRollbackHandler(t) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(true) - rpm.EXPECT().GetDeploymentIndex("new").Return(1, nil) - ostree.EXPECT().SetDefaultDeployment(1).Return(errors.New("set-fail")) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(true, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), "new").Return(1, nil) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), 1).Return(errors.New("set-fail")) - err := handler.Run("new") + err := handler.Run(context.Background(), "new") assert.Error(t, err) assert.Contains(t, err.Error(), "failed to set default deployment") } @@ -63,9 +64,9 @@ func TestRollbackRunSetDefaultDeploymentError(t *testing.T) { func TestRollbackRunFeatureDisabledNoop(t *testing.T) { handler, _, ostree, rpm := newRollbackHandler(t) - ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled().Return(false) - rpm.EXPECT().GetDeploymentIndex(gomock.Any()).Times(0) - ostree.EXPECT().SetDefaultDeployment(gomock.Any()).Times(0) + ostree.EXPECT().IsOstreeAdminSetDefaultFeatureEnabled(gomock.Any()).Return(false, nil) + rpm.EXPECT().GetDeploymentIndex(gomock.Any(), gomock.Any()).Times(0) + ostree.EXPECT().SetDefaultDeployment(gomock.Any(), gomock.Any()).Times(0) - assert.NoError(t, handler.Run("new")) + assert.NoError(t, handler.Run(context.Background(), "new")) } diff --git a/lca-cli/ops/execute.go b/lca-cli/ops/execute.go index 6c0cf8f0c5..a8293fd6fc 100644 --- a/lca-cli/ops/execute.go +++ b/lca-cli/ops/execute.go @@ -2,6 +2,7 @@ package ops import ( "bytes" + "context" "fmt" "io" "os/exec" @@ -18,8 +19,8 @@ import ( const nsenter = "nsenter" type Execute interface { - Execute(command string, args ...string) (string, error) - ExecuteWithLiveLogger(command string, args ...string) (string, error) + Execute(ctx context.Context, command string, args ...string) (string, error) + ExecuteWithLiveLogger(ctx context.Context, command string, args ...string) (string, error) } type executor struct { @@ -27,9 +28,9 @@ type executor struct { verbose bool } -func (e *executor) execute(liveLogger io.Writer, root, command string, args ...string) (string, error) { +func (e *executor) execute(ctx context.Context, liveLogger io.Writer, root, command string, args ...string) (string, error) { e.log.Infof("Executing %s with args %s", command, args) - cmd := exec.Command(command, args...) //nolint:gosec // command is validated by caller + cmd := exec.CommandContext(ctx, command, args...) //nolint:gosec // command is validated by caller var stdoutBytes bytes.Buffer if liveLogger != nil { cmd.Stdout = io.MultiWriter(liveLogger, &stdoutBytes) @@ -59,12 +60,12 @@ func NewRegularExecutor(logger *logrus.Logger, verbose bool) Execute { return ®ularExecutor{executor: executor{logger, verbose}} } -func (e *regularExecutor) Execute(command string, args ...string) (string, error) { - return e.execute(nil, "", command, args...) +func (e *regularExecutor) Execute(ctx context.Context, command string, args ...string) (string, error) { + return e.execute(ctx, nil, "", command, args...) } -func (e *regularExecutor) ExecuteWithLiveLogger(command string, args ...string) (string, error) { - return e.execute(e.log.Writer(), "", command, args...) +func (e *regularExecutor) ExecuteWithLiveLogger(ctx context.Context, command string, args ...string) (string, error) { + return e.execute(ctx, e.log.Writer(), "", command, args...) } type nsenterExecutor struct { @@ -75,20 +76,20 @@ func NewNsenterExecutor(logger *logrus.Logger, verbose bool) Execute { return &nsenterExecutor{executor: executor{logger, verbose}} } -func (e *nsenterExecutor) ExecuteWithLiveLogger(command string, args ...string) (string, error) { - return e.baseExecute(e.log.Writer(), command, args...) +func (e *nsenterExecutor) ExecuteWithLiveLogger(ctx context.Context, command string, args ...string) (string, error) { + return e.baseExecute(ctx, e.log.Writer(), command, args...) } -func (e *nsenterExecutor) baseExecute(writer io.Writer, command string, args ...string) (string, error) { +func (e *nsenterExecutor) baseExecute(ctx context.Context, writer io.Writer, command string, args ...string) (string, error) { // nsenter is used here to launch processes inside the container in a way that makes said processes feel // and behave as if they're running on the host directly rather than inside the container arguments := append(nsenterArgs(), command) arguments = append(arguments, args...) - return e.execute(writer, "", nsenter, arguments...) + return e.execute(ctx, writer, "", nsenter, arguments...) } -func (e *nsenterExecutor) Execute(command string, args ...string) (string, error) { - return e.baseExecute(nil, command, args...) +func (e *nsenterExecutor) Execute(ctx context.Context, command string, args ...string) (string, error) { + return e.baseExecute(ctx, nil, command, args...) } type chrootExecutor struct { @@ -103,18 +104,18 @@ func NewChrootExecutor(logger *logrus.Logger, verbose bool, root string) Execute // Running a command with chroot using exec.Command runs into issues with exec.LookPath, // if an absolute path is not used for the "command", as it does not account for the chroot dir. // To workaround this issue, prefix the command with /usr/bin/env. -func (e *chrootExecutor) baseExecute(writer io.Writer, command string, args ...string) (string, error) { +func (e *chrootExecutor) baseExecute(ctx context.Context, writer io.Writer, command string, args ...string) (string, error) { commandBase := "/usr/bin/env" args = append([]string{"--", command}, args...) - return e.execute(writer, e.root, commandBase, args...) + return e.execute(ctx, writer, e.root, commandBase, args...) } -func (e *chrootExecutor) Execute(command string, args ...string) (string, error) { - return e.baseExecute(nil, command, args...) +func (e *chrootExecutor) Execute(ctx context.Context, command string, args ...string) (string, error) { + return e.baseExecute(ctx, nil, command, args...) } -func (e *chrootExecutor) ExecuteWithLiveLogger(command string, args ...string) (string, error) { - return e.baseExecute(e.log.Writer(), command, args...) +func (e *chrootExecutor) ExecuteWithLiveLogger(ctx context.Context, command string, args ...string) (string, error) { + return e.baseExecute(ctx, e.log.Writer(), command, args...) } func nsenterArgs() []string { diff --git a/lca-cli/ops/mock_execute.go b/lca-cli/ops/mock_execute.go index 9c63b6c2c2..6033c3a4ce 100644 --- a/lca-cli/ops/mock_execute.go +++ b/lca-cli/ops/mock_execute.go @@ -9,6 +9,7 @@ package ops import ( + context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -38,9 +39,9 @@ func (m *MockExecute) EXPECT() *MockExecuteMockRecorder { } // Execute mocks base method. -func (m *MockExecute) Execute(command string, args ...string) (string, error) { +func (m *MockExecute) Execute(ctx context.Context, command string, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{command} + varargs := []any{ctx, command} for _, a := range args { varargs = append(varargs, a) } @@ -51,16 +52,16 @@ func (m *MockExecute) Execute(command string, args ...string) (string, error) { } // Execute indicates an expected call of Execute. -func (mr *MockExecuteMockRecorder) Execute(command any, args ...any) *gomock.Call { +func (mr *MockExecuteMockRecorder) Execute(ctx, command any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{command}, args...) + varargs := append([]any{ctx, command}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockExecute)(nil).Execute), varargs...) } // ExecuteWithLiveLogger mocks base method. -func (m *MockExecute) ExecuteWithLiveLogger(command string, args ...string) (string, error) { +func (m *MockExecute) ExecuteWithLiveLogger(ctx context.Context, command string, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{command} + varargs := []any{ctx, command} for _, a := range args { varargs = append(varargs, a) } @@ -71,8 +72,8 @@ func (m *MockExecute) ExecuteWithLiveLogger(command string, args ...string) (str } // ExecuteWithLiveLogger indicates an expected call of ExecuteWithLiveLogger. -func (mr *MockExecuteMockRecorder) ExecuteWithLiveLogger(command any, args ...any) *gomock.Call { +func (mr *MockExecuteMockRecorder) ExecuteWithLiveLogger(ctx, command any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{command}, args...) + varargs := append([]any{ctx, command}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteWithLiveLogger", reflect.TypeOf((*MockExecute)(nil).ExecuteWithLiveLogger), varargs...) } diff --git a/lca-cli/ops/mock_ops.go b/lca-cli/ops/mock_ops.go index 773464462f..8511ffa8c4 100644 --- a/lca-cli/ops/mock_ops.go +++ b/lca-cli/ops/mock_ops.go @@ -9,6 +9,7 @@ package ops import ( + context "context" os "os" reflect "reflect" @@ -69,17 +70,17 @@ func (mr *MockOpsMockRecorder) CopyFile(src, dest, perm any) *gomock.Call { } // CreateExtraPartition mocks base method. -func (m *MockOps) CreateExtraPartition(installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error { +func (m *MockOps) CreateExtraPartition(ctx context.Context, installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateExtraPartition", installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber) + ret := m.ctrl.Call(m, "CreateExtraPartition", ctx, installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber) ret0, _ := ret[0].(error) return ret0 } // CreateExtraPartition indicates an expected call of CreateExtraPartition. -func (mr *MockOpsMockRecorder) CreateExtraPartition(installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber any) *gomock.Call { +func (mr *MockOpsMockRecorder) CreateExtraPartition(ctx, installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateExtraPartition", reflect.TypeOf((*MockOps)(nil).CreateExtraPartition), installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateExtraPartition", reflect.TypeOf((*MockOps)(nil).CreateExtraPartition), ctx, installationDisk, extraPartitionLabel, extraPartitionStart, extraPartitionNumber) } // CreateIsoWithEmbeddedIgnition mocks base method. @@ -97,60 +98,60 @@ func (mr *MockOpsMockRecorder) CreateIsoWithEmbeddedIgnition(log, ignitionBytes, } // EnableClusterServices mocks base method. -func (m *MockOps) EnableClusterServices() error { +func (m *MockOps) EnableClusterServices(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnableClusterServices") + ret := m.ctrl.Call(m, "EnableClusterServices", ctx) ret0, _ := ret[0].(error) return ret0 } // EnableClusterServices indicates an expected call of EnableClusterServices. -func (mr *MockOpsMockRecorder) EnableClusterServices() *gomock.Call { +func (mr *MockOpsMockRecorder) EnableClusterServices(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableClusterServices", reflect.TypeOf((*MockOps)(nil).EnableClusterServices)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableClusterServices", reflect.TypeOf((*MockOps)(nil).EnableClusterServices), ctx) } // ExtractTarWithSELinux mocks base method. -func (m *MockOps) ExtractTarWithSELinux(srcPath, destPath string) error { +func (m *MockOps) ExtractTarWithSELinux(ctx context.Context, srcPath, destPath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExtractTarWithSELinux", srcPath, destPath) + ret := m.ctrl.Call(m, "ExtractTarWithSELinux", ctx, srcPath, destPath) ret0, _ := ret[0].(error) return ret0 } // ExtractTarWithSELinux indicates an expected call of ExtractTarWithSELinux. -func (mr *MockOpsMockRecorder) ExtractTarWithSELinux(srcPath, destPath any) *gomock.Call { +func (mr *MockOpsMockRecorder) ExtractTarWithSELinux(ctx, srcPath, destPath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtractTarWithSELinux", reflect.TypeOf((*MockOps)(nil).ExtractTarWithSELinux), srcPath, destPath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtractTarWithSELinux", reflect.TypeOf((*MockOps)(nil).ExtractTarWithSELinux), ctx, srcPath, destPath) } // ForceExpireSeedCrypto mocks base method. -func (m *MockOps) ForceExpireSeedCrypto(recertContainerImage, authFile string, hasKubeAdminPassword bool) error { +func (m *MockOps) ForceExpireSeedCrypto(ctx context.Context, recertContainerImage, authFile string, hasKubeAdminPassword bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ForceExpireSeedCrypto", recertContainerImage, authFile, hasKubeAdminPassword) + ret := m.ctrl.Call(m, "ForceExpireSeedCrypto", ctx, recertContainerImage, authFile, hasKubeAdminPassword) ret0, _ := ret[0].(error) return ret0 } // ForceExpireSeedCrypto indicates an expected call of ForceExpireSeedCrypto. -func (mr *MockOpsMockRecorder) ForceExpireSeedCrypto(recertContainerImage, authFile, hasKubeAdminPassword any) *gomock.Call { +func (mr *MockOpsMockRecorder) ForceExpireSeedCrypto(ctx, recertContainerImage, authFile, hasKubeAdminPassword any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForceExpireSeedCrypto", reflect.TypeOf((*MockOps)(nil).ForceExpireSeedCrypto), recertContainerImage, authFile, hasKubeAdminPassword) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForceExpireSeedCrypto", reflect.TypeOf((*MockOps)(nil).ForceExpireSeedCrypto), ctx, recertContainerImage, authFile, hasKubeAdminPassword) } // GetContainerStorageTarget mocks base method. -func (m *MockOps) GetContainerStorageTarget() (string, error) { +func (m *MockOps) GetContainerStorageTarget(ctx context.Context) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetContainerStorageTarget") + ret := m.ctrl.Call(m, "GetContainerStorageTarget", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetContainerStorageTarget indicates an expected call of GetContainerStorageTarget. -func (mr *MockOpsMockRecorder) GetContainerStorageTarget() *gomock.Call { +func (mr *MockOpsMockRecorder) GetContainerStorageTarget(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContainerStorageTarget", reflect.TypeOf((*MockOps)(nil).GetContainerStorageTarget)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContainerStorageTarget", reflect.TypeOf((*MockOps)(nil).GetContainerStorageTarget), ctx) } // GetHostname mocks base method. @@ -169,33 +170,33 @@ func (mr *MockOpsMockRecorder) GetHostname() *gomock.Call { } // ImageExists mocks base method. -func (m *MockOps) ImageExists(img string) (bool, error) { +func (m *MockOps) ImageExists(ctx context.Context, img string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageExists", img) + ret := m.ctrl.Call(m, "ImageExists", ctx, img) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // ImageExists indicates an expected call of ImageExists. -func (mr *MockOpsMockRecorder) ImageExists(img any) *gomock.Call { +func (mr *MockOpsMockRecorder) ImageExists(ctx, img any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageExists", reflect.TypeOf((*MockOps)(nil).ImageExists), img) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageExists", reflect.TypeOf((*MockOps)(nil).ImageExists), ctx, img) } // IsImageMounted mocks base method. -func (m *MockOps) IsImageMounted(img string) (bool, error) { +func (m *MockOps) IsImageMounted(ctx context.Context, img string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsImageMounted", img) + ret := m.ctrl.Call(m, "IsImageMounted", ctx, img) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsImageMounted indicates an expected call of IsImageMounted. -func (mr *MockOpsMockRecorder) IsImageMounted(img any) *gomock.Call { +func (mr *MockOpsMockRecorder) IsImageMounted(ctx, img any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsImageMounted", reflect.TypeOf((*MockOps)(nil).IsImageMounted), img) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsImageMounted", reflect.TypeOf((*MockOps)(nil).IsImageMounted), ctx, img) } // IsNotExist mocks base method. @@ -213,18 +214,18 @@ func (mr *MockOpsMockRecorder) IsNotExist(err any) *gomock.Call { } // ListBlockDevices mocks base method. -func (m *MockOps) ListBlockDevices() ([]BlockDevice, error) { +func (m *MockOps) ListBlockDevices(ctx context.Context) ([]BlockDevice, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListBlockDevices") + ret := m.ctrl.Call(m, "ListBlockDevices", ctx) ret0, _ := ret[0].([]BlockDevice) ret1, _ := ret[1].(error) return ret0, ret1 } // ListBlockDevices indicates an expected call of ListBlockDevices. -func (mr *MockOpsMockRecorder) ListBlockDevices() *gomock.Call { +func (mr *MockOpsMockRecorder) ListBlockDevices(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlockDevices", reflect.TypeOf((*MockOps)(nil).ListBlockDevices)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlockDevices", reflect.TypeOf((*MockOps)(nil).ListBlockDevices), ctx) } // MkdirAll mocks base method. @@ -242,32 +243,32 @@ func (mr *MockOpsMockRecorder) MkdirAll(path, perm any) *gomock.Call { } // Mount mocks base method. -func (m *MockOps) Mount(deviceName, mountFolder string) error { +func (m *MockOps) Mount(ctx context.Context, deviceName, mountFolder string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Mount", deviceName, mountFolder) + ret := m.ctrl.Call(m, "Mount", ctx, deviceName, mountFolder) ret0, _ := ret[0].(error) return ret0 } // Mount indicates an expected call of Mount. -func (mr *MockOpsMockRecorder) Mount(deviceName, mountFolder any) *gomock.Call { +func (mr *MockOpsMockRecorder) Mount(ctx, deviceName, mountFolder any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mount", reflect.TypeOf((*MockOps)(nil).Mount), deviceName, mountFolder) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mount", reflect.TypeOf((*MockOps)(nil).Mount), ctx, deviceName, mountFolder) } // MountImage mocks base method. -func (m *MockOps) MountImage(img string) (string, error) { +func (m *MockOps) MountImage(ctx context.Context, img string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MountImage", img) + ret := m.ctrl.Call(m, "MountImage", ctx, img) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // MountImage indicates an expected call of MountImage. -func (mr *MockOpsMockRecorder) MountImage(img any) *gomock.Call { +func (mr *MockOpsMockRecorder) MountImage(ctx, img any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MountImage", reflect.TypeOf((*MockOps)(nil).MountImage), img) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MountImage", reflect.TypeOf((*MockOps)(nil).MountImage), ctx, img) } // ReadDir mocks base method. @@ -301,9 +302,9 @@ func (mr *MockOpsMockRecorder) ReadFile(filename any) *gomock.Call { } // RecertFullFlow mocks base method. -func (m *MockOps) RecertFullFlow(recertContainerImage, authFile, configFile string, preRecertOperations, postRecertOperations func() error, additionalPodmanParams ...string) error { +func (m *MockOps) RecertFullFlow(ctx context.Context, recertContainerImage, authFile, configFile string, preRecertOperations, postRecertOperations func() error, additionalPodmanParams ...string) error { m.ctrl.T.Helper() - varargs := []any{recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations} + varargs := []any{ctx, recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations} for _, a := range additionalPodmanParams { varargs = append(varargs, a) } @@ -313,38 +314,38 @@ func (m *MockOps) RecertFullFlow(recertContainerImage, authFile, configFile stri } // RecertFullFlow indicates an expected call of RecertFullFlow. -func (mr *MockOpsMockRecorder) RecertFullFlow(recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations any, additionalPodmanParams ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) RecertFullFlow(ctx, recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations any, additionalPodmanParams ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations}, additionalPodmanParams...) + varargs := append([]any{ctx, recertContainerImage, authFile, configFile, preRecertOperations, postRecertOperations}, additionalPodmanParams...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecertFullFlow", reflect.TypeOf((*MockOps)(nil).RecertFullFlow), varargs...) } // RemountBoot mocks base method. -func (m *MockOps) RemountBoot() error { +func (m *MockOps) RemountBoot(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemountBoot") + ret := m.ctrl.Call(m, "RemountBoot", ctx) ret0, _ := ret[0].(error) return ret0 } // RemountBoot indicates an expected call of RemountBoot. -func (mr *MockOpsMockRecorder) RemountBoot() *gomock.Call { +func (mr *MockOpsMockRecorder) RemountBoot(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemountBoot", reflect.TypeOf((*MockOps)(nil).RemountBoot)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemountBoot", reflect.TypeOf((*MockOps)(nil).RemountBoot), ctx) } // RemountSysroot mocks base method. -func (m *MockOps) RemountSysroot() error { +func (m *MockOps) RemountSysroot(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemountSysroot") + ret := m.ctrl.Call(m, "RemountSysroot", ctx) ret0, _ := ret[0].(error) return ret0 } // RemountSysroot indicates an expected call of RemountSysroot. -func (mr *MockOpsMockRecorder) RemountSysroot() *gomock.Call { +func (mr *MockOpsMockRecorder) RemountSysroot(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemountSysroot", reflect.TypeOf((*MockOps)(nil).RemountSysroot)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemountSysroot", reflect.TypeOf((*MockOps)(nil).RemountSysroot), ctx) } // RemoveAllFiles mocks base method. @@ -376,23 +377,23 @@ func (mr *MockOpsMockRecorder) RemoveFile(path any) *gomock.Call { } // RestoreOriginalSeedCrypto mocks base method. -func (m *MockOps) RestoreOriginalSeedCrypto(recertContainerImage, authFile string) error { +func (m *MockOps) RestoreOriginalSeedCrypto(ctx context.Context, recertContainerImage, authFile string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RestoreOriginalSeedCrypto", recertContainerImage, authFile) + ret := m.ctrl.Call(m, "RestoreOriginalSeedCrypto", ctx, recertContainerImage, authFile) ret0, _ := ret[0].(error) return ret0 } // RestoreOriginalSeedCrypto indicates an expected call of RestoreOriginalSeedCrypto. -func (mr *MockOpsMockRecorder) RestoreOriginalSeedCrypto(recertContainerImage, authFile any) *gomock.Call { +func (mr *MockOpsMockRecorder) RestoreOriginalSeedCrypto(ctx, recertContainerImage, authFile any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreOriginalSeedCrypto", reflect.TypeOf((*MockOps)(nil).RestoreOriginalSeedCrypto), recertContainerImage, authFile) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreOriginalSeedCrypto", reflect.TypeOf((*MockOps)(nil).RestoreOriginalSeedCrypto), ctx, recertContainerImage, authFile) } // RunBashInHostNamespace mocks base method. -func (m *MockOps) RunBashInHostNamespace(command string, args ...string) (string, error) { +func (m *MockOps) RunBashInHostNamespace(ctx context.Context, command string, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{command} + varargs := []any{ctx, command} for _, a := range args { varargs = append(varargs, a) } @@ -403,16 +404,16 @@ func (m *MockOps) RunBashInHostNamespace(command string, args ...string) (string } // RunBashInHostNamespace indicates an expected call of RunBashInHostNamespace. -func (mr *MockOpsMockRecorder) RunBashInHostNamespace(command any, args ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunBashInHostNamespace(ctx, command any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{command}, args...) + varargs := append([]any{ctx, command}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunBashInHostNamespace", reflect.TypeOf((*MockOps)(nil).RunBashInHostNamespace), varargs...) } // RunInHostNamespace mocks base method. -func (m *MockOps) RunInHostNamespace(command string, args ...string) (string, error) { +func (m *MockOps) RunInHostNamespace(ctx context.Context, command string, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{command} + varargs := []any{ctx, command} for _, a := range args { varargs = append(varargs, a) } @@ -423,30 +424,30 @@ func (m *MockOps) RunInHostNamespace(command string, args ...string) (string, er } // RunInHostNamespace indicates an expected call of RunInHostNamespace. -func (mr *MockOpsMockRecorder) RunInHostNamespace(command any, args ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunInHostNamespace(ctx, command any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{command}, args...) + varargs := append([]any{ctx, command}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunInHostNamespace", reflect.TypeOf((*MockOps)(nil).RunInHostNamespace), varargs...) } // RunListOfCommands mocks base method. -func (m *MockOps) RunListOfCommands(cmds []*CMD) error { +func (m *MockOps) RunListOfCommands(ctx context.Context, cmds []*CMD) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RunListOfCommands", cmds) + ret := m.ctrl.Call(m, "RunListOfCommands", ctx, cmds) ret0, _ := ret[0].(error) return ret0 } // RunListOfCommands indicates an expected call of RunListOfCommands. -func (mr *MockOpsMockRecorder) RunListOfCommands(cmds any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunListOfCommands(ctx, cmds any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunListOfCommands", reflect.TypeOf((*MockOps)(nil).RunListOfCommands), cmds) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunListOfCommands", reflect.TypeOf((*MockOps)(nil).RunListOfCommands), ctx, cmds) } // RunRecert mocks base method. -func (m *MockOps) RunRecert(recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error { +func (m *MockOps) RunRecert(ctx context.Context, recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error { m.ctrl.T.Helper() - varargs := []any{recertContainerImage, authFile, recertConfigFile} + varargs := []any{ctx, recertContainerImage, authFile, recertConfigFile} for _, a := range additionalPodmanParams { varargs = append(varargs, a) } @@ -456,16 +457,16 @@ func (m *MockOps) RunRecert(recertContainerImage, authFile, recertConfigFile str } // RunRecert indicates an expected call of RunRecert. -func (mr *MockOpsMockRecorder) RunRecert(recertContainerImage, authFile, recertConfigFile any, additionalPodmanParams ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunRecert(ctx, recertContainerImage, authFile, recertConfigFile any, additionalPodmanParams ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{recertContainerImage, authFile, recertConfigFile}, additionalPodmanParams...) + varargs := append([]any{ctx, recertContainerImage, authFile, recertConfigFile}, additionalPodmanParams...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunRecert", reflect.TypeOf((*MockOps)(nil).RunRecert), varargs...) } // RunSystemdAction mocks base method. -func (m *MockOps) RunSystemdAction(args ...string) (string, error) { +func (m *MockOps) RunSystemdAction(ctx context.Context, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{} + varargs := []any{ctx} for _, a := range args { varargs = append(varargs, a) } @@ -476,37 +477,38 @@ func (m *MockOps) RunSystemdAction(args ...string) (string, error) { } // RunSystemdAction indicates an expected call of RunSystemdAction. -func (mr *MockOpsMockRecorder) RunSystemdAction(args ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunSystemdAction(ctx any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunSystemdAction", reflect.TypeOf((*MockOps)(nil).RunSystemdAction), args...) + varargs := append([]any{ctx}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunSystemdAction", reflect.TypeOf((*MockOps)(nil).RunSystemdAction), varargs...) } // RunUnauthenticatedEtcdServer mocks base method. -func (m *MockOps) RunUnauthenticatedEtcdServer(authFile, name string) error { +func (m *MockOps) RunUnauthenticatedEtcdServer(ctx context.Context, authFile, name string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RunUnauthenticatedEtcdServer", authFile, name) + ret := m.ctrl.Call(m, "RunUnauthenticatedEtcdServer", ctx, authFile, name) ret0, _ := ret[0].(error) return ret0 } // RunUnauthenticatedEtcdServer indicates an expected call of RunUnauthenticatedEtcdServer. -func (mr *MockOpsMockRecorder) RunUnauthenticatedEtcdServer(authFile, name any) *gomock.Call { +func (mr *MockOpsMockRecorder) RunUnauthenticatedEtcdServer(ctx, authFile, name any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunUnauthenticatedEtcdServer", reflect.TypeOf((*MockOps)(nil).RunUnauthenticatedEtcdServer), authFile, name) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunUnauthenticatedEtcdServer", reflect.TypeOf((*MockOps)(nil).RunUnauthenticatedEtcdServer), ctx, authFile, name) } // SetupContainersFolderCommands mocks base method. -func (m *MockOps) SetupContainersFolderCommands() error { +func (m *MockOps) SetupContainersFolderCommands(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetupContainersFolderCommands") + ret := m.ctrl.Call(m, "SetupContainersFolderCommands", ctx) ret0, _ := ret[0].(error) return ret0 } // SetupContainersFolderCommands indicates an expected call of SetupContainersFolderCommands. -func (mr *MockOpsMockRecorder) SetupContainersFolderCommands() *gomock.Call { +func (mr *MockOpsMockRecorder) SetupContainersFolderCommands(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupContainersFolderCommands", reflect.TypeOf((*MockOps)(nil).SetupContainersFolderCommands)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupContainersFolderCommands", reflect.TypeOf((*MockOps)(nil).SetupContainersFolderCommands), ctx) } // StatFile mocks base method. @@ -525,37 +527,37 @@ func (mr *MockOpsMockRecorder) StatFile(name any) *gomock.Call { } // StopClusterServices mocks base method. -func (m *MockOps) StopClusterServices() error { +func (m *MockOps) StopClusterServices(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StopClusterServices") + ret := m.ctrl.Call(m, "StopClusterServices", ctx) ret0, _ := ret[0].(error) return ret0 } // StopClusterServices indicates an expected call of StopClusterServices. -func (mr *MockOpsMockRecorder) StopClusterServices() *gomock.Call { +func (mr *MockOpsMockRecorder) StopClusterServices(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopClusterServices", reflect.TypeOf((*MockOps)(nil).StopClusterServices)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopClusterServices", reflect.TypeOf((*MockOps)(nil).StopClusterServices), ctx) } // StopEtcdServer mocks base method. -func (m *MockOps) StopEtcdServer(authfile, name string) error { +func (m *MockOps) StopEtcdServer(ctx context.Context, authfile, name string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StopEtcdServer", authfile, name) + ret := m.ctrl.Call(m, "StopEtcdServer", ctx, authfile, name) ret0, _ := ret[0].(error) return ret0 } // StopEtcdServer indicates an expected call of StopEtcdServer. -func (mr *MockOpsMockRecorder) StopEtcdServer(authfile, name any) *gomock.Call { +func (mr *MockOpsMockRecorder) StopEtcdServer(ctx, authfile, name any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopEtcdServer", reflect.TypeOf((*MockOps)(nil).StopEtcdServer), authfile, name) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopEtcdServer", reflect.TypeOf((*MockOps)(nil).StopEtcdServer), ctx, authfile, name) } // SystemctlAction mocks base method. -func (m *MockOps) SystemctlAction(action string, args ...string) (string, error) { +func (m *MockOps) SystemctlAction(ctx context.Context, action string, args ...string) (string, error) { m.ctrl.T.Helper() - varargs := []any{action} + varargs := []any{ctx, action} for _, a := range args { varargs = append(varargs, a) } @@ -566,38 +568,38 @@ func (m *MockOps) SystemctlAction(action string, args ...string) (string, error) } // SystemctlAction indicates an expected call of SystemctlAction. -func (mr *MockOpsMockRecorder) SystemctlAction(action any, args ...any) *gomock.Call { +func (mr *MockOpsMockRecorder) SystemctlAction(ctx, action any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{action}, args...) + varargs := append([]any{ctx, action}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SystemctlAction", reflect.TypeOf((*MockOps)(nil).SystemctlAction), varargs...) } // Umount mocks base method. -func (m *MockOps) Umount(deviceName string) error { +func (m *MockOps) Umount(ctx context.Context, deviceName string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Umount", deviceName) + ret := m.ctrl.Call(m, "Umount", ctx, deviceName) ret0, _ := ret[0].(error) return ret0 } // Umount indicates an expected call of Umount. -func (mr *MockOpsMockRecorder) Umount(deviceName any) *gomock.Call { +func (mr *MockOpsMockRecorder) Umount(ctx, deviceName any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Umount", reflect.TypeOf((*MockOps)(nil).Umount), deviceName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Umount", reflect.TypeOf((*MockOps)(nil).Umount), ctx, deviceName) } // UnmountAndRemoveImage mocks base method. -func (m *MockOps) UnmountAndRemoveImage(img string) error { +func (m *MockOps) UnmountAndRemoveImage(ctx context.Context, img string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UnmountAndRemoveImage", img) + ret := m.ctrl.Call(m, "UnmountAndRemoveImage", ctx, img) ret0, _ := ret[0].(error) return ret0 } // UnmountAndRemoveImage indicates an expected call of UnmountAndRemoveImage. -func (mr *MockOpsMockRecorder) UnmountAndRemoveImage(img any) *gomock.Call { +func (mr *MockOpsMockRecorder) UnmountAndRemoveImage(ctx, img any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmountAndRemoveImage", reflect.TypeOf((*MockOps)(nil).UnmountAndRemoveImage), img) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmountAndRemoveImage", reflect.TypeOf((*MockOps)(nil).UnmountAndRemoveImage), ctx, img) } // WriteFile mocks base method. @@ -615,15 +617,15 @@ func (mr *MockOpsMockRecorder) WriteFile(filename, data, perm any) *gomock.Call } // waitForEtcd mocks base method. -func (m *MockOps) waitForEtcd(healthzEndpoint string) error { +func (m *MockOps) waitForEtcd(ctx context.Context, healthzEndpoint string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "waitForEtcd", healthzEndpoint) + ret := m.ctrl.Call(m, "waitForEtcd", ctx, healthzEndpoint) ret0, _ := ret[0].(error) return ret0 } // waitForEtcd indicates an expected call of waitForEtcd. -func (mr *MockOpsMockRecorder) waitForEtcd(healthzEndpoint any) *gomock.Call { +func (mr *MockOpsMockRecorder) waitForEtcd(ctx, healthzEndpoint any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "waitForEtcd", reflect.TypeOf((*MockOps)(nil).waitForEtcd), healthzEndpoint) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "waitForEtcd", reflect.TypeOf((*MockOps)(nil).waitForEtcd), ctx, healthzEndpoint) } diff --git a/lca-cli/ops/ops.go b/lca-cli/ops/ops.go index 5894549e05..15b6ba20b3 100644 --- a/lca-cli/ops/ops.go +++ b/lca-cli/ops/ops.go @@ -54,11 +54,11 @@ var podmanRecertArgs = []string{ // //go:generate mockgen -source=ops.go -package=ops -destination=mock_ops.go type Ops interface { - SystemctlAction(action string, args ...string) (string, error) - RunSystemdAction(args ...string) (string, error) - RunInHostNamespace(command string, args ...string) (string, error) - RunBashInHostNamespace(command string, args ...string) (string, error) - RunListOfCommands(cmds []*CMD) error + SystemctlAction(ctx context.Context, action string, args ...string) (string, error) + RunSystemdAction(ctx context.Context, args ...string) (string, error) + RunInHostNamespace(ctx context.Context, command string, args ...string) (string, error) + RunBashInHostNamespace(ctx context.Context, command string, args ...string) (string, error) + RunListOfCommands(ctx context.Context, cmds []*CMD) error ReadFile(filename string) ([]byte, error) WriteFile(filename string, data []byte, perm os.FileMode) error CopyFile(src, dest string, perm os.FileMode) error @@ -68,32 +68,32 @@ type Ops interface { ReadDir(path string) ([]os.DirEntry, error) StatFile(name string) (os.FileInfo, error) IsNotExist(err error) bool - ForceExpireSeedCrypto(recertContainerImage, authFile string, hasKubeAdminPassword bool) error - RestoreOriginalSeedCrypto(recertContainerImage, authFile string) error - RunUnauthenticatedEtcdServer(authFile, name string) error - StopEtcdServer(authfile, name string) error - waitForEtcd(healthzEndpoint string) error - RunRecert(recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error - ExtractTarWithSELinux(srcPath, destPath string) error - RemountSysroot() error - RemountBoot() error - ImageExists(img string) (bool, error) - IsImageMounted(img string) (bool, error) - UnmountAndRemoveImage(img string) error - MountImage(img string) (string, error) - RecertFullFlow(recertContainerImage, authFile, configFile string, + ForceExpireSeedCrypto(ctx context.Context, recertContainerImage, authFile string, hasKubeAdminPassword bool) error + RestoreOriginalSeedCrypto(ctx context.Context, recertContainerImage, authFile string) error + RunUnauthenticatedEtcdServer(ctx context.Context, authFile, name string) error + StopEtcdServer(ctx context.Context, authfile, name string) error + waitForEtcd(ctx context.Context, healthzEndpoint string) error + RunRecert(ctx context.Context, recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error + ExtractTarWithSELinux(ctx context.Context, srcPath, destPath string) error + RemountSysroot(ctx context.Context) error + RemountBoot(ctx context.Context) error + ImageExists(ctx context.Context, img string) (bool, error) + IsImageMounted(ctx context.Context, img string) (bool, error) + UnmountAndRemoveImage(ctx context.Context, img string) error + MountImage(ctx context.Context, img string) (string, error) + RecertFullFlow(ctx context.Context, recertContainerImage, authFile, configFile string, preRecertOperations func() error, postRecertOperations func() error, additionalPodmanParams ...string) error - ListBlockDevices() ([]BlockDevice, error) - Mount(deviceName, mountFolder string) error - Umount(deviceName string) error + ListBlockDevices(ctx context.Context) ([]BlockDevice, error) + Mount(ctx context.Context, deviceName, mountFolder string) error + Umount(ctx context.Context, deviceName string) error Chroot(chrootPath string) (func() error, error) - CreateExtraPartition(installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error - SetupContainersFolderCommands() error + CreateExtraPartition(ctx context.Context, installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error + SetupContainersFolderCommands(ctx context.Context) error GetHostname() (string, error) CreateIsoWithEmbeddedIgnition(log logrus.FieldLogger, ignitionBytes []byte, baseIsoPath, outputIsoPath string) error - GetContainerStorageTarget() (string, error) - StopClusterServices() error - EnableClusterServices() error + GetContainerStorageTarget(ctx context.Context) (string, error) + StopClusterServices(ctx context.Context) error + EnableClusterServices(ctx context.Context) error } type CMD struct { @@ -120,41 +120,41 @@ func NewOps(log *logrus.Logger, hostCommandsExecutor Execute) Ops { return &ops{hostCommandsExecutor: hostCommandsExecutor, log: log} } -func (o *ops) SystemctlAction(action string, args ...string) (string, error) { +func (o *ops) SystemctlAction(ctx context.Context, action string, args ...string) (string, error) { o.log.Infof("Running systemctl %s %s", action, args) - output, err := o.hostCommandsExecutor.Execute("systemctl", append([]string{action}, args...)...) + output, err := o.hostCommandsExecutor.Execute(ctx, "systemctl", append([]string{action}, args...)...) if err != nil { err = fmt.Errorf("failed executing systemctl %s %s: %w", action, args, err) } return output, err } -func (o *ops) RunSystemdAction(args ...string) (string, error) { +func (o *ops) RunSystemdAction(ctx context.Context, args ...string) (string, error) { o.log.Infof("Running systemd-run %s", args) - output, err := o.hostCommandsExecutor.Execute("systemd-run", args...) + output, err := o.hostCommandsExecutor.Execute(ctx, "systemd-run", args...) if err != nil { err = fmt.Errorf("failed executing systemd-run with args %s: %w", args, err) } return output, err } -func (o *ops) RunBashInHostNamespace(command string, args ...string) (string, error) { +func (o *ops) RunBashInHostNamespace(ctx context.Context, command string, args ...string) (string, error) { args = append([]string{command}, args...) - execute, err := o.hostCommandsExecutor.Execute("bash", "-c", strings.Join(args, " ")) + execute, err := o.hostCommandsExecutor.Execute(ctx, "bash", "-c", strings.Join(args, " ")) if err != nil { return "", fmt.Errorf("failed to run bash in host namespace with args %s: %w", args, err) } return execute, nil } -func (o *ops) RunInHostNamespace(command string, args ...string) (string, error) { - execute, err := o.hostCommandsExecutor.Execute(command, args...) +func (o *ops) RunInHostNamespace(ctx context.Context, command string, args ...string) (string, error) { + execute, err := o.hostCommandsExecutor.Execute(ctx, command, args...) if err != nil { return "", fmt.Errorf("failed to run %q in host namespace with args %s: %w", command, args, err) } return execute, nil } -func (o *ops) ForceExpireSeedCrypto(recertContainerImage, authFile string, hasKubeAdminPassword bool) error { +func (o *ops) ForceExpireSeedCrypto(ctx context.Context, recertContainerImage, authFile string, hasKubeAdminPassword bool) error { o.log.Info("Running recert --force-expire tool and saving a summary without sensitive data") // Run recert tool to force expiration of seed cluster certificates, and save a summary without sensitive data. // This pre-check is also useful for validating that a cluster can be re-certified error-free before turning it @@ -165,7 +165,7 @@ func (o *ops) ForceExpireSeedCrypto(recertContainerImage, authFile string, hasKu return fmt.Errorf("failed to create %s file: %w", recertConfigFile, err) } - if err := o.RecertFullFlow(recertContainerImage, authFile, recertConfigFile, nil, nil); err != nil { + if err := o.RecertFullFlow(ctx, recertContainerImage, authFile, recertConfigFile, nil, nil); err != nil { return err } @@ -173,7 +173,7 @@ func (o *ops) ForceExpireSeedCrypto(recertContainerImage, authFile string, hasKu return nil } -func (o *ops) RestoreOriginalSeedCrypto(recertContainerImage, authFile string) error { +func (o *ops) RestoreOriginalSeedCrypto(ctx context.Context, recertContainerImage, authFile string) error { o.log.Info("Running recert --extend-expiration tool to restore original seed crypto") o.log.Info("Run recert --extend-expiration tool") recertConfigFile := path.Join(common.BackupCertsDir, recert.RecertConfigFile) @@ -195,7 +195,7 @@ func (o *ops) RestoreOriginalSeedCrypto(recertContainerImage, authFile string) e return nil } - if err := o.RecertFullFlow(recertContainerImage, authFile, recertConfigFile, nil, postRecertOp); err != nil { + if err := o.RecertFullFlow(ctx, recertContainerImage, authFile, recertConfigFile, nil, postRecertOp); err != nil { return err } @@ -206,7 +206,7 @@ func (o *ops) RestoreOriginalSeedCrypto(recertContainerImage, authFile string) e // RunUnauthenticatedEtcdServer Run unauthenticated etcd server for the recert tool. // This runs a small (fake) unauthenticated etcd server backed by the actual etcd database, // which is required before running the recert tool. -func (o *ops) RunUnauthenticatedEtcdServer(authFile, name string) error { +func (o *ops) RunUnauthenticatedEtcdServer(ctx context.Context, authFile, name string) error { // Get etcdImage available for the current release, this is needed by recert to // run an unauthenticated etcd server for running successfully. o.log.Infof("Getting image from %s static pod file", common.EtcdStaticPodFile) @@ -228,12 +228,12 @@ func (o *ops) RunUnauthenticatedEtcdServer(authFile, name string) error { "--name", "editor", "--data-dir", "/store") // Run the command and return an error if it occurs - if _, err := o.RunInHostNamespace(command, args...); err != nil { + if _, err := o.RunInHostNamespace(ctx, command, args...); err != nil { return err } o.log.Info("Waiting for unauthenticated etcd start serving for recert tool") - if err := o.waitForEtcd("http://" + common.EtcdDefaultEndpoint + "/health"); err != nil { + if err := o.waitForEtcd(ctx, "http://"+common.EtcdDefaultEndpoint+"/health"); err != nil { return fmt.Errorf("failed to wait for unauthenticated etcd server: %w", err) } o.log.Info("Unauthenticated etcd server for recert is up and running") @@ -241,15 +241,15 @@ func (o *ops) RunUnauthenticatedEtcdServer(authFile, name string) error { return nil } -func (o *ops) StopEtcdServer(authfile, name string) error { +func (o *ops) StopEtcdServer(ctx context.Context, authfile, name string) error { o.log.Info("Stopping the unauthenticated etcd server") - if _, err := o.RunInHostNamespace(podman, "stop", common.EtcdContainerName); err != nil { + if _, err := o.RunInHostNamespace(ctx, podman, "stop", common.EtcdContainerName); err != nil { o.log.WithError(err).Errorf("failed to stop %s container.", common.EtcdContainerName) } return nil } -func (o *ops) waitForEtcd(healthzEndpoint string) error { +func (o *ops) waitForEtcd(ctx context.Context, healthzEndpoint string) error { timeout := time.After(1 * time.Minute) ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() @@ -276,7 +276,7 @@ func (o *ops) waitForEtcd(healthzEndpoint string) error { } } -func (o *ops) RunRecert(recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error { +func (o *ops) RunRecert(ctx context.Context, recertContainerImage, authFile, recertConfigFile string, additionalPodmanParams ...string) error { o.log.Info("Start running recert") command := podman @@ -297,7 +297,7 @@ func (o *ops) RunRecert(recertContainerImage, authFile, recertConfigFile string, args = append(args, additionalPodmanParams...) args = append(args, recertContainerImage) - if _, err := o.hostCommandsExecutor.Execute(command, args...); err != nil { + if _, err := o.hostCommandsExecutor.Execute(ctx, command, args...); err != nil { return fmt.Errorf("failed to run recert tool container: %w", err) } @@ -306,7 +306,7 @@ func (o *ops) RunRecert(recertContainerImage, authFile, recertConfigFile string, // prepareSELinuxTar prepares a copy of tar executable with install_exec_t context. // This type allows SELinux labeling of non-existing labels -func (o *ops) prepareSELinuxTar() (string, error) { +func (o *ops) prepareSELinuxTar(ctx context.Context) (string, error) { tarPath := common.PathOutsideChroot("/usr/bin/tar") destDir := common.PathOutsideChroot("/var/tmp") newTarPath, err := utils.CopyToTempFile(tarPath, destDir, "tar-") @@ -320,15 +320,15 @@ func (o *ops) prepareSELinuxTar() (string, error) { } // Set SELinux attribute - if _, err := o.hostCommandsExecutor.Execute("chcon", "-t", "install_exec_t", newTarPathInsideChroot); err != nil { + if _, err := o.hostCommandsExecutor.Execute(ctx, "chcon", "-t", "install_exec_t", newTarPathInsideChroot); err != nil { return "", fmt.Errorf("failed to set SELinux context install_exec_t to %s: %w", newTarPath, err) } return newTarPath, nil } -func (o *ops) ExtractTarWithSELinux(srcPath, destPath string) error { +func (o *ops) ExtractTarWithSELinux(ctx context.Context, srcPath, destPath string) error { // Create a copy of /usr/bin/tar with extended permissions - tarExec, err := o.prepareSELinuxTar() + tarExec, err := o.prepareSELinuxTar(ctx) if err != nil { return fmt.Errorf("failed to create copy of tar with install_exec_t attribute: %w", err) } @@ -343,28 +343,28 @@ func (o *ops) ExtractTarWithSELinux(srcPath, destPath string) error { tarArgs := []string{"xzf", srcPath, "-C", destPath} tarArgs = append(tarArgs, common.TarOpts...) - if _, err = o.hostCommandsExecutor.Execute(tarExecInsideChroot, tarArgs...); err != nil { + if _, err = o.hostCommandsExecutor.Execute(ctx, tarExecInsideChroot, tarArgs...); err != nil { return fmt.Errorf("failed to extract tar with SELinux with sourcePath %s and destPath %s: %w", srcPath, destPath, err) } return nil } -func (o *ops) RemountSysroot() error { - if _, err := o.hostCommandsExecutor.Execute("mount", "/sysroot", "-o", "remount,rw"); err != nil { +func (o *ops) RemountSysroot(ctx context.Context) error { + if _, err := o.hostCommandsExecutor.Execute(ctx, "mount", "/sysroot", "-o", "remount,rw"); err != nil { return fmt.Errorf("failed to remount sysroot: %w", err) } return nil } -func (o *ops) RemountBoot() error { - if _, err := o.hostCommandsExecutor.Execute("mount", "/boot", "-o", "remount,rw"); err != nil { +func (o *ops) RemountBoot(ctx context.Context) error { + if _, err := o.hostCommandsExecutor.Execute(ctx, "mount", "/boot", "-o", "remount,rw"); err != nil { return fmt.Errorf("failed to remount boot: %w", err) } return nil } -func (o *ops) ImageExists(img string) (bool, error) { - _, err := o.hostCommandsExecutor.Execute(podman, "image", "exists", img) +func (o *ops) ImageExists(ctx context.Context, img string) (bool, error) { + _, err := o.hostCommandsExecutor.Execute(ctx, podman, "image", "exists", img) if err != nil { var exitError *exec.ExitError if errors.As(err, &exitError) { @@ -385,8 +385,8 @@ type PodmanImage struct { // IsImageMounted checkes whether certain image is mounted // pass in the full address with tag e.g: quay.io/openshift/lifecycle-agent-operator:latest -func (o *ops) IsImageMounted(imgName string) (bool, error) { - output, err := o.hostCommandsExecutor.Execute(podman, "image", "mount", "--format", "json", "--log-level", "error") +func (o *ops) IsImageMounted(ctx context.Context, imgName string) (bool, error) { + output, err := o.hostCommandsExecutor.Execute(ctx, podman, "image", "mount", "--format", "json", "--log-level", "error") if err != nil { return false, fmt.Errorf("failed to mount podamn image: %w", err) } @@ -404,8 +404,8 @@ func (o *ops) IsImageMounted(imgName string) (bool, error) { return false, nil } -func (o *ops) removeImage(img string) error { - exist, err := o.ImageExists(img) +func (o *ops) removeImage(ctx context.Context, img string) error { + exist, err := o.ImageExists(ctx, img) if err != nil { return fmt.Errorf("failed to check if image exist: %w", err) } @@ -413,28 +413,28 @@ func (o *ops) removeImage(img string) error { return nil } if _, err := o.hostCommandsExecutor.Execute( - podman, "rmi", img, + ctx, podman, "rmi", img, ); err != nil { return fmt.Errorf("failed to remove image: %w", err) } return nil } -func (o *ops) UnmountAndRemoveImage(img string) error { - if mounted, err := o.IsImageMounted(img); err != nil { +func (o *ops) UnmountAndRemoveImage(ctx context.Context, img string) error { + if mounted, err := o.IsImageMounted(ctx, img); err != nil { return fmt.Errorf("failed to check if image is mounted: %w", err) } else if mounted { if _, err := o.hostCommandsExecutor.Execute( - podman, "image", "umount", img, + ctx, podman, "image", "umount", img, ); err != nil { return fmt.Errorf("failed to unmount image: %w", err) } } - return o.removeImage(img) + return o.removeImage(ctx, img) } -func (o *ops) MountImage(img string) (string, error) { +func (o *ops) MountImage(ctx context.Context, img string) (string, error) { command := podman args := []string{"image", "mount", img} @@ -447,20 +447,20 @@ func (o *ops) MountImage(img string) (string, error) { command = nsenter } - mountpoint, err := o.hostCommandsExecutor.Execute(command, args...) + mountpoint, err := o.hostCommandsExecutor.Execute(ctx, command, args...) if err != nil { return mountpoint, fmt.Errorf("failed to mount seed image: %w", err) } return mountpoint, nil } -func (o *ops) RecertFullFlow(recertContainerImage, authFile, configFile string, +func (o *ops) RecertFullFlow(ctx context.Context, recertContainerImage, authFile, configFile string, preRecertOperations func() error, postRecertOperations func() error, additionalPodmanParams ...string) error { - if err := o.RunUnauthenticatedEtcdServer(authFile, common.EtcdContainerName); err != nil { + if err := o.RunUnauthenticatedEtcdServer(ctx, authFile, common.EtcdContainerName); err != nil { return fmt.Errorf("failed to run etcd, err: %w", err) } - defer func() { _ = o.StopEtcdServer(authFile, common.EtcdContainerName) }() + defer func() { _ = o.StopEtcdServer(context.WithoutCancel(ctx), authFile, common.EtcdContainerName) }() if preRecertOperations != nil { if err := preRecertOperations(); err != nil { @@ -468,7 +468,7 @@ func (o *ops) RecertFullFlow(recertContainerImage, authFile, configFile string, } } - if err := o.RunRecert(recertContainerImage, authFile, configFile, + if err := o.RunRecert(ctx, recertContainerImage, authFile, configFile, additionalPodmanParams...); err != nil { return err } @@ -484,9 +484,9 @@ func (o *ops) RecertFullFlow(recertContainerImage, authFile, configFile string, // ListBlockDevices runs lsblk command and not using go library cause // each library that i was looking into doesn't show label for block device and shows labels only for partitions -func (o *ops) ListBlockDevices() ([]BlockDevice, error) { +func (o *ops) ListBlockDevices(ctx context.Context) ([]BlockDevice, error) { o.log.Info("Listing block devices") - lsblkOutput, err := o.RunInHostNamespace("lsblk", "-f", + lsblkOutput, err := o.RunInHostNamespace(ctx, "lsblk", "-f", "--json", "--output", "NAME,LABEL") if err != nil { return nil, fmt.Errorf("failed to run lsblk, err: %w", err) @@ -504,20 +504,20 @@ func (o *ops) ListBlockDevices() ([]BlockDevice, error) { return blockDeviceList, nil } -func (o *ops) Mount(deviceName, mountFolder string) error { +func (o *ops) Mount(ctx context.Context, deviceName, mountFolder string) error { o.log.Infof("Mounting %s into %s", deviceName, mountFolder) if err := os.MkdirAll(mountFolder, 0o700); err != nil { return fmt.Errorf("failed to create %s, err: %w", mountFolder, err) } - if _, err := o.RunInHostNamespace("mount", fmt.Sprintf("/dev/%s", deviceName), mountFolder); err != nil { + if _, err := o.RunInHostNamespace(ctx, "mount", fmt.Sprintf("/dev/%s", deviceName), mountFolder); err != nil { return fmt.Errorf("failed to mount %s into %s, err: %w", deviceName, mountFolder, err) } return nil } -func (o *ops) Umount(deviceName string) error { +func (o *ops) Umount(ctx context.Context, deviceName string) error { o.log.Infof("Unmounting %s", deviceName) - if _, err := o.RunInHostNamespace("umount", fmt.Sprintf("/dev/%s", deviceName)); err != nil { + if _, err := o.RunInHostNamespace(ctx, "umount", fmt.Sprintf("/dev/%s", deviceName)); err != nil { return fmt.Errorf("failed to unmount %s, err: %w", deviceName, err) } return nil @@ -546,9 +546,9 @@ func (o *ops) Chroot(chrootPath string) (func() error, error) { }, nil } -func (o *ops) RunListOfCommands(cmds []*CMD) error { +func (o *ops) RunListOfCommands(ctx context.Context, cmds []*CMD) error { for _, c := range cmds { - if _, err := o.hostCommandsExecutor.Execute(c.command, c.args...); err != nil { + if _, err := o.hostCommandsExecutor.Execute(ctx, c.command, c.args...); err != nil { return fmt.Errorf("failed to run %s with args %s: %w", c.command, c.args, err) } } @@ -599,20 +599,20 @@ func (o *ops) IsNotExist(err error) bool { return os.IsNotExist(err) } -func (o *ops) CreateExtraPartition(installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error { +func (o *ops) CreateExtraPartition(ctx context.Context, installationDisk, extraPartitionLabel, extraPartitionStart string, extraPartitionNumber uint) error { o.log.Info("Creating extra partition") - if _, err := o.RunBashInHostNamespace( + if _, err := o.RunBashInHostNamespace(ctx, "echo", "write", "|", "sfdisk", installationDisk); err != nil { return fmt.Errorf("failed to create extra partition: %w", err) } - if _, err := o.RunInHostNamespace("sgdisk", "--new", + if _, err := o.RunInHostNamespace(ctx, "sgdisk", "--new", fmt.Sprintf("%d:%s", extraPartitionNumber, extraPartitionStart), "--change-name", fmt.Sprintf("%d:%s", extraPartitionNumber, extraPartitionLabel), installationDisk); err != nil { return fmt.Errorf("failed to create extra partition: %w", err) } - extraPartitionPath, err := o.RunBashInHostNamespace("lsblk", installationDisk, "--json", "-O", "|", "jq", + extraPartitionPath, err := o.RunBashInHostNamespace(ctx, "lsblk", installationDisk, "--json", "-O", "|", "jq", fmt.Sprintf(".blockdevices[0].children[%d].path", extraPartitionNumber-1), "-r") if err != nil { return fmt.Errorf("failed to get extra partition path: %w", err) @@ -624,14 +624,14 @@ func (o *ops) CreateExtraPartition(installationDisk, extraPartitionLabel, extraP cmds = append(cmds, NewCMD("mount", fmt.Sprintf("/dev/disk/by-partlabel/%s", extraPartitionLabel), common.ContainerStoragePath), NewCMD("restorecon", "-R", common.ContainerStoragePath)) - if err := o.RunListOfCommands(cmds); err != nil { + if err := o.RunListOfCommands(ctx, cmds); err != nil { return fmt.Errorf("failed to grow root partition: %w", err) } return nil } -func (o *ops) SetupContainersFolderCommands() error { +func (o *ops) SetupContainersFolderCommands(ctx context.Context) error { o.log.Info("Setting up containers folder") var cmds []*CMD cmds = append(cmds, NewCMD("chattr", "-i", "/mnt/"), @@ -639,7 +639,7 @@ func (o *ops) SetupContainersFolderCommands() error { NewCMD("chattr", "+i", "/mnt/"), NewCMD("mount", "-o", "bind", "/mnt/containers", common.ContainerStoragePath), NewCMD("restorecon", "-R", common.ContainerStoragePath)) - if err := o.RunListOfCommands(cmds); err != nil { + if err := o.RunListOfCommands(ctx, cmds); err != nil { return fmt.Errorf("failed to setup containers folder: %w", err) } return nil @@ -686,14 +686,14 @@ func (o *ops) CreateIsoWithEmbeddedIgnition(log logrus.FieldLogger, ignitionByte return nil } -func (o *ops) GetContainerStorageTarget() (string, error) { +func (o *ops) GetContainerStorageTarget(ctx context.Context) (string, error) { const containerStorageMountUnit = "var-lib-containers.mount" - if _, err := o.SystemctlAction("is-active", containerStorageMountUnit); err != nil { + if _, err := o.SystemctlAction(ctx, "is-active", containerStorageMountUnit); err != nil { // No active mount, return nil return "", nil } - output, err := o.SystemctlAction("cat", containerStorageMountUnit) + output, err := o.SystemctlAction(ctx, "cat", containerStorageMountUnit) if err != nil { return "", fmt.Errorf("unable to systemctl cat %s: %w", containerStorageMountUnit, err) } @@ -711,21 +711,21 @@ func (o *ops) GetContainerStorageTarget() (string, error) { } // StopClusterServices stops kubelet and crio services with proper container cleanup -func (o *ops) StopClusterServices() error { +func (o *ops) StopClusterServices(ctx context.Context) error { o.log.Info("Stop kubelet service") - _, err := o.SystemctlAction("stop", "kubelet.service") + _, err := o.SystemctlAction(ctx, "stop", "kubelet.service") if err != nil { return fmt.Errorf("failed to stop kubelet: %w", err) } o.log.Info("Disabling kubelet service") - _, err = o.SystemctlAction("disable", "kubelet.service") + _, err = o.SystemctlAction(ctx, "disable", "kubelet.service") if err != nil { return fmt.Errorf("failed to disable kubelet: %w", err) } o.log.Info("Stopping containers and CRI-O runtime.") - crioSystemdStatus, err := o.SystemctlAction("is-active", "crio") + crioSystemdStatus, err := o.SystemctlAction(ctx, "is-active", "crio") var exitErr *exec.ExitError // If ExitCode is 3, the command succeeded and told us that crio is down if err != nil && errors.As(err, &exitErr) && exitErr.ExitCode() != 3 { @@ -734,10 +734,10 @@ func (o *ops) StopClusterServices() error { o.log.Info("crio status is ", crioSystemdStatus) if crioSystemdStatus == "active" { // CRI-O is active, so stop running containers with retry - _ = wait.PollUntilContextCancel(context.TODO(), time.Second, true, func(ctx context.Context) (done bool, err error) { + _ = wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { o.log.Info("Stop running containers") args := []string{"ps", "-q", "|", "xargs", "--no-run-if-empty", "--max-args", "1", "--max-procs", "10", "crictl", "stop", "--timeout", "5"} - _, err = o.RunBashInHostNamespace("crictl", args...) + _, err = o.RunBashInHostNamespace(ctx, "crictl", args...) if err != nil { return false, fmt.Errorf("failed to stop running containers: %w", err) } @@ -746,7 +746,7 @@ func (o *ops) StopClusterServices() error { // Execute a D-Bus call to stop the CRI-O runtime o.log.Debug("Stopping CRI-O engine") - _, err = o.SystemctlAction("stop", "crio.service") + _, err = o.SystemctlAction(ctx, "stop", "crio.service") if err != nil { return fmt.Errorf("failed to stop crio engine: %w", err) } @@ -758,8 +758,8 @@ func (o *ops) StopClusterServices() error { return nil } -func (o *ops) EnableClusterServices() error { - _, err := o.SystemctlAction("enable", "kubelet.service") +func (o *ops) EnableClusterServices(ctx context.Context) error { + _, err := o.SystemctlAction(ctx, "enable", "kubelet.service") if err != nil { return fmt.Errorf("failed to enable kubelet: %w", err) } diff --git a/lca-cli/ostreeclient/mock_rpmostreeclient.go b/lca-cli/ostreeclient/mock_rpmostreeclient.go index d4e34209cc..0f9ddcd94e 100644 --- a/lca-cli/ostreeclient/mock_rpmostreeclient.go +++ b/lca-cli/ostreeclient/mock_rpmostreeclient.go @@ -9,6 +9,7 @@ package rpmostreeclient import ( + context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -38,143 +39,143 @@ func (m *MockIClient) EXPECT() *MockIClientMockRecorder { } // GetCurrentStaterootName mocks base method. -func (m *MockIClient) GetCurrentStaterootName() (string, error) { +func (m *MockIClient) GetCurrentStaterootName(ctx context.Context) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentStaterootName") + ret := m.ctrl.Call(m, "GetCurrentStaterootName", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentStaterootName indicates an expected call of GetCurrentStaterootName. -func (mr *MockIClientMockRecorder) GetCurrentStaterootName() *gomock.Call { +func (mr *MockIClientMockRecorder) GetCurrentStaterootName(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentStaterootName", reflect.TypeOf((*MockIClient)(nil).GetCurrentStaterootName)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentStaterootName", reflect.TypeOf((*MockIClient)(nil).GetCurrentStaterootName), ctx) } // GetDeploymentID mocks base method. -func (m *MockIClient) GetDeploymentID(osname string) (string, error) { +func (m *MockIClient) GetDeploymentID(ctx context.Context, osname string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeploymentID", osname) + ret := m.ctrl.Call(m, "GetDeploymentID", ctx, osname) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeploymentID indicates an expected call of GetDeploymentID. -func (mr *MockIClientMockRecorder) GetDeploymentID(osname any) *gomock.Call { +func (mr *MockIClientMockRecorder) GetDeploymentID(ctx, osname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentID", reflect.TypeOf((*MockIClient)(nil).GetDeploymentID), osname) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentID", reflect.TypeOf((*MockIClient)(nil).GetDeploymentID), ctx, osname) } // GetDeploymentIndex mocks base method. -func (m *MockIClient) GetDeploymentIndex(osname string) (int, error) { +func (m *MockIClient) GetDeploymentIndex(ctx context.Context, osname string) (int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeploymentIndex", osname) + ret := m.ctrl.Call(m, "GetDeploymentIndex", ctx, osname) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeploymentIndex indicates an expected call of GetDeploymentIndex. -func (mr *MockIClientMockRecorder) GetDeploymentIndex(osname any) *gomock.Call { +func (mr *MockIClientMockRecorder) GetDeploymentIndex(ctx, osname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentIndex", reflect.TypeOf((*MockIClient)(nil).GetDeploymentIndex), osname) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeploymentIndex", reflect.TypeOf((*MockIClient)(nil).GetDeploymentIndex), ctx, osname) } // GetUnbootedDeploymentIndex mocks base method. -func (m *MockIClient) GetUnbootedDeploymentIndex() (int, error) { +func (m *MockIClient) GetUnbootedDeploymentIndex(ctx context.Context) (int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUnbootedDeploymentIndex") + ret := m.ctrl.Call(m, "GetUnbootedDeploymentIndex", ctx) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUnbootedDeploymentIndex indicates an expected call of GetUnbootedDeploymentIndex. -func (mr *MockIClientMockRecorder) GetUnbootedDeploymentIndex() *gomock.Call { +func (mr *MockIClientMockRecorder) GetUnbootedDeploymentIndex(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbootedDeploymentIndex", reflect.TypeOf((*MockIClient)(nil).GetUnbootedDeploymentIndex)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbootedDeploymentIndex", reflect.TypeOf((*MockIClient)(nil).GetUnbootedDeploymentIndex), ctx) } // GetUnbootedStaterootName mocks base method. -func (m *MockIClient) GetUnbootedStaterootName() (string, error) { +func (m *MockIClient) GetUnbootedStaterootName(ctx context.Context) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUnbootedStaterootName") + ret := m.ctrl.Call(m, "GetUnbootedStaterootName", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUnbootedStaterootName indicates an expected call of GetUnbootedStaterootName. -func (mr *MockIClientMockRecorder) GetUnbootedStaterootName() *gomock.Call { +func (mr *MockIClientMockRecorder) GetUnbootedStaterootName(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbootedStaterootName", reflect.TypeOf((*MockIClient)(nil).GetUnbootedStaterootName)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbootedStaterootName", reflect.TypeOf((*MockIClient)(nil).GetUnbootedStaterootName), ctx) } // IsStaterootBooted mocks base method. -func (m *MockIClient) IsStaterootBooted(stateroot string) (bool, error) { +func (m *MockIClient) IsStaterootBooted(ctx context.Context, stateroot string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsStaterootBooted", stateroot) + ret := m.ctrl.Call(m, "IsStaterootBooted", ctx, stateroot) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsStaterootBooted indicates an expected call of IsStaterootBooted. -func (mr *MockIClientMockRecorder) IsStaterootBooted(stateroot any) *gomock.Call { +func (mr *MockIClientMockRecorder) IsStaterootBooted(ctx, stateroot any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStaterootBooted", reflect.TypeOf((*MockIClient)(nil).IsStaterootBooted), stateroot) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStaterootBooted", reflect.TypeOf((*MockIClient)(nil).IsStaterootBooted), ctx, stateroot) } // QueryStatus mocks base method. -func (m *MockIClient) QueryStatus() (*Status, error) { +func (m *MockIClient) QueryStatus(ctx context.Context) (*Status, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryStatus") + ret := m.ctrl.Call(m, "QueryStatus", ctx) ret0, _ := ret[0].(*Status) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryStatus indicates an expected call of QueryStatus. -func (mr *MockIClientMockRecorder) QueryStatus() *gomock.Call { +func (mr *MockIClientMockRecorder) QueryStatus(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryStatus", reflect.TypeOf((*MockIClient)(nil).QueryStatus)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryStatus", reflect.TypeOf((*MockIClient)(nil).QueryStatus), ctx) } // RpmOstreeCleanup mocks base method. -func (m *MockIClient) RpmOstreeCleanup() error { +func (m *MockIClient) RpmOstreeCleanup(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RpmOstreeCleanup") + ret := m.ctrl.Call(m, "RpmOstreeCleanup", ctx) ret0, _ := ret[0].(error) return ret0 } // RpmOstreeCleanup indicates an expected call of RpmOstreeCleanup. -func (mr *MockIClientMockRecorder) RpmOstreeCleanup() *gomock.Call { +func (mr *MockIClientMockRecorder) RpmOstreeCleanup(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RpmOstreeCleanup", reflect.TypeOf((*MockIClient)(nil).RpmOstreeCleanup)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RpmOstreeCleanup", reflect.TypeOf((*MockIClient)(nil).RpmOstreeCleanup), ctx) } // RpmOstreeVersion mocks base method. -func (m *MockIClient) RpmOstreeVersion() (*VersionData, error) { +func (m *MockIClient) RpmOstreeVersion(ctx context.Context) (*VersionData, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RpmOstreeVersion") + ret := m.ctrl.Call(m, "RpmOstreeVersion", ctx) ret0, _ := ret[0].(*VersionData) ret1, _ := ret[1].(error) return ret0, ret1 } // RpmOstreeVersion indicates an expected call of RpmOstreeVersion. -func (mr *MockIClientMockRecorder) RpmOstreeVersion() *gomock.Call { +func (mr *MockIClientMockRecorder) RpmOstreeVersion(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RpmOstreeVersion", reflect.TypeOf((*MockIClient)(nil).RpmOstreeVersion)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RpmOstreeVersion", reflect.TypeOf((*MockIClient)(nil).RpmOstreeVersion), ctx) } // newCmd mocks base method. -func (m *MockIClient) newCmd(args ...string) ([]byte, error) { +func (m *MockIClient) newCmd(ctx context.Context, args ...string) ([]byte, error) { m.ctrl.T.Helper() - varargs := []any{} + varargs := []any{ctx} for _, a := range args { varargs = append(varargs, a) } @@ -185,7 +186,8 @@ func (m *MockIClient) newCmd(args ...string) ([]byte, error) { } // newCmd indicates an expected call of newCmd. -func (mr *MockIClientMockRecorder) newCmd(args ...any) *gomock.Call { +func (mr *MockIClientMockRecorder) newCmd(ctx any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "newCmd", reflect.TypeOf((*MockIClient)(nil).newCmd), args...) + varargs := append([]any{ctx}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "newCmd", reflect.TypeOf((*MockIClient)(nil).newCmd), varargs...) } diff --git a/lca-cli/ostreeclient/rpmostreeclient.go b/lca-cli/ostreeclient/rpmostreeclient.go index 673208f711..70e04538ae 100644 --- a/lca-cli/ostreeclient/rpmostreeclient.go +++ b/lca-cli/ostreeclient/rpmostreeclient.go @@ -19,6 +19,7 @@ limitations under the License. package rpmostreeclient import ( + "context" "encoding/json" "fmt" @@ -58,16 +59,16 @@ type Deployment struct { //go:generate mockgen -source=rpmostreeclient.go -package=rpmostreeclient -destination=mock_rpmostreeclient.go type IClient interface { - newCmd(args ...string) ([]byte, error) - RpmOstreeVersion() (*VersionData, error) - QueryStatus() (*Status, error) - IsStaterootBooted(stateroot string) (bool, error) - GetCurrentStaterootName() (string, error) - GetUnbootedStaterootName() (string, error) - GetDeploymentID(osname string) (string, error) - GetDeploymentIndex(osname string) (int, error) - GetUnbootedDeploymentIndex() (int, error) - RpmOstreeCleanup() error + newCmd(ctx context.Context, args ...string) ([]byte, error) + RpmOstreeVersion(ctx context.Context) (*VersionData, error) + QueryStatus(ctx context.Context) (*Status, error) + IsStaterootBooted(ctx context.Context, stateroot string) (bool, error) + GetCurrentStaterootName(ctx context.Context) (string, error) + GetUnbootedStaterootName(ctx context.Context) (string, error) + GetDeploymentID(ctx context.Context, osname string) (string, error) + GetDeploymentIndex(ctx context.Context, osname string) (int, error) + GetUnbootedDeploymentIndex(ctx context.Context) (int, error) + RpmOstreeCleanup(ctx context.Context) error } // Client is a handle for interacting with a rpm-ostree based system. @@ -86,8 +87,8 @@ func NewClient(id string, executor ops.Execute) *Client { } } -func (c *Client) newCmd(args ...string) ([]byte, error) { - rawOutput, err := c.executor.Execute("rpm-ostree", args...) +func (c *Client) newCmd(ctx context.Context, args ...string) ([]byte, error) { + rawOutput, err := c.executor.Execute(ctx, "rpm-ostree", args...) return []byte(rawOutput), err } @@ -138,8 +139,8 @@ func getDeploymentIndex(deployments []Deployment, stateroot string) (int, error) } // RpmOstreeVersion returns the running rpm-ostree version number -func (c *Client) RpmOstreeVersion() (*VersionData, error) { - buf, err := c.newCmd("--version") +func (c *Client) RpmOstreeVersion(ctx context.Context) (*VersionData, error) { + buf, err := c.newCmd(ctx, "--version") if err != nil { return nil, err } @@ -154,9 +155,9 @@ func (c *Client) RpmOstreeVersion() (*VersionData, error) { } // QueryStatus loads the current system state. -func (c *Client) QueryStatus() (*Status, error) { +func (c *Client) QueryStatus(ctx context.Context) (*Status, error) { var q Status - buf, err := c.newCmd("status", "--json") + buf, err := c.newCmd(ctx, "status", "--json") if err != nil { return nil, err } @@ -168,8 +169,8 @@ func (c *Client) QueryStatus() (*Status, error) { return &q, nil } -func (c *Client) GetDeploymentID(stateroot string) (string, error) { - status, err := c.QueryStatus() +func (c *Client) GetDeploymentID(ctx context.Context, stateroot string) (string, error) { + status, err := c.QueryStatus(ctx) if err != nil { return "", err } @@ -182,8 +183,8 @@ func (c *Client) GetDeploymentID(stateroot string) (string, error) { return "", fmt.Errorf("failed to find deployment with osname %s", stateroot) } -func (c *Client) GetDeploymentIndex(stateroot string) (int, error) { - status, err := c.QueryStatus() +func (c *Client) GetDeploymentIndex(ctx context.Context, stateroot string) (int, error) { + status, err := c.QueryStatus(ctx) if err != nil { return -1, err } @@ -191,8 +192,8 @@ func (c *Client) GetDeploymentIndex(stateroot string) (int, error) { return getDeploymentIndex(status.Deployments, stateroot) } -func (c *Client) GetUnbootedDeploymentIndex() (int, error) { - status, err := c.QueryStatus() +func (c *Client) GetUnbootedDeploymentIndex(ctx context.Context) (int, error) { + status, err := c.QueryStatus(ctx) if err != nil { return -1, err } @@ -208,8 +209,8 @@ func (c *Client) GetUnbootedDeploymentIndex() (int, error) { } // IsStaterootBooted returns whether the specified stateroot is booted -func (c *Client) IsStaterootBooted(stateroot string) (bool, error) { - status, err := c.QueryStatus() +func (c *Client) IsStaterootBooted(ctx context.Context, stateroot string) (bool, error) { + status, err := c.QueryStatus(ctx) if err != nil { return false, err } @@ -223,8 +224,8 @@ func (c *Client) IsStaterootBooted(stateroot string) (bool, error) { } // GetCurrentStaterootName returns current stateroot name (a.k.a OSName) -func (c *Client) GetCurrentStaterootName() (string, error) { - status, err := c.QueryStatus() +func (c *Client) GetCurrentStaterootName(ctx context.Context) (string, error) { + status, err := c.QueryStatus(ctx) if err != nil { return "", err } @@ -233,8 +234,8 @@ func (c *Client) GetCurrentStaterootName() (string, error) { } // GetUnbootedStaterootName returns unbooted stateroot name (a.k.a OSName) -func (c *Client) GetUnbootedStaterootName() (string, error) { - status, err := c.QueryStatus() +func (c *Client) GetUnbootedStaterootName(ctx context.Context) (string, error) { + status, err := c.QueryStatus(ctx) if err != nil { return "", err } @@ -243,7 +244,7 @@ func (c *Client) GetUnbootedStaterootName() (string, error) { return getUnbootedStaterootName(status.Deployments) } -func (c *Client) RpmOstreeCleanup() error { - _, err := c.newCmd("cleanup", "-b") +func (c *Client) RpmOstreeCleanup(ctx context.Context) error { + _, err := c.newCmd(ctx, "cleanup", "-b") return err } diff --git a/lca-cli/postpivot/postpivot.go b/lca-cli/postpivot/postpivot.go index 2a5518db9e..afa6c98ae0 100644 --- a/lca-cli/postpivot/postpivot.go +++ b/lca-cli/postpivot/postpivot.go @@ -144,7 +144,7 @@ func (p *PostPivot) PostPivotConfiguration(ctx context.Context) error { seedReconfiguration.NodeIPs = seedReconfigurationNodeIPs(seedReconfiguration) p.log.Infof("Seed reconfiguration node IPs: %v", seedReconfiguration.NodeIPs) - if err := utils.RunOnce("setSSHKey", p.workingDir, p.log, p.setSSHKey, + if err := utils.RunOnce("setSSHKey", p.workingDir, p.log, p.setSSHKey, ctx, seedReconfiguration.SSHKey, sshKeyEarlyAccessFile); err != nil { return fmt.Errorf("failed to run once setSSHKey for post pivot: %w", err) } @@ -172,7 +172,7 @@ func (p *PostPivot) PostPivotConfiguration(ctx context.Context) error { // NOTE: This must be done before we run recert, as otherwise recert could // fail to pull in absence of a precached recert image - if err := p.establishEarlyCertificateTrust(seedReconfiguration); err != nil { + if err := p.establishEarlyCertificateTrust(ctx, seedReconfiguration); err != nil { return fmt.Errorf("failed copy cluster config files: %w", err) } @@ -180,7 +180,7 @@ func (p *PostPivot) PostPivotConfiguration(ctx context.Context) error { return fmt.Errorf("failed to run once recert for post pivot: %w", err) } - if err := p.restartChronydService(seedReconfiguration.ChronyConfig); err != nil { + if err := p.restartChronydService(ctx, seedReconfiguration.ChronyConfig); err != nil { return fmt.Errorf("failed to restart chronyd service: %w", err) } @@ -201,7 +201,7 @@ func (p *PostPivot) PostPivotConfiguration(ctx context.Context) error { return fmt.Errorf("failed to create k8s dynamic client, err: %w", err) } - if _, err := p.ops.SystemctlAction("enable", "kubelet", "--now"); err != nil { + if _, err := p.ops.SystemctlAction(ctx, "enable", "kubelet", "--now"); err != nil { return fmt.Errorf("failed to enable kubelet: %w", err) } utils.WaitForApi(ctx, client, p.log) @@ -243,11 +243,11 @@ func (p *PostPivot) PostPivotConfiguration(ctx context.Context) error { } // Restore lvm devices - if err := utils.RunOnce("recover_lvm_devices", p.workingDir, p.log, p.recoverLvmDevices); err != nil { + if err := utils.RunOnce("recover_lvm_devices", p.workingDir, p.log, p.recoverLvmDevices, ctx); err != nil { return fmt.Errorf("failed to run once recover_lvm_devices for post pivot: %w", err) } - if _, err = p.ops.SystemctlAction("disable", "installation-configuration.service"); err != nil { + if _, err = p.ops.SystemctlAction(ctx, "disable", "installation-configuration.service"); err != nil { return fmt.Errorf("failed to disable installation-configuration.service, err: %w", err) } @@ -272,7 +272,7 @@ func (p *PostPivot) recert(ctx context.Context, seedReconfiguration *clusterconf return fmt.Errorf("failed to create recert config file: %w", err) } - if _, err := p.ops.RunInHostNamespace("podman", "image", "exists", seedClusterInfo.RecertImagePullSpec); err != nil { + if _, err := p.ops.RunInHostNamespace(ctx, "podman", "image", "exists", seedClusterInfo.RecertImagePullSpec); err != nil { ctxWithTimeout, cancel := context.WithTimeout(ctx, 10*time.Minute) defer cancel() _ = wait.PollUntilContextCancel(ctxWithTimeout, time.Second, true, func(ctx context.Context) (bool, error) { @@ -283,7 +283,7 @@ func (p *PostPivot) recert(ctx context.Context, seedReconfiguration *clusterconf seedReconfiguration.Proxy.HTTPProxy, seedReconfiguration.Proxy.HTTPSProxy, seedReconfiguration.Proxy.NoProxy, command) } - if _, err := p.ops.RunBashInHostNamespace(command, "pull", "--authfile", common.ImageRegistryAuthFile, seedClusterInfo.RecertImagePullSpec); err != nil { + if _, err := p.ops.RunBashInHostNamespace(ctx, command, "pull", "--authfile", common.ImageRegistryAuthFile, seedClusterInfo.RecertImagePullSpec); err != nil { p.log.Warnf("failed to pull recert image, will retry, err: %s", err.Error()) return false, nil } @@ -291,10 +291,10 @@ func (p *PostPivot) recert(ctx context.Context, seedReconfiguration *clusterconf }) } - err := p.ops.RecertFullFlow(seedClusterInfo.RecertImagePullSpec, p.authFile, + err := p.ops.RecertFullFlow(ctx, seedClusterInfo.RecertImagePullSpec, p.authFile, path.Join(p.workingDir, recert.RecertConfigFile), nil, - func() error { return p.postRecertCommands() }, + func() error { return p.postRecertCommands(ctx) }, "-v", fmt.Sprintf("%s:%s", p.workingDir, p.workingDir)) if err != nil { return fmt.Errorf("failed recert full flow: %w", err) @@ -410,22 +410,22 @@ func (p *PostPivot) setProxyAndProxyStatus(seedReconfig *clusterconfig_api.SeedR return nil } -func (p *PostPivot) postRecertCommands() error { +func (p *PostPivot) postRecertCommands(ctx context.Context) error { // changing seed ip to new ip in all static pod files - _, err := p.ops.RunBashInHostNamespace("update-ca-trust") + _, err := p.ops.RunBashInHostNamespace(ctx, "update-ca-trust") if err != nil { return fmt.Errorf("failed to run update-ca-trust after recert: %w", err) } return nil } -func (p *PostPivot) restartChronydService(chronyConfig string) error { +func (p *PostPivot) restartChronydService(ctx context.Context, chronyConfig string) error { if chronyConfig == "" { p.log.Info("Chrony config is empty, skipping restart of chronyd service") return nil } p.log.Info("Restarting chronyd service") - if _, err := p.ops.SystemctlAction("restart", "chronyd"); err != nil { + if _, err := p.ops.SystemctlAction(ctx, "restart", "chronyd"); err != nil { return fmt.Errorf("failed to restart chronyd: %w", err) } return nil @@ -488,7 +488,7 @@ func (p *PostPivot) handleManifest(ctx context.Context, mPath string, dynamicCli return nil } -func (p *PostPivot) recoverLvmDevices() error { +func (p *PostPivot) recoverLvmDevices(ctx context.Context) error { lvmConfigPath := path.Join(p.workingDir, common.LvmConfigDir) lvmDevicesPath := path.Join(lvmConfigPath, path.Base(common.LvmDevicesPath)) @@ -500,7 +500,7 @@ func (p *PostPivot) recoverLvmDevices() error { } // Update the online record of PVs and activate all lvm devices in the VGs - _, err := p.ops.RunInHostNamespace("pvscan", "--cache", "--activate", "ay") + _, err := p.ops.RunInHostNamespace(ctx, "pvscan", "--cache", "--activate", "ay") if err != nil { return fmt.Errorf("failed to scan and active lvm devices, err: %w", err) } @@ -570,14 +570,14 @@ func (p *PostPivot) restoreOadpDataProtectionApplication(ctx context.Context, cl // which might require trusting the registry's certificate. This is actually // somewhat redundant because recert itself already does the same thing (among // other things), but we need to do it before we can even run recert. -func (p *PostPivot) establishEarlyCertificateTrust(seedReconfiguration *clusterconfig_api.SeedReconfiguration) error { +func (p *PostPivot) establishEarlyCertificateTrust(ctx context.Context, seedReconfiguration *clusterconfig_api.SeedReconfiguration) error { if seedReconfiguration.AdditionalTrustBundle.UserCaBundle != "" { if err := os.WriteFile(common.CABundleFilePath, []byte(seedReconfiguration.AdditionalTrustBundle.UserCaBundle), 0o600); err != nil { return fmt.Errorf("failed to write user ca bundle to %s: %w", common.CABundleFilePath, err) } } - _, err := p.ops.RunBashInHostNamespace("update-ca-trust") + _, err := p.ops.RunBashInHostNamespace(ctx, "update-ca-trust") if err != nil { return fmt.Errorf("failed to run update-ca-trust after early certificate trust: %w", err) } @@ -747,7 +747,7 @@ func (p *PostPivot) setNodeIPsIfNotProvided( // See: https://github.com/openshift/baremetal-runtimecfg/blob/e898adc576b343a214aff860e349f9bba3a125d4/cmd/runtimecfg/node-ip.go#L107 // and: https://github.com/openshift/baremetal-runtimecfg/blob/e898adc576b343a214aff860e349f9bba3a125d4/cmd/runtimecfg/node-ip.go#L148 if _, err := os.Stat(nodePrimaryIPFile); err != nil { - _, err := p.ops.SystemctlAction("start", "nodeip-configuration") + _, err := p.ops.SystemctlAction(ctx, "start", "nodeip-configuration") if err != nil { return fmt.Errorf("failed to start nodeip-configuration service, err %w", err) } @@ -814,7 +814,7 @@ func parseKubeletNodeIPs(content string) ([]string, error) { // setSSHKey sets ssh public key provided by user in 2 operations: // 1. as file in order to give early access to the node // 2. creates 2 machine configs in manifests dir that will be applied when cluster is up -func (p *PostPivot) setSSHKey(sshKey, sshKeyFile string) error { +func (p *PostPivot) setSSHKey(ctx context.Context, sshKey, sshKeyFile string) error { if sshKey == "" { p.log.Infof("No ssh public key was provided, skipping") return nil @@ -826,7 +826,7 @@ func (p *PostPivot) setSSHKey(sshKey, sshKeyFile string) error { } p.log.Infof("Setting %s user ownership on %s", userCore, sshKeyFile) - if _, err := p.ops.RunInHostNamespace("chown", userCore, sshKeyFile); err != nil { + if _, err := p.ops.RunInHostNamespace(ctx, "chown", userCore, sshKeyFile); err != nil { return fmt.Errorf("failed to set %s user ownership on %s, err :%w", userCore, sshKeyFile, err) } @@ -877,7 +877,7 @@ func (p *PostPivot) createSSHKeyMachineConfigs(sshKey string) error { // applyNMStateConfiguration is applying nmstate yaml provided as string in seedReconfiguration. // It uses nmstatectl apply command that will return error in case configuration is not successful -func (p *PostPivot) applyNMStateConfiguration(seedReconfiguration *clusterconfig_api.SeedReconfiguration) error { +func (p *PostPivot) applyNMStateConfiguration(ctx context.Context, seedReconfiguration *clusterconfig_api.SeedReconfiguration) error { if seedReconfiguration.RawNMStateConfig == "" { p.log.Infof("NMState config is empty, skipping") return nil @@ -887,7 +887,7 @@ func (p *PostPivot) applyNMStateConfiguration(seedReconfiguration *clusterconfig if err := os.WriteFile(nmFile, []byte(seedReconfiguration.RawNMStateConfig), 0o600); err != nil { return fmt.Errorf("failed to write nmstate config to %s, err %w", nmFile, err) } - if _, err := p.ops.RunInHostNamespace("nmstatectl", "apply", nmFile); err != nil { + if _, err := p.ops.RunInHostNamespace(ctx, "nmstatectl", "apply", nmFile); err != nil { return fmt.Errorf("failed to apply nmstate config %s, err: %w", seedReconfiguration.RawNMStateConfig, err) } @@ -918,7 +918,7 @@ func (p *PostPivot) waitForConfiguration(ctx context.Context, configFolder, bloc if _, err := os.Stat(configFolder); err == nil { return true, nil } - blockDevices, err := p.ops.ListBlockDevices() + blockDevices, err := p.ops.ListBlockDevices(ctx) if err != nil { p.log.Infof("Failed to list block devices with error %s, will retry", err.Error()) return false, nil @@ -927,7 +927,7 @@ func (p *PostPivot) waitForConfiguration(ctx context.Context, configFolder, bloc // TODO: change after all the components will move to clusterconfig_api.BlockDeviceLabel if lo.Contains([]string{clusterconfig_api.BlockDeviceLabel, OldblockDeviceLabel}, bd.Label) { // in case of error while mounting device we exit wait and return the error - if err := p.setupConfigurationFolder(bd.Name, blockDeviceMountFolder, filepath.Dir(configFolder)); err != nil { + if err := p.setupConfigurationFolder(ctx, bd.Name, blockDeviceMountFolder, filepath.Dir(configFolder)); err != nil { return true, err } return true, nil @@ -944,17 +944,17 @@ func (p *PostPivot) waitForConfiguration(ctx context.Context, configFolder, bloc } // setupConfigurationFolder mounts device to mountFolder and copies everything to configFolder -func (p *PostPivot) setupConfigurationFolder(deviceName, mountFolder, configFolder string) error { +func (p *PostPivot) setupConfigurationFolder(ctx context.Context, deviceName, mountFolder, configFolder string) error { p.log.Infof("Running setup of configuration folder") if err := os.MkdirAll(configFolder, 0o700); err != nil { return fmt.Errorf("failed to create %s, err: %w", configFolder, err) } defer func() { _ = os.RemoveAll(mountFolder) }() - if err := p.ops.Mount(deviceName, mountFolder); err != nil { + if err := p.ops.Mount(ctx, deviceName, mountFolder); err != nil { return fmt.Errorf("failed to mount %s: %w", mountFolder, err) } - defer func() { _ = p.ops.Umount(deviceName) }() + defer func() { _ = p.ops.Umount(context.WithoutCancel(ctx), deviceName) }() if err := utils.CopyFileIfExists(mountFolder, configFolder); err != nil { return fmt.Errorf("failed to copy contert of %s to %s, err: %w", mountFolder, configFolder, err) @@ -965,10 +965,10 @@ func (p *PostPivot) setupConfigurationFolder(deviceName, mountFolder, configFold // setHostname set provided hostname in case it was provided, in case it was not provided we will get hostname from kernel // retuning error in case hostname is localhost -func (p *PostPivot) setHostname(hostname string) (string, error) { +func (p *PostPivot) setHostname(ctx context.Context, hostname string) (string, error) { if hostname != "" && hostname != localhost { p.log.Infof("Setting new hostname %s", hostname) - if _, err := p.ops.RunInHostNamespace("hostnamectl", "set-hostname", hostname); err != nil { + if _, err := p.ops.RunInHostNamespace(ctx, "hostnamectl", "set-hostname", hostname); err != nil { return "", fmt.Errorf("failed to set hostname %s, err %w", hostname, err) } return hostname, nil @@ -1044,16 +1044,16 @@ func (p *PostPivot) networkConfiguration(ctx context.Context, seedReconfiguratio return err } - if err := utils.RunOnce("apply-static-network", p.workingDir, p.log, p.applyNMStateConfiguration, + if err := utils.RunOnce("apply-static-network", p.workingDir, p.log, p.applyNMStateConfiguration, ctx, seedReconfiguration); err != nil { return fmt.Errorf("failed to apply static network: %w", err) } - if _, err := p.ops.SystemctlAction("restart", nmService); err != nil { + if _, err := p.ops.SystemctlAction(ctx, "restart", nmService); err != nil { return fmt.Errorf("failed to restart network manager service, err %w", err) } - seedReconfiguration.Hostname, err = p.setHostname(seedReconfiguration.Hostname) + seedReconfiguration.Hostname, err = p.setHostname(ctx, seedReconfiguration.Hostname) if err != nil { return err } @@ -1070,7 +1070,7 @@ func (p *PostPivot) networkConfiguration(ctx context.Context, seedReconfiguratio return err } - if _, err := p.ops.SystemctlAction("restart", dnsmasqService); err != nil { + if _, err := p.ops.SystemctlAction(ctx, "restart", dnsmasqService); err != nil { return fmt.Errorf("failed to restart dnsmasq service, err %w", err) } diff --git a/lca-cli/postpivot/postpivot_test.go b/lca-cli/postpivot/postpivot_test.go index 872d1c101e..42cc24c4c1 100644 --- a/lca-cli/postpivot/postpivot_test.go +++ b/lca-cli/postpivot/postpivot_test.go @@ -665,12 +665,12 @@ interfaces: nmstateFile := path.Join(tmpDir, "nmstate.yaml") pp := NewPostPivot(nil, log, mockOps, "", tmpDir, "") if tc.expectedError { - mockOps.EXPECT().RunInHostNamespace("nmstatectl", "apply", nmstateFile).Return("", fmt.Errorf("Dummy")) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", nmstateFile).Return("", fmt.Errorf("Dummy")) } else { - mockOps.EXPECT().RunInHostNamespace("nmstatectl", "apply", nmstateFile).Return("", nil) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "nmstatectl", "apply", nmstateFile).Return("", nil) } - err := pp.applyNMStateConfiguration(tc.seedReconfiguration) + err := pp.applyNMStateConfiguration(context.Background(), tc.seedReconfiguration) if !tc.expectedError { if err != nil { t.Errorf("unexpected error: %v", err) @@ -783,13 +783,13 @@ func TestSetHostname(t *testing.T) { mockOps := ops.NewMockOps(mockController) pp := NewPostPivot(nil, log, mockOps, "", "", "") if tc.hostname != "" && tc.hostname != localhost { - mockOps.EXPECT().RunInHostNamespace("hostnamectl", "set-hostname", tc.hostname).Return("", nil).Times(1) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "hostnamectl", "set-hostname", tc.hostname).Return("", nil).Times(1) } else if tc.hostname == "" { - mockOps.EXPECT().RunInHostNamespace("hostnamectl", "set-hostname", tc.hostname).Return("", nil).Times(0) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "hostnamectl", "set-hostname", tc.hostname).Return("", nil).Times(0) mockOps.EXPECT().GetHostname().Return(tc.osHostname, nil).Times(1) } - hostname, err := pp.setHostname(tc.hostname) + hostname, err := pp.setHostname(context.Background(), tc.hostname) assert.Equal(t, tc.expectedError, err != nil, err) if tc.hostname == "" { assert.Equal(t, tc.osHostname, hostname) @@ -851,16 +851,16 @@ func TestWaitForConfiguration(t *testing.T) { } } if tc.listBlockDevicesSucceeds { - mockOps.EXPECT().ListBlockDevices().Return([]ops.BlockDevice{{Name: deviceName, + mockOps.EXPECT().ListBlockDevices(gomock.Any()).Return([]ops.BlockDevice{{Name: deviceName, Label: clusterconfig_api.BlockDeviceLabel}}, nil).Times(1) if tc.mountSucceeds { - mockOps.EXPECT().Mount(deviceName, gomock.Any()).Return(nil).Times(1) - mockOps.EXPECT().Umount(deviceName).Return(nil).Times(1) + mockOps.EXPECT().Mount(gomock.Any(), deviceName, gomock.Any()).Return(nil).Times(1) + mockOps.EXPECT().Umount(gomock.Any(), deviceName).Return(nil).Times(1) } else { - mockOps.EXPECT().Mount(deviceName, gomock.Any()).Return(fmt.Errorf("dummy")).Times(1) + mockOps.EXPECT().Mount(gomock.Any(), deviceName, gomock.Any()).Return(fmt.Errorf("dummy")).Times(1) } } else if !tc.configurationFolderExists { - mockOps.EXPECT().ListBlockDevices().Return(nil, fmt.Errorf("dummy")).Do(cancel).Times(1) + mockOps.EXPECT().ListBlockDevices(gomock.Any()).Return(nil, fmt.Errorf("dummy")).Do(func(_ context.Context) { cancel() }).Times(1) } err := pp.waitForConfiguration(ctx, configFolder, configFolder) @@ -913,16 +913,16 @@ func TestNetworkConfiguration(t *testing.T) { dnsmasqOverrides = path.Join(tmpDir, "dnsmasqoverrides") if tc.restartNMSuccess { - mockOps.EXPECT().SystemctlAction("restart", nmService).Return("", nil).Times(1) + mockOps.EXPECT().SystemctlAction(gomock.Any(), "restart", nmService).Return("", nil).Times(1) if tc.restartDNSMASQSuccess { - mockOps.EXPECT().RunInHostNamespace("hostnamectl", "set-hostname", "test").Return("", nil).Times(1) - mockOps.EXPECT().SystemctlAction("restart", dnsmasqService).Return("", nil).Times(1) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "hostnamectl", "set-hostname", "test").Return("", nil).Times(1) + mockOps.EXPECT().SystemctlAction(gomock.Any(), "restart", dnsmasqService).Return("", nil).Times(1) } else { - mockOps.EXPECT().RunInHostNamespace("hostnamectl", "set-hostname", "test").Return("", nil).Times(1) - mockOps.EXPECT().SystemctlAction("restart", dnsmasqService).Return("", fmt.Errorf("dummy")).Times(1) + mockOps.EXPECT().RunInHostNamespace(gomock.Any(), "hostnamectl", "set-hostname", "test").Return("", nil).Times(1) + mockOps.EXPECT().SystemctlAction(gomock.Any(), "restart", dnsmasqService).Return("", fmt.Errorf("dummy")).Times(1) } } else { - mockOps.EXPECT().SystemctlAction("restart", nmService).Return("", fmt.Errorf("dummy")).Times(1) + mockOps.EXPECT().SystemctlAction(gomock.Any(), "restart", nmService).Return("", fmt.Errorf("dummy")).Times(1) } err := pp.networkConfiguration(context.TODO(), seedReconfiguration) @@ -1495,11 +1495,11 @@ func TestRestartChronydService(t *testing.T) { // Mock: chronyd restart only if expected if tc.expectCall { - testMockOps.EXPECT().SystemctlAction("restart", "chronyd"). + testMockOps.EXPECT().SystemctlAction(gomock.Any(), "restart", "chronyd"). Return("", tc.mockError).Times(1) } - err := pp.restartChronydService(tc.chronyConfig) + err := pp.restartChronydService(context.Background(), tc.chronyConfig) if tc.expectedError { assert.Error(t, err) diff --git a/lca-cli/seedcreator/seedcreator.go b/lca-cli/seedcreator/seedcreator.go index 028e75556d..856ce616e5 100644 --- a/lca-cli/seedcreator/seedcreator.go +++ b/lca-cli/seedcreator/seedcreator.go @@ -62,11 +62,10 @@ func NewSeedCreator(client runtime.Client, log *logrus.Logger, ops ops.Ops, ostr } // CreateSeedImage comprises the lca-cli workflow for creating a single OCI seed image -func (s *SeedCreator) CreateSeedImage() error { +func (s *SeedCreator) CreateSeedImage(ctx context.Context) error { s.log.Info("Creating seed image") - ctx := context.TODO() - if err := s.copyConfigurationFiles(); err != nil { + if err := s.copyConfigurationFiles(ctx); err != nil { return fmt.Errorf("failed to add configuration files: %w", err) } @@ -114,14 +113,14 @@ func (s *SeedCreator) CreateSeedImage() error { s.log.Info("Seed cluster certificates backed up successfully for recert tool") } - if err := s.stopServices(); err != nil { + if err := s.stopServices(ctx); err != nil { return fmt.Errorf("failed to stop services to create seed image: %w", err) } if s.recertSkipValidation { s.log.Info("Skipping recert validation.") } else { - if err := utils.RunOnce("recert", common.BackupChecksDir, s.log, s.ops.ForceExpireSeedCrypto, + if err := utils.RunOnce("recert", common.BackupChecksDir, s.log, s.ops.ForceExpireSeedCrypto, ctx, s.recertContainerImage, s.authFile, seedHasKubeadminPassword); err != nil { return fmt.Errorf("failed to run once recert: %w", err) } @@ -130,23 +129,23 @@ func (s *SeedCreator) CreateSeedImage() error { return fmt.Errorf("failed remove all OVN certs folders: %w", err) } - if err := utils.RunOnce("backup_var", common.BackupChecksDir, s.log, s.backupVar); err != nil { + if err := utils.RunOnce("backup_var", common.BackupChecksDir, s.log, s.backupVar, ctx); err != nil { return fmt.Errorf("failed to run once backup_var: %w", err) } - if err := utils.RunOnce("backup_etc", common.BackupChecksDir, s.log, s.backupEtc); err != nil { + if err := utils.RunOnce("backup_etc", common.BackupChecksDir, s.log, s.backupEtc, ctx); err != nil { return fmt.Errorf("failed to run once backup_etc: %w", err) } - if err := utils.RunOnce("backup_ostree", common.BackupChecksDir, s.log, s.backupOstree); err != nil { + if err := utils.RunOnce("backup_ostree", common.BackupChecksDir, s.log, s.backupOstree, ctx); err != nil { return fmt.Errorf("failed to run once backup_ostree: %w", err) } - if err := utils.RunOnce("backup_rpmostree", common.BackupChecksDir, s.log, s.backupRPMOstree); err != nil { + if err := utils.RunOnce("backup_rpmostree", common.BackupChecksDir, s.log, s.backupRPMOstree, ctx); err != nil { return fmt.Errorf("failed to run once backup_rpmostree: %w", err) } - if err := utils.RunOnce("backup_mco_config", common.BackupChecksDir, s.log, s.backupMCOConfig); err != nil { + if err := utils.RunOnce("backup_mco_config", common.BackupChecksDir, s.log, s.backupMCOConfig, ctx); err != nil { return fmt.Errorf("failed to run once backup_mco_config: %w", err) } @@ -156,19 +155,19 @@ func (s *SeedCreator) CreateSeedImage() error { } clusterInfoJSON := string(clusterInfoJSONSBytes) - if err := s.createAndPushSeedImage(clusterInfoJSON); err != nil { + if err := s.createAndPushSeedImage(ctx, clusterInfoJSON); err != nil { return fmt.Errorf("failed to create and push seed image: %w", err) } return nil } -func (s *SeedCreator) copyConfigurationFiles() error { +func (s *SeedCreator) copyConfigurationFiles(ctx context.Context) error { - return s.handleServices() + return s.handleServices(ctx) } -func (s *SeedCreator) handleServices() error { +func (s *SeedCreator) handleServices(ctx context.Context) error { dir := filepath.Join(common.InstallationConfigurationFilesDir, "services") return utils.HandleFilesWithCallback(dir, func(path string) error { //nolint:wrapcheck serviceName := filepath.Base(path) @@ -179,7 +178,7 @@ func (s *SeedCreator) handleServices() error { } s.log.Infof("Enabling service %s", serviceName) - if _, err := s.ops.SystemctlAction("enable", serviceName); err != nil { + if _, err := s.ops.SystemctlAction(ctx, "enable", serviceName); err != nil { return fmt.Errorf("failed to enabling service %s: %w", serviceName, err) } return nil @@ -222,7 +221,7 @@ func (s *SeedCreator) gatherClusterInfo(ctx context.Context) error { return fmt.Errorf("failed to get additional trust bundle information: %w", err) } - containerStorageMountpointTarget, err := s.ops.GetContainerStorageTarget() + containerStorageMountpointTarget, err := s.ops.GetContainerStorageTarget(ctx) if err != nil { return fmt.Errorf("failed to get container storage mountpoint target: %w", err) } @@ -286,7 +285,7 @@ func (s *SeedCreator) createContainerList(ctx context.Context) error { // purge all unknown image if exists s.log.Info("Cleaning image list") // Don't ever add -a option as we don't want to delete unused images - if _, err := s.ops.RunBashInHostNamespace("podman", "image", "prune", "-f"); err != nil { + if _, err := s.ops.RunBashInHostNamespace(ctx, "podman", "image", "prune", "-f"); err != nil { return fmt.Errorf("failed to prune with podmamn: %w", err) } @@ -295,7 +294,7 @@ func (s *SeedCreator) createContainerList(ctx context.Context) error { args := []string{"images", "-o", "json", "|", "jq", "-r", "'.images[] | if .repoTags | length > 0 then .repoTags[] else .repoDigests[] end'"} - output, err := s.ops.RunBashInHostNamespace("crictl", args...) + output, err := s.ops.RunBashInHostNamespace(ctx, "crictl", args...) if err != nil { return fmt.Errorf("failed to run crictl with args %s: %w", args, err) } @@ -327,14 +326,14 @@ func (s *SeedCreator) createContainerList(ctx context.Context) error { return nil } -func (s *SeedCreator) stopServices() error { - if err := s.ops.StopClusterServices(); err != nil { +func (s *SeedCreator) stopServices(ctx context.Context) error { + if err := s.ops.StopClusterServices(ctx); err != nil { return fmt.Errorf("failed to stop cluster services: %w", err) } return nil } -func (s *SeedCreator) backupVar() error { +func (s *SeedCreator) backupVar(ctx context.Context) error { varTarFile := path.Join(s.backupDir, "var.tgz") // Define the 'exclude' patterns @@ -370,7 +369,7 @@ func (s *SeedCreator) backupVar() error { tarArgs = append(tarArgs, common.TarOpts...) // Run the tar command - _, err := s.ops.RunBashInHostNamespace("tar", tarArgs...) + _, err := s.ops.RunBashInHostNamespace(ctx, "tar", tarArgs...) if err != nil { return fmt.Errorf("failed to run tar for backupVar: %w", err) } @@ -379,7 +378,7 @@ func (s *SeedCreator) backupVar() error { return nil } -func (s *SeedCreator) backupEtc() error { +func (s *SeedCreator) backupEtc(ctx context.Context) error { s.log.Info("Backing up /etc") // Execute 'ostree admin config-diff' command and backup etc.deletions @@ -400,7 +399,7 @@ func (s *SeedCreator) backupEtc() error { args := []string{"admin", "config-diff", "|", "awk", `'$1 == "D" {print "/etc/" $2}'`, ">", path.Join(s.backupDir, "/etc.deletions")} - _, err := s.ops.RunBashInHostNamespace("ostree", args...) + _, err := s.ops.RunBashInHostNamespace(ctx, "ostree", args...) if err != nil { return fmt.Errorf("failed backing up /etc with args %s: %w", args, err) } @@ -409,7 +408,7 @@ func (s *SeedCreator) backupEtc() error { "|", "awk", `'$1 != "D" {print "/etc/" $2}'`, "|"} args = append(args, tarArgs...) - _, err = s.ops.RunBashInHostNamespace("ostree", args...) + _, err = s.ops.RunBashInHostNamespace(ctx, "ostree", args...) if err != nil { return fmt.Errorf("failed backing up /etc with args %s: %w", args, err) } @@ -418,33 +417,33 @@ func (s *SeedCreator) backupEtc() error { return nil } -func (s *SeedCreator) backupOstree() error { +func (s *SeedCreator) backupOstree(ctx context.Context) error { s.log.Info("Backing up ostree") ostreeTar := s.backupDir + "/ostree.tgz" // Execute 'tar' command and backup /etc args := []string{"czf", ostreeTar, "-C", "/ostree/repo", "."} args = append(args, common.TarOpts...) - if _, err := s.ops.RunBashInHostNamespace("tar", args...); err != nil { + if _, err := s.ops.RunBashInHostNamespace(ctx, "tar", args...); err != nil { return fmt.Errorf("failed backing ostree with args %s: %w", args, err) } return nil } -func (s *SeedCreator) backupRPMOstree() error { +func (s *SeedCreator) backupRPMOstree(ctx context.Context) error { rpmJSON := s.backupDir + "/rpm-ostree.json" args := append([]string{"status", "-v", "--json"}, ">", rpmJSON) - if _, err := s.ops.RunBashInHostNamespace("rpm-ostree", args...); err != nil { + if _, err := s.ops.RunBashInHostNamespace(ctx, "rpm-ostree", args...); err != nil { return fmt.Errorf("failed to run backup rpmostree with args %s: %w", args, err) } s.log.Info("Backup of rpm-ostree.json created successfully.") return nil } -func (s *SeedCreator) backupMCOConfig() error { +func (s *SeedCreator) backupMCOConfig(ctx context.Context) error { mcoJSON := s.backupDir + "/mco-currentconfig.json" - if _, err := s.ops.RunBashInHostNamespace("cp", common.MCDCurrentConfig, mcoJSON); err != nil { + if _, err := s.ops.RunBashInHostNamespace(ctx, "cp", common.MCDCurrentConfig, mcoJSON); err != nil { return fmt.Errorf("failed to backup MCO config: %w", err) } s.log.Info("Backup of mco-currentconfig created successfully.") @@ -452,16 +451,16 @@ func (s *SeedCreator) backupMCOConfig() error { } // Building and pushing OCI image -func (s *SeedCreator) createAndPushSeedImage(clusterInfo string) error { +func (s *SeedCreator) createAndPushSeedImage(ctx context.Context, clusterInfo string) error { s.log.Info("Build and push OCI image to ", s.containerRegistry) - s.log.Debug(s.ostreeClient.RpmOstreeVersion()) // If verbose, also dump out current rpm-ostree version available + s.log.Debug(s.ostreeClient.RpmOstreeVersion(ctx)) // If verbose, also dump out current rpm-ostree version available // Get the current status of rpm-ostree daemon in the host - statusRpmOstree, err := s.ostreeClient.QueryStatus() + statusRpmOstree, err := s.ostreeClient.QueryStatus(ctx) if err != nil { return fmt.Errorf("failed to query ostree status: %w", err) } - if err := s.backupOstreeOrigin(statusRpmOstree); err != nil { + if err := s.backupOstreeOrigin(ctx, statusRpmOstree); err != nil { return err } @@ -488,14 +487,14 @@ func (s *SeedCreator) createAndPushSeedImage(clusterInfo string) error { "--label", fmt.Sprintf("%s=%s", common.SeedClusterInfoOCILabel, clusterInfo), s.backupDir, } - _, err = s.ops.RunInHostNamespace( + _, err = s.ops.RunInHostNamespace(ctx, "podman", podmanBuildArgs...) if err != nil { return fmt.Errorf("failed to build seed image: %w", err) } // Push the created OCI image to user's repository - _, err = s.ops.RunInHostNamespace( + _, err = s.ops.RunInHostNamespace(ctx, "podman", []string{"push", "--authfile", s.authFile, s.containerRegistry}...) if err != nil { return fmt.Errorf("failed to push seed image: %w", err) @@ -504,7 +503,7 @@ func (s *SeedCreator) createAndPushSeedImage(clusterInfo string) error { return nil } -func (s *SeedCreator) backupOstreeOrigin(statusRpmOstree *ostree.Status) error { +func (s *SeedCreator) backupOstreeOrigin(ctx context.Context, statusRpmOstree *ostree.Status) error { // Get OSName for booted ostree deployment bootedOSName := statusRpmOstree.Deployments[0].OSName @@ -520,7 +519,7 @@ func (s *SeedCreator) backupOstreeOrigin(statusRpmOstree *ostree.Status) error { return fmt.Errorf("failed to get file info for %s: %w", originFileName, err) } // Execute 'copy' command and backup .origin file - _, err = s.ops.RunInHostNamespace( + _, err = s.ops.RunInHostNamespace(ctx, "cp", []string{"/ostree/deploy/" + bootedOSName + "/deploy/" + bootedDeployment + ".origin", originFileName}...) if err != nil { return fmt.Errorf("failed 'copy' command to backup .origin file,: %w", err) diff --git a/lca-cli/seedrestoration/seedrestoration.go b/lca-cli/seedrestoration/seedrestoration.go index e8dd52db2b..e7f32f7282 100644 --- a/lca-cli/seedrestoration/seedrestoration.go +++ b/lca-cli/seedrestoration/seedrestoration.go @@ -17,6 +17,7 @@ limitations under the License. package seedrestoration import ( + "context" "fmt" "os" "path/filepath" @@ -62,7 +63,7 @@ func NewSeedRestoration(log *logrus.Logger, ops ops.Ops, backupDir, // RestoreSeedCluster comprises the lca-cli workflow for restoration operations // after creating a seed image out of an SNO cluster. -func (s *SeedRestoration) RestoreSeedCluster() error { +func (s *SeedRestoration) RestoreSeedCluster(ctx context.Context) error { s.log.Info("Cleaning up seed cluster") // Collect all restoration errors to only fail fatally at the end, @@ -70,13 +71,13 @@ func (s *SeedRestoration) RestoreSeedCluster() error { var errors []error s.log.Info("Removing seed image") - if _, err := s.ops.RunInHostNamespace("podman", []string{"rmi", s.containerRegistry}...); err != nil { + if _, err := s.ops.RunInHostNamespace(ctx, "podman", []string{"rmi", s.containerRegistry}...); err != nil { s.log.Errorf("failed to remove seed image: %v", err) errors = append(errors, err) } s.log.Info("Cleaning up systemd service units") - if err := s.cleanupServiceUnits(); err != nil { + if err := s.cleanupServiceUnits(ctx); err != nil { s.log.Errorf("Error cleaning up systemd service files: %v", err) errors = append(errors, err) } @@ -87,7 +88,7 @@ func (s *SeedRestoration) RestoreSeedCluster() error { s.log.Info("Restoring crypto via recert tool") recertFilePath := filepath.Join(common.BackupChecksDir, "recert.done") if _, err := os.Stat(recertFilePath); err == nil && !os.IsNotExist(err) { - if err := s.ops.RestoreOriginalSeedCrypto(s.recertContainerImage, s.authFile); err != nil { + if err := s.ops.RestoreOriginalSeedCrypto(ctx, s.recertContainerImage, s.authFile); err != nil { s.log.Errorf("Error restoring certificates: %v", err) errors = append(errors, err) } @@ -103,7 +104,7 @@ func (s *SeedRestoration) RestoreSeedCluster() error { } s.log.Info("Restoring cluster services (i.e. kubelet.service unit)") - if _, err := s.ops.SystemctlAction("enable", "kubelet.service", "--now"); err != nil { + if _, err := s.ops.SystemctlAction(ctx, "enable", "kubelet.service", "--now"); err != nil { s.log.Errorf("Error enabling kubelet.service unit: %v", err) errors = append(errors, err) } @@ -115,7 +116,7 @@ func (s *SeedRestoration) RestoreSeedCluster() error { return nil } -func (s *SeedRestoration) cleanupServiceUnits() error { +func (s *SeedRestoration) cleanupServiceUnits(ctx context.Context) error { dir := filepath.Join(common.InstallationConfigurationFilesDir, "services") err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if info.IsDir() { @@ -124,7 +125,7 @@ func (s *SeedRestoration) cleanupServiceUnits() error { serviceName := info.Name() s.log.Infof("Disabling service unit %s", serviceName) - if _, err := s.ops.SystemctlAction("disable", serviceName, "--now"); err != nil { + if _, err := s.ops.SystemctlAction(ctx, "disable", serviceName, "--now"); err != nil { s.log.Errorf("Error disabling %s unit: %v", serviceName, err) } diff --git a/main/main.go b/main/main.go index 3994a5b1b4..f0a3e95c4a 100644 --- a/main/main.go +++ b/main/main.go @@ -23,6 +23,7 @@ import ( "fmt" "os" "sync" + "time" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -267,7 +268,9 @@ func main() { extraManifest := &extramanifest.EMHandler{ Client: mgr.GetClient(), DynamicClient: dynamicClient, Log: log.WithName("ExtraManifest")} - containerStorageMountpointTarget, err := chrootOp.GetContainerStorageTarget() + startupCtx, startupCancel := context.WithTimeout(context.Background(), 30*time.Second) + containerStorageMountpointTarget, err := chrootOp.GetContainerStorageTarget(startupCtx) + startupCancel() if err != nil { setupLog.Error(err, "unable to get container storage mountpoint target") os.Exit(1)