Skip to content

Commit 067395e

Browse files
Merge pull request #5546 from isabella-janssen/mco-1924
MCO-1924: MCO-500: Migrate direct runGetOut() to use CommandRunner in the MCD
2 parents c9188a4 + a9ea655 commit 067395e

8 files changed

Lines changed: 163 additions & 52 deletions

File tree

pkg/daemon/bootc.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,6 @@ func (b *BootcClient) GetBootedAndStagedImage() (*BootEntry, *BootEntry, error)
181181
return status.Status.GetBootedImage(), status.Status.GetStagedImage(), nil
182182
}
183183

184-
// // GetStatus returns multi-line human-readable text describing system status
185-
// // Bootc is working on status update: https://github.com/containers/bootc/issues/408
186-
// func (r *BootcClient) GetStatus() (string, error) {
187-
// output, err := runGetOut("bootc", "status")
188-
// if err != nil {
189-
// return "", err
190-
// }
191-
192-
// return string(output), nil
193-
// }
194-
195184
// GetBootedImageInfo() returns the image URL as well as the image version(for logging) and the ostree commit (for comparisons)
196185
func (b *BootcClient) GetBootedImageInfo() (*BootedImageInfo, error) {
197186
bootedImage, _, err := b.GetBootedAndStagedImage()

pkg/daemon/command_runner.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ func (r *CommandRunnerOS) RunGetOut(command string, args ...string) ([]byte, err
3535
return rawOut, nil
3636
}
3737

38-
// TODO: Delete this function to always consume the interface instance
39-
// Tracking story: https://issues.redhat.com/browse/MCO-1924
40-
//
41-
// Conserved the old signature to avoid a big footprint bugfix with this change
42-
func runGetOut(command string, args ...string) ([]byte, error) {
43-
return (&CommandRunnerOS{}).RunGetOut(command, args...)
44-
}
45-
4638
// MockCommandRunner is a test implementation that returns pre-configured outputs.
4739
type MockCommandRunner struct {
4840
outputs map[string][]byte

pkg/daemon/daemon.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ func (dn *Daemon) RunFirstbootCompleteMachineconfig(machineConfigFile string) er
13101310

13111311
// Setting the Kernel Arguments is for comparison only with the desired MachineConfig.
13121312
// The resulting MC should not be for updating node configuration.
1313-
if err = setRunningKargs(oldConfig, mc.Spec.KernelArguments); err != nil {
1313+
if err = dn.setRunningKargs(oldConfig, mc.Spec.KernelArguments); err != nil {
13141314
return fmt.Errorf("failed to set kernel arguments from /proc/cmdline: %w", err)
13151315
}
13161316

@@ -1830,7 +1830,7 @@ func (dn *Daemon) getStateAndConfigs() (*stateAndConfigs, error) {
18301830
func (dn *Daemon) LogSystemData() {
18311831
// Print status if available
18321832
if dn.os.IsCoreOSVariant() {
1833-
out, err := runGetOut("rpm-ostree", "status")
1833+
out, err := dn.cmdRunner.RunGetOut("rpm-ostree", "status")
18341834
if err != nil {
18351835
klog.Fatalf("unable to get rpm-ostree status: %s", err)
18361836
}
@@ -1839,7 +1839,7 @@ func (dn *Daemon) LogSystemData() {
18391839
logProvisioningInformation()
18401840
}
18411841

1842-
boots, err := runGetOut("journalctl", "--list-boots")
1842+
boots, err := dn.cmdRunner.RunGetOut("journalctl", "--list-boots")
18431843
if err != nil {
18441844
klog.Errorf("Listing boots: %v", err)
18451845
}
@@ -1852,7 +1852,7 @@ func (dn *Daemon) LogSystemData() {
18521852
// to also watch dynamically of course.
18531853
//
18541854
// also xref https://github.com/coreos/console-login-helper-messages/blob/e8a849f4c23910e7c556c10719911cc59873fc23/usr/share/console-login-helper-messages/profile.sh
1855-
failedServices, err := runGetOut("systemctl", "list-units", "--state=failed", "--no-legend")
1855+
failedServices, err := dn.cmdRunner.RunGetOut("systemctl", "list-units", "--state=failed", "--no-legend")
18561856
switch {
18571857
case err != nil:
18581858
klog.Errorf("Listing failed systemd services: %v", err)
@@ -2690,7 +2690,7 @@ func (dn *Daemon) getMachineConfigFromClusterIfNotSet(mc *mcfgv1.MachineConfig,
26902690
// validateKernelArguments checks that the current boot has all arguments specified
26912691
// in the target machineconfig.
26922692
func (dn *CoreOSDaemon) validateKernelArguments(currentConfig *mcfgv1.MachineConfig) error {
2693-
rpmostreeKargsBytes, err := runGetOut("rpm-ostree", "kargs")
2693+
rpmostreeKargsBytes, err := dn.cmdRunner.RunGetOut("rpm-ostree", "kargs")
26942694
if err != nil {
26952695
return err
26962696
}

pkg/daemon/on_disk_validation.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ func checkUnitEnabled(name string, expectedEnabled *bool) error {
241241
if expectedEnabled == nil {
242242
return nil
243243
}
244-
outBytes, _ := runGetOut("systemctl", "is-enabled", name)
244+
cmdRunner := &CommandRunnerOS{}
245+
outBytes, _ := cmdRunner.RunGetOut("systemctl", "is-enabled", name)
245246
out := strings.TrimSpace(string(outBytes))
246247

247248
switch {

pkg/daemon/podman.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type PodmanInterface interface {
3131
GetPodmanImageInfoByReference(reference string) (*PodmanImageInfo, error)
3232
// GetPodmanInfo retrieves Podman system information.
3333
GetPodmanInfo() (*PodmanInfo, error)
34+
// CreatePodmanContainer creates a container from the specified image URL.
35+
CreatePodmanContainer(additionalArgs []string, containerName, imgURL string) ([]byte, error)
3436
}
3537

3638
// PodmanExecInterface is the production implementation that executes real podman commands.
@@ -87,3 +89,15 @@ func (p *PodmanExecInterface) GetPodmanInfo() (*PodmanInfo, error) {
8789
}
8890
return &podmanInfo, nil
8991
}
92+
93+
// CreatePodmanContainer creates a container from the specified image URL.
94+
// It executes 'podman create <args> --name <containerName> <imgURL>'.
95+
func (p *PodmanExecInterface) CreatePodmanContainer(additionalArgs []string, containerName, imgURL string) ([]byte, error) {
96+
args := []string{"create"}
97+
if len(additionalArgs) > 0 {
98+
args = append(args, additionalArgs...)
99+
}
100+
args = append(args, "--name", containerName, imgURL)
101+
102+
return p.cmdRunner.RunGetOut("podman", args...)
103+
}

pkg/daemon/podman_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,85 @@ func TestGetPodmanInfo_InvalidJSON(t *testing.T) {
219219
assert.Nil(t, info)
220220
assert.Contains(t, err.Error(), "failed to decode podman system info output")
221221
}
222+
223+
// Assisted by: Cursor
224+
func TestCreatePodmanContainer_Success(t *testing.T) {
225+
containerName := "test-container"
226+
imgURL := "quay.io/openshift/test:latest"
227+
additionalArgs := []string{"--net=none", "--annotation=org.openshift.machineconfigoperator.pivot=true"}
228+
expectedOutput := []byte("container-id-12345")
229+
230+
mock := &MockCommandRunner{
231+
outputs: map[string][]byte{
232+
"podman create --net=none --annotation=org.openshift.machineconfigoperator.pivot=true --name test-container quay.io/openshift/test:latest": expectedOutput,
233+
},
234+
errors: map[string]error{},
235+
}
236+
237+
podman := NewPodmanExec(mock)
238+
output, err := podman.CreatePodmanContainer(additionalArgs, containerName, imgURL)
239+
240+
assert.Nil(t, err)
241+
assert.Equal(t, expectedOutput, output)
242+
}
243+
244+
// Assisted by: Cursor
245+
func TestCreatePodmanContainer_NoAdditionalArgs(t *testing.T) {
246+
containerName := "test-container"
247+
imgURL := "quay.io/openshift/test:latest"
248+
expectedOutput := []byte("container-id-67890")
249+
250+
mock := &MockCommandRunner{
251+
outputs: map[string][]byte{
252+
"podman create --name test-container quay.io/openshift/test:latest": expectedOutput,
253+
},
254+
errors: map[string]error{},
255+
}
256+
257+
podman := NewPodmanExec(mock)
258+
output, err := podman.CreatePodmanContainer(nil, containerName, imgURL)
259+
260+
assert.Nil(t, err)
261+
assert.Equal(t, expectedOutput, output)
262+
}
263+
264+
// Assisted by: Cursor
265+
func TestCreatePodmanContainer_EmptyAdditionalArgs(t *testing.T) {
266+
containerName := "test-container"
267+
imgURL := "quay.io/openshift/test:latest"
268+
expectedOutput := []byte("container-id-empty")
269+
270+
mock := &MockCommandRunner{
271+
outputs: map[string][]byte{
272+
"podman create --name test-container quay.io/openshift/test:latest": expectedOutput,
273+
},
274+
errors: map[string]error{},
275+
}
276+
277+
podman := NewPodmanExec(mock)
278+
output, err := podman.CreatePodmanContainer([]string{}, containerName, imgURL)
279+
280+
assert.Nil(t, err)
281+
assert.Equal(t, expectedOutput, output)
282+
}
283+
284+
// Assisted by: Cursor
285+
func TestCreatePodmanContainer_CommandError(t *testing.T) {
286+
containerName := "test-container"
287+
imgURL := "quay.io/openshift/test:latest"
288+
additionalArgs := []string{"--net=none"}
289+
290+
mock := &MockCommandRunner{
291+
outputs: map[string][]byte{},
292+
errors: map[string]error{
293+
"podman create --net=none --name test-container quay.io/openshift/test:latest": fmt.Errorf("podman create failed"),
294+
},
295+
}
296+
297+
podman := NewPodmanExec(mock)
298+
output, err := podman.CreatePodmanContainer(additionalArgs, containerName, imgURL)
299+
300+
assert.Error(t, err)
301+
assert.Nil(t, output)
302+
assert.Contains(t, err.Error(), "podman create failed")
303+
}

pkg/daemon/update.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ func setRunningKargsWithCmdline(config *mcfgv1.MachineConfig, requestedKargs []s
389389
return nil
390390
}
391391

392-
func setRunningKargs(config *mcfgv1.MachineConfig, requestedKargs []string) error {
393-
rpmostreeKargsBytes, err := runGetOut("rpm-ostree", "kargs")
392+
func (dn *Daemon) setRunningKargs(config *mcfgv1.MachineConfig, requestedKargs []string) error {
393+
rpmostreeKargsBytes, err := dn.cmdRunner.RunGetOut("rpm-ostree", "kargs")
394394
if err != nil {
395395
return err
396396
}
@@ -469,14 +469,15 @@ func pullExtensionsImage(podmanInterface PodmanInterface, imgURL string) error {
469469
return err
470470
}
471471

472-
func podmanCopy(imgURL, osImageContentDir string) (err error) {
472+
func podmanCopy(podmanInterface PodmanInterface, imgURL, osImageContentDir string) (err error) {
473473
// make sure that osImageContentDir doesn't exist
474474
os.RemoveAll(osImageContentDir)
475475

476476
// create a container
477477
var cidBuf []byte
478478
containerName := pivottypes.PivotNamePrefix + string(uuid.NewUUID())
479-
cidBuf, err = runGetOut("podman", "create", "--net=none", "--annotation=org.openshift.machineconfigoperator.pivot=true", "--name", containerName, imgURL)
479+
additionalArgs := []string{"--net=none", "--annotation=org.openshift.machineconfigoperator.pivot=true"}
480+
cidBuf, err = podmanInterface.CreatePodmanContainer(additionalArgs, containerName, imgURL)
480481
if err != nil {
481482
return
482483
}
@@ -517,7 +518,7 @@ func (dn *CoreOSDaemon) ExtractExtensionsImage(imgURL string) (osExtensionsImage
517518
return osExtensionsImageContentDir, fmt.Errorf("error pulling extensions image %s: %w", imgURL, err)
518519
}
519520
// Extract the image using `podman cp`
520-
return osExtensionsImageContentDir, podmanCopy(imgURL, osExtensionsImageContentDir)
521+
return osExtensionsImageContentDir, podmanCopy(dn.podmanInterface, imgURL, osExtensionsImageContentDir)
521522
}
522523

523524
// Remove pending deployment on OSTree based system

pkg/daemon/update_test.go

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -687,28 +687,6 @@ func checkIrreconcilableResults(t *testing.T, key string, reconcilableError erro
687687
}
688688
}
689689

690-
func TestRunGetOut(t *testing.T) {
691-
o, err := runGetOut("true")
692-
assert.Nil(t, err)
693-
assert.Equal(t, len(o), 0)
694-
695-
o, err = runGetOut("false")
696-
assert.NotNil(t, err)
697-
698-
o, err = runGetOut("echo", "hello")
699-
assert.Nil(t, err)
700-
assert.Equal(t, string(o), "hello\n")
701-
702-
// base64 encode "oops" so we can't match on the command arguments
703-
o, err = runGetOut("/bin/sh", "-c", "echo hello; echo b29wcwo= | base64 -d 1>&2; exit 1")
704-
assert.Error(t, err)
705-
errtext := err.Error()
706-
assert.Contains(t, errtext, "exit status 1\noops\n")
707-
708-
o, err = runGetOut("/usr/bin/test-failure-to-exec-this-should-not-exist", "arg")
709-
assert.Error(t, err)
710-
}
711-
712690
// TestOriginalFileBackupRestore tests backikg up and restoring original files (files that are present in the base image and
713691
// get overwritten by a machine configuration)
714692
func TestOriginalFileBackupRestore(t *testing.T) {
@@ -942,3 +920,57 @@ func TestGenerateExtensionsArgs(t *testing.T) {
942920
})
943921
}
944922
}
923+
924+
// MockPodmanInterface for testing podmanCopy function
925+
type MockPodmanInterface struct {
926+
createContainerFunc func(additionalArgs []string, containerName, imgURL string) ([]byte, error)
927+
}
928+
929+
func (m *MockPodmanInterface) GetPodmanImageInfoByReference(reference string) (*PodmanImageInfo, error) {
930+
return nil, fmt.Errorf("not implemented in mock")
931+
}
932+
933+
func (m *MockPodmanInterface) GetPodmanInfo() (*PodmanInfo, error) {
934+
return nil, fmt.Errorf("not implemented in mock")
935+
}
936+
937+
func (m *MockPodmanInterface) CreatePodmanContainer(additionalArgs []string, containerName, imgURL string) ([]byte, error) {
938+
if m.createContainerFunc != nil {
939+
return m.createContainerFunc(additionalArgs, containerName, imgURL)
940+
}
941+
return []byte("test-container-id"), nil
942+
}
943+
944+
// Assisted by: Cursor
945+
func TestPodmanCopy_CreateContainerCall(t *testing.T) {
946+
imgURL := "quay.io/openshift/test:latest"
947+
osImageContentDir := "/tmp/test-content"
948+
949+
// Track the call to CreatePodmanContainer
950+
var capturedArgs []string
951+
var capturedContainerName string
952+
var capturedImgURL string
953+
954+
mockPodman := &MockPodmanInterface{
955+
createContainerFunc: func(additionalArgs []string, containerName, imgURL string) ([]byte, error) {
956+
capturedArgs = additionalArgs
957+
capturedContainerName = containerName
958+
capturedImgURL = imgURL
959+
return []byte("test-container-id-123"), nil
960+
},
961+
}
962+
963+
// Note: This test will fail at podmanRemove since we're only testing the interface call
964+
// The function will return an error, but we can verify the CreatePodmanContainer was called correctly
965+
err := podmanCopy(mockPodman, imgURL, osImageContentDir)
966+
967+
// Verify the CreatePodmanContainer was called with correct parameters
968+
expectedArgs := []string{"--net=none", "--annotation=org.openshift.machineconfigoperator.pivot=true"}
969+
assert.Equal(t, expectedArgs, capturedArgs, "CreatePodmanContainer should be called with correct additional args")
970+
assert.Contains(t, capturedContainerName, "pivot-", "Container name should contain pivot prefix")
971+
assert.Equal(t, imgURL, capturedImgURL, "Image URL should match")
972+
973+
// The function will error at podmanRemove, but that's expected in this unit test
974+
// We're only testing that the CreatePodmanContainer interface is called correctly
975+
assert.Error(t, err, "Expected error from podmanRemove since we're not mocking that part")
976+
}

0 commit comments

Comments
 (0)