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

Add excludeFromBackup on macOS #2159

Closed
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
6 changes: 6 additions & 0 deletions examples/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ rosetta:
# 🟢 Builtin default: false
binfmt: null

# Exclude from backup: "all", "disks" or "none"
# Specify which file in the instance's configuration to exclude from backup.
# Only available on macOS.
# 🟢 Builtin default: "disks"
excludeFromBackup: null

# Specify the timezone name (as used by the zoneinfo database). Specify the empty string
# to not set a timezone in the instance.
# 🟢 Builtin default: use name from /etc/timezone or deduce from symlink target of /etc/localtime
Expand Down
14 changes: 14 additions & 0 deletions pkg/limayaml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,20 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
y.Rosetta.BinFmt = ptr.Of(false)
}

if runtime.GOOS == "darwin" {
if y.ExcludeFromBackup == nil {
y.ExcludeFromBackup = d.ExcludeFromBackup
}
if o.ExcludeFromBackup != nil {
y.ExcludeFromBackup = o.ExcludeFromBackup
}
if y.ExcludeFromBackup == nil {
y.ExcludeFromBackup = ptr.Of(ExcludeFromBackupDisks)
}
} else {
y.ExcludeFromBackup = ptr.Of(ExcludeFromBackupNone)
}

if y.Plain == nil {
y.Plain = d.Plain
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/limayaml/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ func TestFillDefault(t *testing.T) {
}
}
}
if runtime.GOOS == "darwin" {
builtin.ExcludeFromBackup = ptr.Of(ExcludeFromBackupDisks)
} else {
builtin.ExcludeFromBackup = ptr.Of(ExcludeFromBackupNone)
}

defaultPortForward := PortForward{
GuestIP: api.IPv4loopback1,
Expand Down Expand Up @@ -444,6 +449,11 @@ func TestFillDefault(t *testing.T) {
}
}
expect.Plain = ptr.Of(false)
if runtime.GOOS == "darwin" {
expect.ExcludeFromBackup = ptr.Of(ExcludeFromBackupDisks)
} else {
expect.ExcludeFromBackup = ptr.Of(ExcludeFromBackupNone)
}

y = LimaYAML{}
FillDefault(&y, &d, &LimaYAML{}, filePath)
Expand Down Expand Up @@ -608,6 +618,7 @@ func TestFillDefault(t *testing.T) {
Enabled: ptr.Of(false),
BinFmt: ptr.Of(false),
},
ExcludeFromBackup: ptr.Of(ExcludeFromBackupAll),
}

y = filledDefaults
Expand Down Expand Up @@ -661,6 +672,11 @@ func TestFillDefault(t *testing.T) {
BinFmt: ptr.Of(false),
}
expect.Plain = ptr.Of(false)
if runtime.GOOS == "darwin" {
expect.ExcludeFromBackup = ptr.Of(ExcludeFromBackupAll)
} else {
expect.ExcludeFromBackup = ptr.Of(ExcludeFromBackupNone)
}

FillDefault(&y, &d, &o, filePath)
assert.DeepEqual(t, &y, &expect, opts...)
Expand Down
19 changes: 14 additions & 5 deletions pkg/limayaml/limayaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ type LimaYAML struct {
DNS []net.IP `yaml:"dns,omitempty" json:"dns,omitempty"`
HostResolver HostResolver `yaml:"hostResolver,omitempty" json:"hostResolver,omitempty"`
// `useHostResolver` was deprecated in Lima v0.8.1, removed in Lima v0.14.0. Use `hostResolver.enabled` instead.
PropagateProxyEnv *bool `yaml:"propagateProxyEnv,omitempty" json:"propagateProxyEnv,omitempty"`
CACertificates CACertificates `yaml:"caCerts,omitempty" json:"caCerts,omitempty"`
Rosetta Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
Plain *bool `yaml:"plain,omitempty" json:"plain,omitempty"`
TimeZone *string `yaml:"timezone,omitempty" json:"timezone,omitempty"`
PropagateProxyEnv *bool `yaml:"propagateProxyEnv,omitempty" json:"propagateProxyEnv,omitempty"`
CACertificates CACertificates `yaml:"caCerts,omitempty" json:"caCerts,omitempty"`
Rosetta Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
ExcludeFromBackup *ExcludeFromBackup `yaml:"excludeFromBackup,omitempty" json:"excludeFromBackup,omitempty"`
Plain *bool `yaml:"plain,omitempty" json:"plain,omitempty"`
TimeZone *string `yaml:"timezone,omitempty" json:"timezone,omitempty"`
}

