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)