Skip to content

Commit 3f5a756

Browse files
authored
Merge pull request #280 from AkihiroSuda/custom-nerdctl-archive
yaml: support overriding the location of `nerdctl-full-X.Y.Z-linux-ARCH.tar.gz`
2 parents e31f365 + ab1a4f1 commit 3f5a756

File tree

7 files changed

+152
-82
lines changed

7 files changed

+152
-82
lines changed

cmd/limactl/info.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/lima-vm/lima/pkg/limayaml"
8+
"github.com/lima-vm/lima/pkg/version"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func newInfoCommand() *cobra.Command {
13+
infoCommand := &cobra.Command{
14+
Use: "info",
15+
Short: "Show diagnostic information",
16+
Args: cobra.NoArgs,
17+
RunE: infoAction,
18+
}
19+
return infoCommand
20+
}
21+
22+
type Info struct {
23+
Version string `json:"version"`
24+
DefaultTemplate *limayaml.LimaYAML `json:"defaultTemplate"`
25+
// TODO: add diagnostic info of QEMU
26+
}
27+
28+
func infoAction(cmd *cobra.Command, args []string) error {
29+
y, err := limayaml.Load(limayaml.DefaultTemplate, "")
30+
if err != nil {
31+
return err
32+
}
33+
info := &Info{
34+
Version: version.Version,
35+
DefaultTemplate: y,
36+
}
37+
j, err := json.MarshalIndent(info, "", " ")
38+
if err != nil {
39+
return err
40+
}
41+
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(j))
42+
return err
43+
}

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func newApp() *cobra.Command {
7979
newSudoersCommand(),
8080
newPruneCommand(),
8181
newHostagentCommand(),
82+
newInfoCommand(),
8283
)
8384
return rootCmd
8485
}

pkg/cidata/cidata.go

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,12 @@ import (
1717
"github.com/lima-vm/lima/pkg/limayaml"
1818
"github.com/lima-vm/lima/pkg/localpathutil"
1919
"github.com/lima-vm/lima/pkg/osutil"
20-
"github.com/lima-vm/lima/pkg/qemu/const"
20+
qemu "github.com/lima-vm/lima/pkg/qemu/const"
2121
"github.com/lima-vm/lima/pkg/sshutil"
2222
"github.com/lima-vm/lima/pkg/store/filenames"
23-
"github.com/opencontainers/go-digest"
2423
"github.com/sirupsen/logrus"
2524
)
2625

27-
const (
28-
NerdctlVersion = "0.12.0"
29-
)
30-
31-
var (
32-
NerdctlFullDigests = map[limayaml.Arch]digest.Digest{
33-
limayaml.X8664: "sha256:7789800cfdd19fa9eccadb5e4a911e4ba759799ad9ec0b7929c983b9d149bc98",
34-
limayaml.AARCH64: "sha256:ebb05e22ac6a3c25ac88ca4f747feed89bfae8e447a626d0fedf3b4f40ac3303",
35-
}
36-
)
37-
3826
func setupEnv(y *limayaml.LimaYAML) (map[string]string, error) {
3927
// Start with the proxy variables from the system settings.
4028
env, err := osutil.ProxySettings()
@@ -177,33 +165,32 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
177165
}
178166

