Skip to content

Commit b8c527c

Browse files
committed
Fix: Handle missing GRUB variables in /etc/default/grub gracefully
1 parent 5592ee2 commit b8c527c

2 files changed

Lines changed: 74 additions & 9 deletions

File tree

toolkit/tools/pkg/imagecustomizerlib/defaultgrubutils.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package imagecustomizerlib
55

66
import (
7+
"errors"
78
"fmt"
89
"path/filepath"
910
"strings"
@@ -33,6 +34,8 @@ const (
3334
defaultGrubFileVarNameCmdlineForSELinux = defaultGrubFileVarNameCmdlineLinux
3435
)
3536

37+
var errDefaultGrubFileVarNotFound = errors.New("default grub variable not found")
38+
3639
type defaultGrubFileVarAssign struct {
3740
Token grub.Token
3841
Line grub.Line
@@ -122,7 +125,8 @@ func findDefaultGrubFileVarAssign(varAssigns []defaultGrubFileVarAssign, name de
122125
}
123126
}
124127

125-
err := fmt.Errorf("failed to find %s variable assignment (%s)", installutils.GrubDefFile, name)
128+
err := fmt.Errorf("failed to find %s variable assignment (%s): %w", installutils.GrubDefFile, name,
129+
errDefaultGrubFileVarNotFound)
126130
return defaultGrubFileVarAssign{}, err
127131
}
128132

@@ -211,7 +215,7 @@ func GetDefaultGrubFileLinuxArgsFromMultipleVars(defaultGrubFileContent string)
211215

