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

docker: use docker-ce packages #884

Merged
merged 5 commits into from
Nov 18, 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
7 changes: 6 additions & 1 deletion environment/container/containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@ func (c containerdRuntime) Name() string {
}

func (c containerdRuntime) Provision(context.Context) error {
if err := c.guest.RunQuiet("sh", "-c",
`sudo sed -i '/disabled_plugins =/c\disabled_plugins = []' /etc/containerd/config.toml`,
); err != nil {
return err
}
return c.guest.Write(buildKitConfFile, buildKitConf)
}

func (c containerdRuntime) Start(ctx context.Context) error {
a := c.Init(ctx)

a.Add(func() error {
return c.guest.Run("sudo", "service", "containerd", "start")
return c.guest.Run("sudo", "service", "containerd", "restart")
})

// service startup takes few seconds, retry at most 10 times before giving up.
Expand Down
6 changes: 4 additions & 2 deletions environment/container/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ func (d dockerRuntime) Provision(ctx context.Context) error {
func (d dockerRuntime) Start(ctx context.Context) error {
a := d.Init(ctx)

a.Add(func() error {
return d.guest.Run("sudo", "service", "docker", "start")
// TODO: interval is high due to 0.6.3->0.6.4 docker-ce package transition
// to ensure startup is successful
a.Retry("", time.Second*5, 24, func(int) error {
return d.guest.RunQuiet("sudo", "service", "docker", "start")
})

// service startup takes few seconds, retry at most 5 times before giving up.
Expand Down
24 changes: 24 additions & 0 deletions environment/vm/lima/deb/deb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package deb

import (
"github.com/abiosoft/colima/environment"
)

type (
hostActions = environment.HostActions
guestActions = environment.GuestActions
)

// URISource is the source for fetching URI for deb packages.
type URISource interface {
// Name is the name for the URISource.
Name() string
// Packages is the list of package names.
Packages() []string
// URIs return the list of URIs to download the deb files.
URIs(arch environment.Arch) ([]string, error)
// PreInstall is done before the deb package are installed.
PreInstall() error
// Install installs the packages directly using the internet.
Install() error
}
77 changes: 77 additions & 0 deletions environment/vm/lima/deb/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package deb

import (
"fmt"
"strings"

"github.com/abiosoft/colima/environment"
)

var dockerPackages = []string{
"docker-ce",
"docker-ce-cli",
"containerd.io",
"docker-buildx-plugin",
"docker-compose-plugin",
}

var _ URISource = (*Docker)(nil)

// Docker is the URISource for Docker CE packages.
type Docker struct {
Host hostActions
Guest guestActions
}

// PreInstall implements URISource.
func (d *Docker) PreInstall() error {
return d.Guest.RunQuiet("sh", "-c", "sudo apt remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc")
}

// Install implements URISource.
func (d *Docker) Install() error {
return d.Guest.Run("sh", "-c",
`curl -fsSL https://get.docker.com -o /tmp/get-docker.sh && sudo sh /tmp/get-docker.sh`,
)
}

// Name implements URISource.
func (*Docker) Name() string {
return "docker-ce"
}

// Packages implements URISource.
func (*Docker) Packages() []string {
return dockerPackages
}

// URIs implements URISource.
func (d *Docker) URIs(arch environment.Arch) ([]string, error) {
var uris []string

pkgFiles, err := d.pkgFiles(arch)
if err != nil {
return nil, fmt.Errorf("error getting package names and version: %w", err)
}

for _, file := range pkgFiles {
uri := d.debPackageBaseURI(arch) + file
uris = append(uris, uri)
}

return uris, nil
}

func (d Docker) pkgFiles(arch environment.Arch) ([]string, error) {
script := fmt.Sprintf(`curl -sL https://download.docker.com/linux/ubuntu/dists/mantic/stable/binary-%s/Packages | grep '^Filename: ' | awk -F'/' '{print $NF}'`, arch.Value().GoArch())
filenames, err := d.Host.RunOutput("sh", "-c", script)
if err != nil {
return nil, fmt.Errorf("error retrieving deb package filenames: %w", err)
}

return strings.Fields(filenames), nil
}

func (d Docker) debPackageBaseURI(arch environment.Arch) string {
return fmt.Sprintf("https://download.docker.com/linux/ubuntu/dists/mantic/pool/stable/%s/", arch.GoArch())
}
61 changes: 61 additions & 0 deletions environment/vm/lima/deb/mantic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package deb

import (
"fmt"
"strings"

"github.com/abiosoft/colima/environment"
)

var manticPackages = []string{
// docker
"iptables",
// k8s
"socat",
// utilities
"htop", "vim", "inetutils-ping", "dnsutils",
}

var _ URISource = (*Mantic)(nil)

// Mantic is the URISource for Ubuntu Mantic packages.
type Mantic struct {
Guest guestActions
}

// PreInstall implements URISource.
func (*Mantic) PreInstall() error {
return nil
}

// Packages implements URISource.
func (*Mantic) Packages() []string {
return manticPackages
}

// Name implements URISource.
func (*Mantic) Name() string {
return "mantic-debs"
}

// URIs implements URISource.
func (m *Mantic) URIs(_ environment.Arch) ([]string, error) {
_ = m.Guest.RunQuiet("sudo apt update -y")

output := ""
for _, p := range manticPackages {
line := fmt.Sprintf(`sudo apt-get install --reinstall --print-uris -qq "%s" | cut -d"'" -f2`, p)
out, err := m.Guest.RunOutput("sh", "-c", line)
if err != nil {
return nil, fmt.Errorf("error fetching dependencies list: %w", err)
}
output += out + " "
}

return strings.Fields(output), nil
}

// Install implements URISource.
func (m *Mantic) Install() error {
return m.Guest.Run("sh", "-c", "sudo apt update && sudo apt install -f -y "+strings.Join(manticPackages, " "))
}
89 changes: 52 additions & 37 deletions environment/vm/lima/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,25 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/abiosoft/colima/config"
"github.com/abiosoft/colima/environment"
"github.com/abiosoft/colima/environment/vm/lima/deb"
"github.com/abiosoft/colima/util/fsutil"
"github.com/abiosoft/colima/util/terminal"
"github.com/sirupsen/logrus"
)

var dependencyPackages = []string{
// docker and k8s
"docker.io", "socat",
// utilities
"htop", "vim", "inetutils-ping", "dnsutils",
}

// cacheDependencies downloads the ubuntu deb files to a path on the host.
// The return value is the directory of the downloaded deb files.
func (l *limaVM) cacheDependencies(log *logrus.Entry, conf config.Config) (string, error) {
func (l *limaVM) cacheDependencies(src deb.URISource, log *logrus.Entry, conf config.Config) (string, error) {
codename, err := l.RunOutput("sh", "-c", `grep "^UBUNTU_CODENAME" /etc/os-release | cut -d= -f2`)
if err != nil {
return "", fmt.Errorf("error retrieving OS version from vm: %w", err)
}

arch := environment.Arch(conf.Arch).Value()
dir := filepath.Join(config.CacheDir(), "packages", codename, string(arch))
dir := filepath.Join(config.CacheDir(), "packages", codename, string(arch), src.Name())
if err := fsutil.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("error creating cache directory for OS packages: %w", err)
}
Expand All @@ -40,17 +33,12 @@ func (l *limaVM) cacheDependencies(log *logrus.Entry, conf config.Config) (strin
return dir, nil
}

output := ""
for _, p := range dependencyPackages {
line := fmt.Sprintf(`sudo apt-get install --reinstall --print-uris -qq "%s" | cut -d"'" -f2`, p)
out, err := l.RunOutput("sh", "-c", line)
if err != nil {
return "", fmt.Errorf("error fetching dependencies list: %w", err)
}
output += out + " "
var debPackages []string
packages, err := src.URIs(arch)
if err != nil {
return "", fmt.Errorf("error fetching package URIs using %s: %w", src.Name(), err)
}

debPackages := strings.Fields(output)
debPackages = append(debPackages, packages...)

// progress bar for Ubuntu deb packages download.
// TODO: extract this into re-usable progress bar for multi-downloads
Expand All @@ -76,27 +64,54 @@ func (l *limaVM) cacheDependencies(log *logrus.Entry, conf config.Config) (strin
}

func (l *limaVM) installDependencies(log *logrus.Entry, conf config.Config) error {
// cache dependencies
dir, err := l.cacheDependencies(log, conf)
if err != nil {
log.Warnln(fmt.Errorf("error caching dependencies: %w", err))
log.Warnln("falling back to normal package install")
return l.Run("sh", "-c", "sudo apt install -y "+strings.Join(dependencyPackages, " "))
srcs := []deb.URISource{
&deb.Mantic{Guest: l},
&deb.Docker{Host: l.host, Guest: l},
}

// validate if packages were previously installed
installed := true
for _, p := range dependencyPackages {
if err := l.RunQuiet("dpkg", "-s", p); err != nil {
installed = false
break
for _, src := range srcs {
if err := src.PreInstall(); err != nil {
log.Warn(fmt.Errorf("preinstall check failed for %s: %w", src.Name(), err))
}

// cache dependencies
dir, err := l.cacheDependencies(src, log, conf)
if err != nil {
log.Warnln(fmt.Errorf("error caching dependencies for %s: %w", src.Name(), err))
log.Warnln("falling back to normal package install")

if err := src.Install(); err != nil {
return fmt.Errorf("error installing packages using %s: %w", src.Name(), err)
}

// installed
continue
}

// validate if packages were previously installed
installed := true
for _, p := range src.Packages() {
if err := l.RunQuiet("dpkg", "-s", p); err != nil {
installed = false
break
}
}

if installed {
continue
}

// install packages
if err := l.Run("sh", "-c", "sudo dpkg -i "+dir+"/*.deb"); err != nil {
log.Warn(fmt.Errorf("error installing packages using %s: %w", src.Name(), err))
log.Warnln("falling back to normal package install")

if err := src.Install(); err != nil {
return fmt.Errorf("error installing packages using %s: %w", src.Name(), err)
}
}
}

if installed {
return nil
}

// install packages
return l.Run("sh", "-c", "sudo dpkg -i "+dir+"/*.deb")
return nil
}