diff --git a/README.md b/README.md index 703c284..cfdc66f 100644 --- a/README.md +++ b/README.md @@ -166,11 +166,15 @@ Usage: Flags: --append-to-cmdline string Extra kernel cmdline arguments to append to the generated one + --add-host strings Add a custom host-to-IP mapping (host:ip) to the /etc/hosts file in the generated image --boot-fs string Filesystem to use for the boot partition, ext4 or fat32 --boot-size uint Size of the boot partition in MB (default 100) --bootloader string Bootloader to use: syslinux, grub, grub-bios, grub-efi, defaults to syslinux on amd64 and grub-efi on arm64 + --dns strings DNS servers to set in the generated image + --dns-search strings DNS search domains to set in the generated image --force Override output qcow2 image -h, --help help for convert + --hostname string Hostname to set in the generated image (default "localhost") --keep-cache Keep the images after the build --luks-password string Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted --network-manager string Network manager to use for the image: none, netplan, ifupdown @@ -324,13 +328,17 @@ Usage: Flags: --append-to-cmdline string Extra kernel cmdline arguments to append to the generated one + --add-host strings Add a custom host-to-IP mapping (host:ip) to the /etc/hosts file in the generated image --boot-fs string Filesystem to use for the boot partition, ext4 or fat32 --boot-size uint Size of the boot partition in MB (default 100) --bootloader string Bootloader to use: syslinux, grub, grub-bios, grub-efi, defaults to syslinux on amd64 and grub-efi on arm64 + --dns strings DNS servers to set in the generated image + --dns-search strings DNS search domains to set in the generated image --build-arg stringArray Set build-time variables -f, --file string Name of the Dockerfile --force Override output qcow2 image -h, --help help for build + --hostname string Hostname to set in the generated image (default "localhost") --keep-cache Keep the images after the build --luks-password string Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted --network-manager string Network manager to use for the image: none, netplan, ifupdown diff --git a/builder.go b/builder.go index 04d89aa..3ada1dc 100644 --- a/builder.go +++ b/builder.go @@ -83,9 +83,14 @@ type builder struct { cmdLineExtra string arch string + + hostname string + dns []string + dnsSearch []string + hosts string } -func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, osRelease OSRelease, format string, cmdLineExtra string, splitBoot bool, bootFS BootFS, bootSize uint64, luksPassword string, bootLoader string, platform string) (Builder, error) { +func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, osRelease OSRelease, format string, cmdLineExtra string, splitBoot bool, bootFS BootFS, bootSize uint64, luksPassword string, bootLoader string, platform, hostname string, dns, dnsSearch []string, extraHosts map[string]string) (Builder, error) { var arch string switch platform { case "linux/amd64": @@ -179,6 +184,16 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, // logrus.Warnf("%s is smaller than rootfs size, using %s", datasize.ByteSize(size), s) // size = int64(s) // } + if hostname == "" { + hostname = "localhost" + } + if len(dns) == 0 { + dns = []string{"8.8.8.8"} + } + hosts := hosts + for k, v := range extraHosts { + hosts += fmt.Sprintf("%s %s\n", v, k) + } b := &builder{ osRelease: osRelease, config: config, @@ -195,6 +210,10 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, bootFS: bootFS, luksPassword: luksPassword, arch: arch, + hostname: hostname, + dns: dns, + dnsSearch: dnsSearch, + hosts: hosts, } if err := b.checkDependencies(); err != nil { return nil, err @@ -409,13 +428,13 @@ func (b *builder) setupRootFS(ctx context.Context) (err error) { if err := b.chWriteFile("/etc/fstab", fstab, perm); err != nil { return err } - if err := b.chWriteFileIfNotExist("/etc/resolv.conf", "nameserver 8.8.8.8", 0644); err != nil { + if err := b.chWriteFile("/etc/resolv.conf", b.resolvConf(), 0644); err != nil { return err } - if err := b.chWriteFileIfNotExist("/etc/hostname", "localhost", perm); err != nil { + if err := b.chWriteFile("/etc/hostname", b.hostname+"\n", perm); err != nil { return err } - if err := b.chWriteFileIfNotExist("/etc/hosts", hosts, perm); err != nil { + if err := b.chWriteFile("/etc/hosts", b.hosts, perm); err != nil { return err } // TODO(adphi): is it the righ fix ? @@ -494,6 +513,21 @@ func (b *builder) isLuksEnabled() bool { return b.luksPassword != "" } +func (b *builder) resolvConf() string { + var sb strings.Builder + for _, v := range b.dns { + sb.WriteString("nameserver ") + sb.WriteString(v) + sb.WriteString("\n") + } + if len(b.dnsSearch) > 0 { + sb.WriteString("search ") + sb.WriteString(strings.Join(b.dnsSearch, " ")) + sb.WriteString("\n") + } + return sb.String() +} + func (b *builder) Close() error { return b.img.Close() } diff --git a/cmd/d2vm/build.go b/cmd/d2vm/build.go index 3d9f97e..9ac9443 100644 --- a/cmd/d2vm/build.go +++ b/cmd/d2vm/build.go @@ -110,6 +110,10 @@ var ( d2vm.WithKeepCache(keepCache), d2vm.WithPlatform(platform), d2vm.WithPull(false), + d2vm.WithHostname(hostname), + d2vm.WithDNS(dns), + d2vm.WithDNSSearch(dnsSearch), + d2vm.WithExtraHosts(extraHosts), ); err != nil { return err } diff --git a/cmd/d2vm/convert.go b/cmd/d2vm/convert.go index 6aea732..6027302 100644 --- a/cmd/d2vm/convert.go +++ b/cmd/d2vm/convert.go @@ -91,6 +91,10 @@ var ( d2vm.WithKeepCache(keepCache), d2vm.WithPlatform(platform), d2vm.WithPull(pull), + d2vm.WithHostname(hostname), + d2vm.WithDNS(dns), + d2vm.WithDNSSearch(dnsSearch), + d2vm.WithExtraHosts(extraHosts), ); err != nil { return err } diff --git a/cmd/d2vm/flags.go b/cmd/d2vm/flags.go index b97abbd..db60a13 100644 --- a/cmd/d2vm/flags.go +++ b/cmd/d2vm/flags.go @@ -44,9 +44,16 @@ var ( keepCache bool platform string + + hostname string + dns []string + dnsSearch []string + hosts []string + + extraHosts map[string]string ) -func validateFlags() error { +func validateFlags() (err error) { switch platform { case "linux/amd64": if bootloader == "" { @@ -94,6 +101,10 @@ func validateFlags() error { return fmt.Errorf("%s already exists", output) } } + extraHosts, err = validateHosts(hosts...) + if err != nil { + return fmt.Errorf("invalid --add-host value: %w", err) + } return nil } @@ -116,5 +127,22 @@ func buildFlags() *pflag.FlagSet { flags.BoolVar(&keepCache, "keep-cache", false, "Keep the images after the build") flags.StringVar(&platform, "platform", d2vm.Arch, "Platform to use for the container disk image, linux/arm64 and linux/arm64 are supported") flags.BoolVar(&pull, "pull", false, "Always pull docker image") + flags.StringVar(&hostname, "hostname", "localhost", "Hostname to set in the generated image") + flags.StringSliceVar(&dns, "dns", []string{}, "DNS servers to set in the generated image") + flags.StringSliceVar(&dnsSearch, "dns-search", []string{}, "DNS search domains to set in the generated image") + flags.StringSliceVar(&hosts, "add-host", []string{}, "Add a custom host-to-IP mapping (host:ip) to the /etc/hosts file in the generated image") return flags } + +func validateHosts(vals ...string) (map[string]string, error) { + out := make(map[string]string) + for _, val := range vals { + // allow for IPv6 addresses in extra hosts by only splitting on first ":" + k, v, ok := strings.Cut(val, ":") + if !ok || k == "" { + return nil, fmt.Errorf("bad format for add-host: %q", val) + } + out[k] = v + } + return out, nil +} diff --git a/convert.go b/convert.go index 769de35..7c4a19a 100644 --- a/convert.go +++ b/convert.go @@ -88,7 +88,7 @@ func Convert(ctx context.Context, img string, opts ...ConvertOption) error { if format == "" { format = "raw" } - b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra, o.splitBoot, o.bootFS, o.bootSize, o.luksPassword, o.bootLoader, o.platform) + b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra, o.splitBoot, o.bootFS, o.bootSize, o.luksPassword, o.bootLoader, o.platform, o.hostname, o.dns, o.dnsSearch, o.hosts) if err != nil { return err } diff --git a/convert_options.go b/convert_options.go index 47efd13..5c04fa9 100644 --- a/convert_options.go +++ b/convert_options.go @@ -34,6 +34,11 @@ type convertOptions struct { keepCache bool platform string pull bool + + hostname string + dns []string + dnsSearch []string + hosts map[string]string } func (o *convertOptions) hasGrubBIOS() bool { @@ -127,3 +132,27 @@ func WithPull(b bool) ConvertOption { o.pull = b } } + +func WithHostname(hostname string) ConvertOption { + return func(o *convertOptions) { + o.hostname = hostname + } +} + +func WithDNS(dns []string) ConvertOption { + return func(o *convertOptions) { + o.dns = dns + } +} + +func WithDNSSearch(dnsSearch []string) ConvertOption { + return func(o *convertOptions) { + o.dnsSearch = dnsSearch + } +} + +func WithExtraHosts(hosts map[string]string) ConvertOption { + return func(o *convertOptions) { + o.hosts = hosts + } +} diff --git a/docs/content/reference/d2vm_build.md b/docs/content/reference/d2vm_build.md index 110eab7..bc3f1be 100644 --- a/docs/content/reference/d2vm_build.md +++ b/docs/content/reference/d2vm_build.md @@ -9,14 +9,18 @@ d2vm build [context directory] [flags] ### Options ``` + --add-host strings Add a custom host-to-IP mapping (host:ip) to the /etc/hosts file in the generated image --append-to-cmdline string Extra kernel cmdline arguments to append to the generated one --boot-fs string Filesystem to use for the boot partition, ext4 or fat32 --boot-size uint Size of the boot partition in MB (default 100) --bootloader string Bootloader to use: syslinux, grub, grub-bios, grub-efi, defaults to syslinux on amd64 and grub-efi on arm64 --build-arg stringArray Set build-time variables + --dns strings DNS servers to set in the generated image + --dns-search strings DNS search domains to set in the generated image -f, --file string Name of the Dockerfile --force Override output qcow2 image -h, --help help for build + --hostname string Hostname to set in the generated image (default "localhost") --keep-cache Keep the images after the build --luks-password string Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted --network-manager string Network manager to use for the image: none, netplan, ifupdown diff --git a/docs/content/reference/d2vm_convert.md b/docs/content/reference/d2vm_convert.md index e143acb..cd97d29 100644 --- a/docs/content/reference/d2vm_convert.md +++ b/docs/content/reference/d2vm_convert.md @@ -9,12 +9,16 @@ d2vm convert [docker image] [flags] ### Options ``` + --add-host strings Add a custom host-to-IP mapping (host:ip) to the /etc/hosts file in the generated image --append-to-cmdline string Extra kernel cmdline arguments to append to the generated one --boot-fs string Filesystem to use for the boot partition, ext4 or fat32 --boot-size uint Size of the boot partition in MB (default 100) --bootloader string Bootloader to use: syslinux, grub, grub-bios, grub-efi, defaults to syslinux on amd64 and grub-efi on arm64 + --dns strings DNS servers to set in the generated image + --dns-search strings DNS search domains to set in the generated image --force Override output qcow2 image -h, --help help for convert + --hostname string Hostname to set in the generated image (default "localhost") --keep-cache Keep the images after the build --luks-password string Password to use for the LUKS encrypted root partition. If not set, the root partition will not be encrypted --network-manager string Network manager to use for the image: none, netplan, ifupdown