212216
// Takes the string contents of /etc/default/grub file and inserts the provided command-line args.
213217
func addExtraCommandLineToDefaultGrubFile(defaultGrubFileContent string, extraCommandLine string) (string, error) {
214-
cmdLineVarAssign, _, insertAt, err := GetDefaultGrubFileLinuxArgs(defaultGrubFileContent,
218+
cmdLineVarAssign, _, insertAt, err := getDefaultGrubFileLinuxArgsOrEmpty(defaultGrubFileContent,
215219
defaultGrubFileVarNameCmdlineLinuxDefault)
216220
if err != nil {
217221
return "", err
@@ -222,9 +226,23 @@ func addExtraCommandLineToDefaultGrubFile(defaultGrubFileContent string, extraCo
222226
// Add the extra command-line args.
223227
argsString = argsString[:insertAt] + " " + extraCommandLine + " " + argsString[insertAt:]
224228

225-
// Rewrite GRUB_CMDLINE_LINUX_DEFAULT line.
226-
defaultGrubFileContent = replaceDefaultGrubFileVarAssign(defaultGrubFileContent, cmdLineVarAssign, argsString)
227-
return defaultGrubFileContent, nil
229+
// Write the variable, creating it if it doesn't exist.
230+
return UpdateDefaultGrubFileVariable(defaultGrubFileContent, string(defaultGrubFileVarNameCmdlineLinuxDefault), argsString)
231+
}
232+
233+
// getDefaultGrubFileLinuxArgsOrEmpty is like GetDefaultGrubFileLinuxArgs but treats a missing variable as having an
234+
// empty value instead of returning an error. Real parse errors are still returned.
235+
func getDefaultGrubFileLinuxArgsOrEmpty(defaultGrubFileContent string, varName defaultGrubFileVarName,
236+
) (defaultGrubFileVarAssign, []grubConfigLinuxArg, int, error) {
237+
cmdLineVarAssign, args, insertAt, err := GetDefaultGrubFileLinuxArgs(defaultGrubFileContent, varName)
238+
if errors.Is(err, errDefaultGrubFileVarNotFound) {
239+
// Variable doesn't exist — treat as empty.
240+
return defaultGrubFileVarAssign{}, nil, 0, nil
241+
} else if err != nil {
242+
return defaultGrubFileVarAssign{}, nil, 0, err
243+
}
244+
245+
return cmdLineVarAssign, args, insertAt, nil
228246
}
229247

230248
// Takes the string contents of the /etc/default/grub file and replaces a set of command-line args.
@@ -241,7 +259,7 @@ func addExtraCommandLineToDefaultGrubFile(defaultGrubFileContent string, extraCo
241259
func updateDefaultGrubFileKernelCommandLineArgs(defaultGrubFileContent string, varName defaultGrubFileVarName,
242260
argsToRemove []string, newArgs []string,
243261
) (string, error) {
244-
cmdLineVarAssign, args, insertAt, err := GetDefaultGrubFileLinuxArgs(defaultGrubFileContent, varName)
262+
cmdLineVarAssign, args, insertAt, err := getDefaultGrubFileLinuxArgsOrEmpty(defaultGrubFileContent, varName)
245263
if err != nil {
246264
return "", err
247265
}
@@ -252,9 +270,8 @@ func updateDefaultGrubFileKernelCommandLineArgs(defaultGrubFileContent string, v
252270
return "", err
253271
}
254272

255-
// Rewrite GRUB_CMDLINE_LINUX line.
256-
defaultGrubFileContent = replaceDefaultGrubFileVarAssign(defaultGrubFileContent, cmdLineVarAssign, value)
257-
return defaultGrubFileContent, nil
273+
// Write the variable, creating it if it doesn't exist.
274+
return UpdateDefaultGrubFileVariable(defaultGrubFileContent, string(varName), value)
258275
}
259276

260277
// Takes the string contents of the /etc/default/grub file and rewrites one of the variable assignments lines.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package imagecustomizerlib
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestUpdateDefaultGrubFileKernelCommandLineArgsMissingVar(t *testing.T) {
13+
// Empty file: GRUB_CMDLINE_LINUX_DEFAULT does not exist.
14+
content := ""
15+
16+
result, err := updateDefaultGrubFileKernelCommandLineArgs(content,
17+
defaultGrubFileVarNameCmdlineLinuxDefault,
18+
nil, []string{"security=selinux", "selinux=1"})
19+
assert.NoError(t, err)
20+
assert.Equal(t, "GRUB_CMDLINE_LINUX_DEFAULT=\" security=selinux selinux=1 \"\n", result)
21+
}
22+
23+
func TestUpdateDefaultGrubFileKernelCommandLineArgsExistingVar(t *testing.T) {
24+
content := `GRUB_CMDLINE_LINUX_DEFAULT="rd.auto=1"` + "\n"
25+
26+
result, err := updateDefaultGrubFileKernelCommandLineArgs(content,
27+
defaultGrubFileVarNameCmdlineLinuxDefault,
28+
nil, []string{"security=selinux"})
29+
assert.NoError(t, err)
30+
assert.Equal(t, "GRUB_CMDLINE_LINUX_DEFAULT=\"rd.auto=1 security=selinux \"\n", result)
31+
}
32+
33+
func TestAddExtraCommandLineToDefaultGrubFileMissingVar(t *testing.T) {
34+
// Empty file: GRUB_CMDLINE_LINUX_DEFAULT does not exist.
35+
content := ""
36+
37+
result, err := addExtraCommandLineToDefaultGrubFile(content, "console=tty0 console=ttyS0")
38+
assert.NoError(t, err)
39+
assert.Equal(t, "GRUB_CMDLINE_LINUX_DEFAULT=\" console=tty0 console=ttyS0 \"\n", result)
40+
}
41+
42+
func TestAddExtraCommandLineToDefaultGrubFileExistingVar(t *testing.T) {
43+
content := `GRUB_CMDLINE_LINUX_DEFAULT=" $kernelopts"` + "\n"
44+
45+
result, err := addExtraCommandLineToDefaultGrubFile(content, "console=tty0")
46+
assert.NoError(t, err)
47+
assert.Equal(t, "GRUB_CMDLINE_LINUX_DEFAULT=\" console=tty0 \\$kernelopts\"\n", result)
48+
}

0 commit comments

Comments
 (0)