Skip to content

Commit 06853cd

Browse files
authored
feat(mock): expose NoPreClean option for mock roots (#324)
* Extend mock runner to be able to request skipping pre-clean of a preexisting root * Fix bug where Clone() hadn't been copying all fields of the runner object * Add further clarification to progress strings
1 parent 8b8e792 commit 06853cd

2 files changed

Lines changed: 130 additions & 3 deletions

File tree

internal/rpm/mock/mock.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/kballard/go-shellquote"
1919
"github.com/microsoft/azldev/internal/global/opctx"
2020
"github.com/microsoft/azldev/internal/utils/hostinfo"
21+
"github.com/samber/lo"
2122
)
2223

2324
const (
@@ -46,6 +47,10 @@ type Runner struct {
4647
bindMounts []bindMountRequest
4748
enableNetwork bool
4849

50+
// noPreClean requests that the root *not* be cleaned on command execution, even if mock deems
51+
// it ought to be.
52+
noPreClean bool
53+
4954
// baseDir is the base directory where mock will create all of its root directories. If not set,
5055
// it defaults to the host system default (typically, /var/lib/mock).
5156
baseDir string // optional
@@ -83,6 +88,9 @@ func (r *Runner) Clone() *Runner {
8388
mockConfigPath: r.mockConfigPath,
8489
bindMounts: deep.MustCopy(r.bindMounts),
8590
enableNetwork: r.enableNetwork,
91+
noPreClean: r.noPreClean,
92+
baseDir: r.baseDir,
93+
rootDir: r.rootDir,
8694
}
8795
}
8896

@@ -97,13 +105,39 @@ func (r *Runner) AddBindMount(hostPath, mockRootPath string) *Runner {
97105
return r
98106
}
99107

108+
// BindMounts retrieves the set of bind mounts configured for this [Runner], expressed as
109+
// a map from host path to mock root path.
110+
func (r *Runner) BindMounts() map[string]string {
111+
return lo.SliceToMap(r.bindMounts, func(item bindMountRequest) (string, string) {
112+
return item.hostPath, item.mockRootPath
113+
})
114+
}
115+
100116
// Updates the [Runner]'s configuration to enable external network access from within the mock root.
101117
func (r *Runner) EnableNetwork() *Runner {
102118
r.enableNetwork = true
103119

104120
return r
105121
}
106122

123+
// HasNetworkEnabled indicates whether the [Runner] is configured to enable network access.
124+
func (r *Runner) HasNetworkEnabled() bool {
125+
return r.enableNetwork
126+
}
127+
128+
// WithNoPreClean updates the [Runner]'s configuration to ensure that mock does *not* pre-clean
129+
// the root when invoking an operation.
130+
func (r *Runner) WithNoPreClean() *Runner {
131+
r.noPreClean = true
132+
133+
return r
134+
}
135+
136+
// HasNoPreClean indicates whether the [Runner] is configured to avoid pre-cleaning the root.
137+
func (r *Runner) HasNoPreClean() bool {
138+
return r.noPreClean
139+
}
140+
107141
// WithBaseDir updates the [Runner]'s configuration to set which directory mock roots are created
108142
// under by default. If not set, mock will write under its default base (/var/lib/mock).
109143
func (r *Runner) WithBaseDir(baseDir string) *Runner {
@@ -112,6 +146,11 @@ func (r *Runner) WithBaseDir(baseDir string) *Runner {
112146
return r
113147
}
114148

149+
// BaseDir retrieves the path to the base directory used by this [Runner] for mock roots.
150+
func (r *Runner) BaseDir() string {
151+
return r.baseDir
152+
}
153+
115154
// WithRootDir update's the [Runner]'s configuration to set which directory the root is created
116155
// under.
117156
func (r *Runner) WithRootDir(rootDir string) *Runner {
@@ -120,6 +159,11 @@ func (r *Runner) WithRootDir(rootDir string) *Runner {
120159
return r
121160
}
122161

162+
// RootDir retrieves the path to the mock root used by this [Runner].
163+
func (r *Runner) RootDir() string {
164+
return r.rootDir
165+
}
166+
123167
// Retrieves the path to the mock .cfg file used by this [Runner].
124168
func (r *Runner) ConfigPath() string {
125169
return r.mockConfigPath
@@ -147,7 +191,7 @@ func (r *Runner) InitRoot(ctx context.Context) (err error) {
147191
}
148192

149193
if !r.verbose {
150-
extcmd.SetLongRunning("Waiting for mock...")
194+
extcmd.SetLongRunning("Waiting for mock (initializing build root)...")
151195
}
152196

153197
err = extcmd.Run(ctx)
@@ -226,7 +270,7 @@ func (r *Runner) BuildSRPM(
226270
}
227271

228272
if !r.verbose {
229-
extcmd.SetLongRunning("Waiting for mock...")
273+
extcmd.SetLongRunning("Waiting for mock (building SRPM)...")
230274
}
231275

232276
// Watch output logs in real-time so we can asynchronously synthesize progress updates.
@@ -314,7 +358,7 @@ func (r *Runner) BuildRPM(ctx context.Context, srpmPath, outputDirPath string, o
314358
return fmt.Errorf("failed to create external command for mock: %w", err)
315359
}
316360

317-
extcmd = extcmd.SetLongRunning("Waiting for mock...")
361+
extcmd = extcmd.SetLongRunning("Waiting for mock (building RPM)...")
318362

319363
// Watch output logs in real-time so we can asynchronously synthesize progress updates.
320364
err = addMockLogListeners(r.eventListener, extcmd, outputDirPath)
@@ -457,6 +501,8 @@ func (r *Runner) ScrubRoot(ctx context.Context) error {
457501
return fmt.Errorf("failed to create external command for mock: %w", err)
458502
}
459503

504+
extcmd = extcmd.SetLongRunning("Waiting for mock (cleaning build root)...")
505+
460506
err = extcmd.Run(ctx)
461507
if err != nil {
462508
return fmt.Errorf("mock failed to scrub root: %w", err)
@@ -493,6 +539,10 @@ func (r *Runner) getBaseArgs() (args []string) {
493539
args = append(args, "--enable-network")
494540
}
495541

542+
if r.noPreClean {
543+
args = append(args, "--no-clean")
544+
}
545+
496546
if len(r.bindMounts) > 0 {
497547
pairs := []string{}
498548

internal/rpm/mock/mock_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,48 @@ func TestMockNotPresent(t *testing.T) {
5353
require.Error(t, err)
5454
}
5555

56+
func TestRunnerDefaults(t *testing.T) {
57+
ctx := newTestCtxWithMockPrereqsPresent()
58+
runner := mock.NewRunner(ctx, testMockConfigPath)
59+
60+
// Pin defaults.
61+
assert.Equal(t, testMockConfigPath, runner.ConfigPath())
62+
assert.Empty(t, runner.BindMounts())
63+
assert.False(t, runner.HasNetworkEnabled())
64+
assert.False(t, runner.HasNoPreClean())
65+
assert.Empty(t, runner.BaseDir())
66+
assert.Empty(t, runner.RootDir())
67+
}
68+
69+
func TestClone(t *testing.T) {
70+
const (
71+
testBaseDir = "/base"
72+
testRootDir = "/root"
73+
testHostPath = "/host"
74+
testGuestPath = "/guest"
75+
)
76+
77+
ctx := newTestCtxWithMockPrereqsPresent()
78+
runner := mock.NewRunner(ctx, testMockConfigPath)
79+
80+
// Set a number of non-default options so we can confirm they propagate to the clone.
81+
runner.AddBindMount(testHostPath, testGuestPath)
82+
runner.WithBaseDir(testBaseDir)
83+
runner.WithRootDir(testRootDir)
84+
runner.WithNoPreClean()
85+
runner.EnableNetwork()
86+
87+
clone := runner.Clone()
88+
89+
// Confirm that the clone matches the original runner.
90+
assert.Equal(t, testMockConfigPath, clone.ConfigPath())
91+
assert.Equal(t, map[string]string{testHostPath: testGuestPath}, clone.BindMounts())
92+
assert.True(t, clone.HasNetworkEnabled())
93+
assert.True(t, clone.HasNoPreClean())
94+
assert.Equal(t, testBaseDir, clone.BaseDir())
95+
assert.Equal(t, testRootDir, clone.RootDir())
96+
}
97+
5698
func TestGetRootPath(t *testing.T) {
5799
ctx := newTestCtxWithMockPrereqsPresent()
58100

@@ -187,13 +229,18 @@ func TestCmdInChroot_Success(t *testing.T) {
187229

188230
// Make sure our args end up in the command line (with quoting as needed).
189231
assert.Contains(t, cmd.GetArgs(), "arg1 'arg2 with spaces'")
232+
233+
// Ensure we *don't* see unexpected options.
234+
assert.NotContains(t, cmd.GetArgs(), "--no-clean")
235+
assert.NotContains(t, cmd.GetArgs(), "--plugin-option=bind_mount")
190236
}
191237

192238
func TestCmdInChroot_BindMount(t *testing.T) {
193239
ctx := newTestCtxWithMockPrereqsPresent()
194240

195241
runner := mock.NewRunner(ctx, testMockConfigPath)
196242
runner.AddBindMount("/host-path", "/mock-path")
243+
assert.Equal(t, map[string]string{"/host-path": "/mock-path"}, runner.BindMounts())
197244

198245
cmd, err := runner.CmdInChroot(ctx, []string{"arg"}, false /*interactive*/)
199246
require.NoError(t, err)
@@ -202,3 +249,33 @@ func TestCmdInChroot_BindMount(t *testing.T) {
202249
// Look for bind-mount arg.
203250
assert.Contains(t, cmd.GetArgs(), `--plugin-option=bind_mount:dirs=[("/host-path", "/mock-path")]`)
204251
}
252+
253+
func TestCmdInChroot_NoPreClean(t *testing.T) {
254+
ctx := newTestCtxWithMockPrereqsPresent()
255+
256+
runner := mock.NewRunner(ctx, testMockConfigPath)
257+
runner.WithNoPreClean()
258+
assert.True(t, runner.HasNoPreClean())
259+
260+
cmd, err := runner.CmdInChroot(ctx, []string{"arg"}, false /*interactive*/)
261+
require.NoError(t, err)
262+
require.NotNil(t, cmd)
263+
264+
// Look for no-clean arg.
265+
assert.Contains(t, cmd.GetArgs(), "--no-clean")
266+
}
267+
268+
func TestCmdInChroot_EnableNetworking(t *testing.T) {
269+
ctx := newTestCtxWithMockPrereqsPresent()
270+
271+
runner := mock.NewRunner(ctx, testMockConfigPath)
272+
runner.EnableNetwork()
273+
assert.True(t, runner.HasNetworkEnabled())
274+
275+
cmd, err := runner.CmdInChroot(ctx, []string{"arg"}, false /*interactive*/)
276+
require.NoError(t, err)
277+
require.NotNil(t, cmd)
278+
279+
// Look for enable-network arg.
280+
assert.Contains(t, cmd.GetArgs(), "--enable-network")
281+
}

0 commit comments

Comments
 (0)