Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR-5404 - The 'fstab' of 'customization' destroy original fstab content #143

Merged
merged 16 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ jobs:
runs-on: ubuntu-latest
name: documentation-check
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get info on if image definition has changed
id: changed-files-image-def
uses: tj-actions/changed-files@v32
uses: tj-actions/changed-files@v39
with:
files: |
internal/imagedefinition/image_definition.go

- name: Get info on if image definition README was updated
id: changed-files-image-def-readme
uses: tj-actions/changed-files@v32
uses: tj-actions/changed-files@v39
with:
files: |
internal/imagedefinition/README.rst
Expand All @@ -30,24 +30,24 @@ jobs:
run: |
echo "Struct has changed"
- name: test struct README
if: steps.changed-files-image-def-reamde.outputs.any_changed != 'true'
if: steps.changed-files-image-def-readme.outputs.any_changed != 'true'
run: |
echo "README has not changed"
- name: Fail if image definition README has not been updated
if: steps.changed-files-image-def.outputs.any_changed == 'true' && steps.changed-files-image-def-readme.outputs.any_changed != true
if: steps.changed-files-image-def.outputs.any_changed == 'true' && steps.changed-files-image-def-readme.outputs.any_changed != 'true'
run: |
echo "Image Definition struct has changed but README was not updated"
exit 1