type (
Expand Down Expand Up @@ -260,6 +261,14 @@ type CACertificates struct {
Certs []string `yaml:"certs,omitempty" json:"certs,omitempty"`
}

type ExcludeFromBackup = string

const (
ExcludeFromBackupAll ExcludeFromBackup = "all"
ExcludeFromBackupDisks ExcludeFromBackup = "disks"
ExcludeFromBackupNone ExcludeFromBackup = "none"
)

// DEPRECATED types below

// Types have been renamed to turn all references to the old names into compiler errors,
Expand Down
10 changes: 10 additions & 0 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,16 @@ func Validate(y LimaYAML, warn bool) error {
if err := validateNetwork(y, warn); err != nil {
return err
}

switch *y.ExcludeFromBackup {
case ExcludeFromBackupAll, ExcludeFromBackupDisks, ExcludeFromBackupNone:
default:
return fmt.Errorf("field `excludeFromBackup` must be %q, %q, or %q; got %q", ExcludeFromBackupAll, ExcludeFromBackupDisks, ExcludeFromBackupNone, *y.ExcludeFromBackup)
}
if warn && runtime.GOOS != "darwin" && *y.ExcludeFromBackup != "none" {
logrus.Warn("field `excludeFromBackup` is only supported on macOS")
}

if warn {
warnExperimental(y)
}
Expand Down
38 changes: 38 additions & 0 deletions pkg/osutil/exclude_from_backup_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package osutil

/*
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c
#cgo darwin LDFLAGS: -lobjc -framework Foundation
#import <Foundation/Foundation.h>

void setExcludeFromBackup(const char *path, int exclude) {
@autoreleasepool {
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path]];
NSError *error = nil;
if (![url setResourceValue: exclude == 1 ? @YES : @NO forKey:NSURLIsExcludedFromBackupKey error:&error]) {
NSLog(@"setResourceValue failed: %@", error);
}
}
}
*/
import "C" //nolint:gocritic
/*
The reason for separating the import statement is that CGO only recognizes the standalone import of "C".
However, the linter tool `gocritic` incorrectly identifies this separation as a duplicate import and raises
a `dupImport` warning. To avoid this warning, the `gocritic` check has been disabled.
Ref: https://github.com/go-critic/go-critic/issues/845
*/
import (
"unsafe" //nolint:gocritic
)

// SetExcludeFromBackup sets the `NSURLIsExcludedFromBackupKey` attribute of the specified file or directory.
func SetExcludeFromBackup(path string, exclude bool) {
cs := C.CString(path)
defer C.free(unsafe.Pointer(cs))
if exclude {
C.setExcludeFromBackup(cs, C.int(1))
} else {
C.setExcludeFromBackup(cs, C.int(0))
}
}
5 changes: 5 additions & 0 deletions pkg/osutil/exclude_from_backup_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !darwin

package osutil

func SetExcludeFromBackup(path string, exclude bool) {}
21 changes: 21 additions & 0 deletions pkg/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,26 @@ func ensureNerdctlArchiveCache(y *limayaml.LimaYAML, created bool) (string, erro
return "", fileutils.Errors(errs)
}

// applyExcludeFromBackup sets the NSURLIsExcludedFromBackupKey attribute of the instance directory.
func applyExcludeFromBackup(instDir string, excludeFromBackup limayaml.ExcludeFromBackup) {
baseDisk := filepath.Join(instDir, filenames.BaseDisk)
diffDisk := filepath.Join(instDir, filenames.DiffDisk)
var targets map[string]bool
switch excludeFromBackup {
case limayaml.ExcludeFromBackupAll:
// Exclude the instance directory from Time Machine backups
targets = map[string]bool{instDir: true, baseDisk: false, diffDisk: false}
case limayaml.ExcludeFromBackupDisks:
// Exclude disk files from Time Machine backup.
targets = map[string]bool{instDir: false, baseDisk: true, diffDisk: true}
default: // limayaml.ExcludeFromBackupNone
targets = map[string]bool{instDir: false, baseDisk: false, diffDisk: false}
}
for target, exclude := range targets {
osutil.SetExcludeFromBackup(target, exclude)
}
}

type Prepared struct {
Driver driver.Driver
NerdctlArchiveCache string
Expand Down Expand Up @@ -108,6 +128,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) {
if err != nil {
return nil, err
}
applyExcludeFromBackup(inst.Dir, *y.ExcludeFromBackup)

return &Prepared{
Driver: limaDriver,
Expand Down
Loading