179167
if args.Containerd.System || args.Containerd.User {
180-
var nftgzBase string
181-
switch y.Arch {
182-
case limayaml.X8664:
183-
nftgzBase = fmt.Sprintf("nerdctl-full-%s-linux-amd64.tar.gz", NerdctlVersion)
184-
case limayaml.AARCH64:
185-
nftgzBase = fmt.Sprintf("nerdctl-full-%s-linux-arm64.tar.gz", NerdctlVersion)
186-
default:
187-
return fmt.Errorf("unexpected arch %q", y.Arch)
168+
var nftgz *limayaml.File
169+
for i := range y.Containerd.Archives {
170+
f := &y.Containerd.Archives[i]
171+
if f.Arch != y.Arch {
172+
continue
173+
}
174+
nftgz = f
175+
}
176+
if nftgz == nil {
177+
return fmt.Errorf("no containerd archive was provided for arch %q", y.Arch)
188178
}
189179
td, err := ioutil.TempDir("", "lima-download-nerdctl")
190180
if err != nil {
191181
return err
192182
}
193183
defer os.RemoveAll(td)
194-
nftgzLocal := filepath.Join(td, nftgzBase)
195-
nftgzURL := fmt.Sprintf("https://github.com/containerd/nerdctl/releases/download/v%s/%s",
196-
NerdctlVersion, nftgzBase)
197-
nftgzDigest := NerdctlFullDigests[y.Arch]
198-
logrus.Infof("Downloading %q (%s)", nftgzURL, nftgzDigest)
199-
res, err := downloader.Download(nftgzLocal, nftgzURL, downloader.WithCache(), downloader.WithExpectedDigest(nftgzDigest))
184+
nftgzLocal := filepath.Join(td, "nerdctl-full.tgz")
185+
logrus.Infof("Downloading %q (%s)", nftgz.Location, nftgz.Digest)
186+
res, err := downloader.Download(nftgzLocal, nftgz.Location, downloader.WithCache(), downloader.WithExpectedDigest(nftgz.Digest))
200187
if err != nil {
201-
return fmt.Errorf("failed to download %q: %w", nftgzURL, err)
188+
return fmt.Errorf("failed to download %q: %w", nftgz.Location, err)
202189
}
203190
logrus.Debugf("res.ValidatedDigest=%v", res.ValidatedDigest)
204191
switch res.Status {
205192
case downloader.StatusDownloaded:
206-
logrus.Infof("Downloaded %q", nftgzBase)
193+
logrus.Infof("Downloaded %q", nftgz.Location)
207194
case downloader.StatusUsedCache:
208195
logrus.Infof("Using cache %q", res.CachePath)
209196
default:

pkg/limayaml/default.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ containerd:
7070
# Enable user-scoped (aka rootless) containerd and its dependencies
7171
# Default: true
7272
user: true
73+
# # Override containerd archive
74+
# # Default: hard-coded URL with hard-coded digest (see the output of `limactl info | jq .defaultTemplate.containerd.archives`)
75+
# archives:
76+
# - location: "~/Downloads/nerdctl-full-X.Y.Z-linux-amd64.tar.gz"
77+
# arch: "x86_64"
78+
# digest: "sha256:..."
7379

7480
# Provisioning scripts need to be idempotent because they might be called
7581
# multiple times, e.g. when the host VM is being restarted.

pkg/limayaml/defaults.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ import (
1111
"github.com/lima-vm/lima/pkg/osutil"
1212
)
1313

14+
func defaultContainerdArchives() []File {
15+
const nerdctlVersion = "0.12.0"
16+
location := func(goarch string) string {
17+
return "https://github.com/containerd/nerdctl/releases/download/v" + nerdctlVersion + "/nerdctl-full-" + nerdctlVersion + "-linux-" + goarch + ".tar.gz"
18+
}
19+
return []File{
20+
{
21+
Location: location("amd64"),
22+
Arch: X8664,
23+
Digest: "sha256:7789800cfdd19fa9eccadb5e4a911e4ba759799ad9ec0b7929c983b9d149bc98",
24+
},
25+
{
26+
Location: location("arm64"),
27+
Arch: AARCH64,
28+
Digest: "sha256:ebb05e22ac6a3c25ac88ca4f747feed89bfae8e447a626d0fedf3b4f40ac3303",
29+
},
30+
}
31+
}
32+
1433
func MACAddress(uniqueID string) string {
1534
sha := sha256.Sum256([]byte(osutil.MachineID() + uniqueID))
1635
// "5" is the magic number in the Lima ecosystem.
@@ -59,6 +78,15 @@ func FillDefault(y *LimaYAML, filePath string) {
5978
if y.Containerd.User == nil {
6079
y.Containerd.User = &[]bool{true}[0]
6180
}
81+
if len(y.Containerd.Archives) == 0 {
82+
y.Containerd.Archives = defaultContainerdArchives()
83+
}
84+
for i := range y.Containerd.Archives {
85+
f := &y.Containerd.Archives[i]
86+
if f.Arch == "" {
87+
f.Arch = y.Arch
88+
}
89+
}
6290
for i := range y.Probes {
6391
probe := &y.Probes[i]
6492
if probe.Mode == "" {
@@ -79,10 +107,10 @@ func FillDefault(y *LimaYAML, filePath string) {
79107
if len(y.Network.VDEDeprecated) > 0 && len(y.Networks) == 0 {
80108
for _, vde := range y.Network.VDEDeprecated {
81109
network := Network{
82-
Interface: vde.Name,
110+
Interface: vde.Name,
83111
MACAddress: vde.MACAddress,
84112
SwitchPort: vde.SwitchPort,
85-
VNL: vde.VNL,
113+
VNL: vde.VNL,
86114
}
87115
y.Networks = append(y.Networks, network)
88116
}

pkg/limayaml/limayaml.go

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@ import (
77
)
88

99
type LimaYAML struct {
10-
Arch Arch `yaml:"arch,omitempty"`
11-
Images []File `yaml:"images"` // REQUIRED
12-
CPUs int `yaml:"cpus,omitempty"`
13-
Memory string `yaml:"memory,omitempty"` // go-units.RAMInBytes
14-
Disk string `yaml:"disk,omitempty"` // go-units.RAMInBytes
15-
Mounts []Mount `yaml:"mounts,omitempty"`
16-
SSH SSH `yaml:"ssh,omitempty"` // REQUIRED (FIXME)
17-
Firmware Firmware `yaml:"firmware,omitempty"`
18-
Video Video `yaml:"video,omitempty"`
19-
Provision []Provision `yaml:"provision,omitempty"`
20-
Containerd Containerd `yaml:"containerd,omitempty"`
21-
Probes []Probe `yaml:"probes,omitempty"`
22-
PortForwards []PortForward `yaml:"portForwards,omitempty"`
23-
Networks []Network `yaml:"networks,omitempty"`
24-
Network NetworkDeprecated `yaml:"network,omitempty"` // DEPRECATED, use `networks` instead
25-
Env map[string]string `yaml:"env,omitempty"`
26-
DNS []net.IP `yaml:"dns,omitempty"`
27-
UseHostResolver *bool `yaml:"useHostResolver,omitempty"`
10+
Arch Arch `yaml:"arch,omitempty" json:"arch,omitempty"`
11+
Images []File `yaml:"images" json:"images"` // REQUIRED
12+
CPUs int `yaml:"cpus,omitempty" json:"cpus,omitempty"`
13+
Memory string `yaml:"memory,omitempty" json:"memory,omitempty"` // go-units.RAMInBytes
14+
Disk string `yaml:"disk,omitempty" json:"disk,omitempty"` // go-units.RAMInBytes
15+
Mounts []Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"`
16+
SSH SSH `yaml:"ssh,omitempty" json:"ssh,omitempty"` // REQUIRED (FIXME)
17+
Firmware Firmware `yaml:"firmware,omitempty" json:"firmware,omitempty"`
18+
Video Video `yaml:"video,omitempty" json:"video,omitempty"`
19+
Provision []Provision `yaml:"provision,omitempty" json:"provision,omitempty"`
20+
Containerd Containerd `yaml:"containerd,omitempty" json:"containerd,omitempty"`
21+
Probes []Probe `yaml:"probes,omitempty" json:"probes,omitempty"`
22+
PortForwards []PortForward `yaml:"portForwards,omitempty" json:"portForwards,omitempty"`
23+
Networks []Network `yaml:"networks,omitempty" json:"networks,omitempty"`
24+
Network NetworkDeprecated `yaml:"network,omitempty" json:"network,omitempty"` // DEPRECATED, use `networks` instead
25+
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
26+
DNS []net.IP `yaml:"dns,omitempty" json:"dns,omitempty"`
27+
UseHostResolver *bool `yaml:"useHostResolver,omitempty" json:"useHostResolver,omitempty"`
2828
}
2929

3030
type Arch = string
@@ -35,33 +35,33 @@ const (
3535
)
3636

3737
type File struct {
38-
Location string `yaml:"location"` // REQUIRED
39-
Arch Arch `yaml:"arch,omitempty"`
40-
Digest digest.Digest `yaml:"digest,omitempty"`
38+
Location string `yaml:"location" json:"location"` // REQUIRED
39+
Arch Arch `yaml:"arch,omitempty" json:"arch,omitempty"`
40+
Digest digest.Digest `yaml:"digest,omitempty" json:"digest,omitempty"`
4141
}
4242

4343
type Mount struct {
44-
Location string `yaml:"location"` // REQUIRED
45-
Writable bool `yaml:"writable,omitempty"`
44+
Location string `yaml:"location" json:"location"` // REQUIRED
45+
Writable bool `yaml:"writable,omitempty" json:"writable,omitempty"`
4646
}
4747

4848
type SSH struct {
49-
LocalPort int `yaml:"localPort,omitempty"` // REQUIRED (FIXME: auto assign)
49+
LocalPort int `yaml:"localPort,omitempty" json:"localPort,omitempty"` // REQUIRED (FIXME: auto assign)
5050

5151
// LoadDotSSHPubKeys loads ~/.ssh/*.pub in addition to $LIMA_HOME/_config/user.pub .
5252
// Default: true
53-
LoadDotSSHPubKeys *bool `yaml:"loadDotSSHPubKeys,omitempty"`
53+
LoadDotSSHPubKeys *bool `yaml:"loadDotSSHPubKeys,omitempty" json:"loadDotSSHPubKeys,omitempty"`
5454
}
5555

5656
type Firmware struct {
5757
// LegacyBIOS disables UEFI if set.
5858
// LegacyBIOS is ignored for aarch64.
59-
LegacyBIOS bool `yaml:"legacyBIOS,omitempty"`
59+
LegacyBIOS bool `yaml:"legacyBIOS,omitempty" json:"legacyBIOS,omitempty"`
6060
}
6161

6262
type Video struct {
6363
// Display is a QEMU display string
64-
Display string `yaml:"display,omitempty"`
64+
Display string `yaml:"display,omitempty" json:"display,omitempty"`
6565
}
6666

6767
type ProvisionMode = string
@@ -72,13 +72,14 @@ const (
7272
)
7373

7474
type Provision struct {
75-
Mode ProvisionMode `yaml:"mode"` // default: "system"
76-
Script string `yaml:"script"`
75+
Mode ProvisionMode `yaml:"mode" json:"mode"` // default: "system"
76+
Script string `yaml:"script" json:"script"`
7777
}
7878

7979
type Containerd struct {
80-
System *bool `yaml:"system,omitempty"` // default: false
81-
User *bool `yaml:"user,omitempty"` // default: true
80+
System *bool `yaml:"system,omitempty" json:"system,omitempty"` // default: false
81+
User *bool `yaml:"user,omitempty" json:"user,omitempty"` // default: true
82+
Archives []File `yaml:"archives,omitempty" json:"archives,omitempty"` // default: see defaultContainerdArchives
8283
}
8384

8485
type ProbeMode = string
@@ -101,25 +102,25 @@ const (
101102
)
102103

103104
type PortForward struct {
104-
GuestIP net.IP `yaml:"guestIP,omitempty"`
105-
GuestPort int `yaml:"guestPort,omitempty"`
106-
GuestPortRange [2]int `yaml:"guestPortRange,omitempty"`
107-
HostIP net.IP `yaml:"hostIP,omitempty"`
108-
HostPort int `yaml:"hostPort,omitempty"`
109-
HostPortRange [2]int `yaml:"hostPortRange,omitempty"`
110-
Proto Proto `yaml:"proto,omitempty"`
111-
Ignore bool `yaml:"ignore,omitempty"`
105+
GuestIP net.IP `yaml:"guestIP,omitempty" json:"guestIP,omitempty"`
106+
GuestPort int `yaml:"guestPort,omitempty" json:"guestPort,omitempty"`
107+
GuestPortRange [2]int `yaml:"guestPortRange,omitempty" json:"guestPortRange,omitempty"`
108+
HostIP net.IP `yaml:"hostIP,omitempty" json:"hostIP,omitempty"`
109+
HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"`
110+
HostPortRange [2]int `yaml:"hostPortRange,omitempty" json:"hostPortRange,omitempty"`
111+
Proto Proto `yaml:"proto,omitempty" json:"proto,omitempty"`
112+
Ignore bool `yaml:"ignore,omitempty" json:"ignore,omitempty"`
112113
}
113114

114115
type Network struct {
115116
// `Lima` and `VNL` are mutually exclusive; exactly one is required
116-
Lima string `yaml:"lima,omitempty"`
117+
Lima string `yaml:"lima,omitempty" json:"lima,omitempty"`
117118
// VNL is a Virtual Network Locator (https://github.com/rd235/vdeplug4/commit/089984200f447abb0e825eb45548b781ba1ebccd).
118119
// On macOS, only VDE2-compatible form (optionally with vde:// prefix) is supported.
119-
VNL string `yaml:"vnl,omitempty"`
120-
SwitchPort uint16 `yaml:"switchPort,omitempty"` // VDE Switch port, not TCP/UDP port (only used by VDE networking)
121-
MACAddress string `yaml:"macAddress,omitempty"`
122-
Interface string `yaml:"interface,omitempty"`
120+
VNL string `yaml:"vnl,omitempty" json:"vnl,omitempty"`
121+
SwitchPort uint16 `yaml:"switchPort,omitempty" json:"switchPort,omitempty"` // VDE Switch port, not TCP/UDP port (only used by VDE networking)
122+
MACAddress string `yaml:"macAddress,omitempty" json:"macAddress,omitempty"`
123+
Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`
123124
}
124125

125126
// DEPRECATED types below
@@ -128,14 +129,14 @@ type Network struct {
128129
// and to avoid accidental usage in new code.
129130

130131
type NetworkDeprecated struct {
131-
VDEDeprecated []VDEDeprecated `yaml:"vde,omitempty"`
132+
VDEDeprecated []VDEDeprecated `yaml:"vde,omitempty" json:"vde,omitempty"`
132133
// migrate will be true when `network.VDE` has been copied to `networks` by FillDefaults()
133134
migrated bool
134135
}
135136

136137
type VDEDeprecated struct {
137-
VNL string `yaml:"vnl,omitempty"`
138-
SwitchPort uint16 `yaml:"switchPort,omitempty"` // VDE Switch port, not TCP/UDP port
139-
MACAddress string `yaml:"macAddress,omitempty"`
140-
Name string `yaml:"name,omitempty"`
138+
VNL string `yaml:"vnl,omitempty" json:"vnl,omitempty"`
139+
SwitchPort uint16 `yaml:"switchPort,omitempty" json:"switchPort,omitempty"` // VDE Switch port, not TCP/UDP port
140+
MACAddress string `yaml:"macAddress,omitempty" json:"macAddress,omitempty"`
141+
Name string `yaml:"name,omitempty" json:"name,omitempty"`
141142
}

pkg/limayaml/validate.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/lima-vm/lima/pkg/localpathutil"
1515
"github.com/lima-vm/lima/pkg/networks"
1616
"github.com/lima-vm/lima/pkg/osutil"
17-
"github.com/lima-vm/lima/pkg/qemu/const"
17+
qemu "github.com/lima-vm/lima/pkg/qemu/const"
1818
"github.com/sirupsen/logrus"
1919
)
2020

@@ -109,6 +109,10 @@ func Validate(y LimaYAML, warn bool) error {
109109
i, ProvisionModeSystem, ProvisionModeUser)
110110
}
111111
}
112+
needsContainerdArchives := (y.Containerd.User != nil && *y.Containerd.User) || (y.Containerd.System != nil && *y.Containerd.System)
113+
if needsContainerdArchives && len(y.Containerd.Archives) == 0 {
114+
return fmt.Errorf("field `containerd.archives` must be provided")
115+
}
112116
for i, p := range y.Probes {
113117
switch p.Mode {
114118
case ProbeModeReadiness:
@@ -161,7 +165,7 @@ func Validate(y LimaYAML, warn bool) error {
161165
// processed sequentially and the first matching rule for a guest port determines forwarding behavior.
162166
}
163167

164-
if *y.UseHostResolver && len(y.DNS) > 0 {
168+
if y.UseHostResolver != nil && *y.UseHostResolver && len(y.DNS) > 0 {
165169
return fmt.Errorf("field `dns` must be empty when field `useHostResolver` is true")
166170
}
167171

0 commit comments

Comments
 (0)