- name: Get info on if command options or flags have changed
id: changed-files-flags
uses: tj-actions/changed-files@v32
uses: tj-actions/changed-files@v39
with:
files: |
internal/commands/*

- name: Fail if command line args have changed but manpage has not been updated
if: steps.changed-files-flags.outputs.any_changed == 'true' && contains(steps.changed-files-flags.outputs.changed_files, 'ubuntu-image.rst') != true
if: steps.changed-files-flags.outputs.any_changed == 'true' && contains(steps.changed-files-flags.outputs.changed_files, 'ubuntu-image.rst') != 'true'
run: |
echo "Command line flags have been updated but the manpage has not"
exit 1
5 changes: 5 additions & 0 deletions cmd/ubuntu-image/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func executeStateMachine(sm statemachine.SmInterface) error {
// unhidePackOpts make pack options visible in help if the pack command is used
// This should be removed when the pack command is made visible to everyone
func unhidePackOpts(parser *flags.Parser) {
// Save given options before removing them temporarily
// otherwise the help will be displayed twice
opts := parser.Options
parser.Options = 0
defer func() { parser.Options = opts }()
// parse once to determine the active command
// we do not care about error here since we will reparse again
_, _ = parser.Parse()
Expand Down
1 change: 1 addition & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ ubuntu-image (3.0+23.04ubuntu1) UNRELEASED; urgency=medium
* Add experimental ubuntu-image 'pack' support
* Support the keep-enabled parameter in ubuntu-image extra-ppas
* Clean image build leftovers
* Make sure the fstab file is cleanly overridden (LP: #2031889)

[ Alfonso Sanchez-Beato ]
* Update to latest snapd, adapting to changes in layouts code.
Expand Down
10 changes: 6 additions & 4 deletions internal/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,16 @@ func SetDefaults(needsDefaults interface{}) error {
}
// special case for pointer to bools
} else if field.Type().Elem() == reflect.TypeOf(true) {
// make sure the pointer is never nil in case no value
// was set
if field.IsNil() {
field.Set(reflect.ValueOf(BoolPtr(false)))
// if a value is set, do nothing
if !field.IsNil() {
continue
}
tags := elem.Type().Field(i).Tag
defaultValue, hasDefault := tags.Lookup("default")
if !hasDefault {
// If no default and no value is set, make sure we have a valid
// value consistent with the "zero" value for a bool (false)
field.Set(reflect.ValueOf(BoolPtr(false)))
continue
}
if defaultValue == "true" {
Expand Down
2 changes: 1 addition & 1 deletion internal/helper/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func TestSetDefaults(t *testing.T) {
},
want: &S2{
A: "test",
B: BoolPtr(true),
B: BoolPtr(false),
C: BoolPtr(false),
D: BoolPtr(true),
},
Expand Down
1 change: 1 addition & 0 deletions internal/imagedefinition/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ The following specification defines what is supported in the YAML:
# ubuntu-image will support creating many different types of
# artifacts, including the actual images, manifest files,
# changelogs, and a list of files in the rootfs.
# Set a custom fstab. The existing one (if any) will be truncated.
fstab: (optional)
-
# the value of LABEL= for the fstab entry
Expand Down
91 changes: 69 additions & 22 deletions internal/statemachine/classic_states.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
"github.com/canonical/ubuntu-image/internal/imagedefinition"
)

var seedVersionRegex = regexp.MustCompile(`^[a-z0-9].*`)
var localePresentRegex = regexp.MustCompile(`(?m)^LANG=|LC_[A-Z_]+=`)
var (
seedVersionRegex = regexp.MustCompile(`^[a-z0-9].*`)
localePresentRegex = regexp.MustCompile(`(?m)^LANG=|LC_[A-Z_]+=`)
)

// parseImageDefinition parses the provided yaml file and ensures it is valid
func (stateMachine *StateMachine) parseImageDefinition() error {
Expand Down Expand Up @@ -1014,9 +1016,9 @@ func (stateMachine *StateMachine) customizeCloudInit() error {
func (stateMachine *StateMachine) customizeFstab() error {
classicStateMachine := stateMachine.parent.(*ClassicStateMachine)

// open /etc/fstab for writing
fstabIO, err := osOpenFile(filepath.Join(stateMachine.tempDirs.chroot, "etc", "fstab"),
os.O_CREATE|os.O_WRONLY, 0644)
fstabPath := filepath.Join(stateMachine.tempDirs.chroot, "etc", "fstab")

fstabIO, err := osOpenFile(fstabPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return fmt.Errorf("Error opening fstab: %s", err.Error())
}
Expand All @@ -1040,6 +1042,7 @@ func (stateMachine *StateMachine) customizeFstab() error {
)
fstabEntries = append(fstabEntries, fstabEntry)
}

_, err = fstabIO.Write([]byte(strings.Join(fstabEntries, "\n") + "\n"))

return err
Expand Down Expand Up @@ -1289,24 +1292,68 @@ func (stateMachine *StateMachine) populateClassicRootfsContents() error {
}
}

if classicStateMachine.ImageDef.Customization != nil {
if len(classicStateMachine.ImageDef.Customization.Fstab) == 0 {
fstabPath := filepath.Join(classicStateMachine.tempDirs.rootfs, "etc", "fstab")
fstabBytes, err := osReadFile(fstabPath)
if err == nil {
if !strings.Contains(string(fstabBytes), "LABEL=writable") {
re := regexp.MustCompile(`(?m:^LABEL=\S+\s+/\s+(.*)$)`)
newContents := re.ReplaceAll(fstabBytes, []byte("LABEL=writable\t/\t$1"))
if !strings.Contains(string(newContents), "LABEL=writable") {
newContents = []byte("LABEL=writable / ext4 defaults 0 0\n")
}
err := osWriteFile(fstabPath, newContents, 0644)
if err != nil {
return fmt.Errorf("Error writing to fstab: %s", err.Error())
}
}
}
if classicStateMachine.ImageDef.Customization == nil {
return nil
}

return classicStateMachine.fixFstab()
}

// fixFstab makes sure the fstab contains a valid entry for the root mount point
func (stateMachine *StateMachine) fixFstab() error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on having this be a separate state, thanks!

classicStateMachine := stateMachine.parent.(*ClassicStateMachine)

if len(classicStateMachine.ImageDef.Customization.Fstab) != 0 {
return nil
}

fstabPath := filepath.Join(classicStateMachine.tempDirs.rootfs, "etc", "fstab")
fstabBytes, err := osReadFile(fstabPath)
if err != nil {
return fmt.Errorf("Error reading fstab: %s", err.Error())
}

rootMountFound := false
newLines := make([]string, 0)
rootFSLabel := "writable"
rootFSOptions := "discard,errors=remount-ro"
fsckOrder := "1"

lines := strings.Split(string(fstabBytes), "\n")
for _, l := range lines {
if l == "# UNCONFIGURED FSTAB" {
// omit this line if still present
continue
}

if strings.HasPrefix(l, "#") {
newLines = append(newLines, l)
continue
}

entry := strings.Fields(l)
if len(entry) < 6 {
// ignore invalid fstab entry
continue
}

if entry[1] == "/" && !rootMountFound {
entry[0] = "LABEL=" + rootFSLabel
entry[3] = rootFSOptions
entry[5] = fsckOrder

rootMountFound = true
}
newLines = append(newLines, strings.Join(entry, "\t"))
}

if !rootMountFound {
newLines = append(newLines, fmt.Sprintf("LABEL=%s / ext4 %s 0 %s", rootFSLabel, rootFSOptions, fsckOrder))
}

err = osWriteFile(fstabPath, []byte(strings.Join(newLines, "\n")+"\n"), 0644)
if err != nil {
return fmt.Errorf("Error writing to fstab: %s", err.Error())
}
return nil
}
Expand Down
Loading
Loading