[lxc-devel] [distrobuilder/master] Add Plamo Linux

monstermunchkin on Github lxc-bot at linuxcontainers.org
Tue Mar 19 19:30:04 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 985 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190319/6889be34/attachment.bin>
-------------- next part --------------
From 5e2b51426d35fcc5fc299d6c2f99fa8901cd48d5 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Mon, 18 Mar 2019 16:05:27 +0100
Subject: [PATCH 1/5] shared: Add custom package manager

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 shared/definition.go      |  82 ++++++++++++----
 shared/definition_test.go | 199 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 264 insertions(+), 17 deletions(-)

diff --git a/shared/definition.go b/shared/definition.go
index b8d4ae7..a7da81e 100644
--- a/shared/definition.go
+++ b/shared/definition.go
@@ -30,13 +30,30 @@ type DefinitionPackagesRepository struct {
 	Releases []string `yaml:"release,omitmepty"` // Releases that this repo applies to
 }
 
+// CustomManagerCmd represents a command for a custom manager.
+type CustomManagerCmd struct {
+	Command string   `yaml:"cmd"`
+	Flags   []string `yaml:"flags,omitempty"`
+}
+
+// DefinitionPackagesCustomManager represents a custom package manager.
+type DefinitionPackagesCustomManager struct {
+	Clean   CustomManagerCmd `yaml:"clean"`
+	Install CustomManagerCmd `yaml:"install"`
+	Remove  CustomManagerCmd `yaml:"remove"`
+	Refresh CustomManagerCmd `yaml:"refresh"`
+	Update  CustomManagerCmd `yaml:"update"`
+	Flags   []string         `yaml:"flags,omitempty"`
+}
+
 // A DefinitionPackages represents a package handler.
 type DefinitionPackages struct {
-	Manager      string                         `yaml:"manager"`
-	Update       bool                           `yaml:"update,omitempty"`
-	Cleanup      bool                           `yaml:"cleanup,omitempty"`
-	Sets         []DefinitionPackagesSet        `yaml:"sets,omitempty"`
-	Repositories []DefinitionPackagesRepository `yaml:"repositories,omitempty"`
+	Manager       string                           `yaml:"manager,omitempty"`
+	CustomManager *DefinitionPackagesCustomManager `yaml:"custom-manager,omitempty"`
+	Update        bool                             `yaml:"update,omitempty"`
+	Cleanup       bool                             `yaml:"cleanup,omitempty"`
+	Sets          []DefinitionPackagesSet          `yaml:"sets,omitempty"`
+	Repositories  []DefinitionPackagesRepository   `yaml:"repositories,omitempty"`
 }
 
 // A DefinitionImage represents the image.
@@ -241,23 +258,54 @@ func (d *Definition) Validate() error {
 		"docker-http",
 		"oraclelinux-http",
 		"opensuse-http",
+		"plamolinux-http",
 	}
 	if !shared.StringInSlice(strings.TrimSpace(d.Source.Downloader), validDownloaders) {
 		return fmt.Errorf("source.downloader must be one of %v", validDownloaders)
 	}
 
-	validManagers := []string{
-		"apk",
-		"apt",
-		"dnf",
-		"pacman",
-		"portage",
-		"yum",
-		"equo",
-		"zypper",
-	}
-	if !shared.StringInSlice(strings.TrimSpace(d.Packages.Manager), validManagers) {
-		return fmt.Errorf("packages.manager must be one of %v", validManagers)
+	if d.Packages.Manager != "" {
+		validManagers := []string{
+			"apk",
+			"apt",
+			"dnf",
+			"pacman",
+			"portage",
+			"yum",
+			"equo",
+			"zypper",
+		}
+		if !shared.StringInSlice(strings.TrimSpace(d.Packages.Manager), validManagers) {
+			return fmt.Errorf("packages.manager must be one of %v", validManagers)
+		}
+
+		if d.Packages.CustomManager != nil {
+			return fmt.Errorf("cannot have both packages.manager and packages.custom-manager set")
+		}
+	} else {
+		if d.Packages.CustomManager == nil {
+			return fmt.Errorf("packages.manager or packages.custom-manager needs to be set")
+		}
+
+		if d.Packages.CustomManager.Clean.Command == "" {
+			return fmt.Errorf("packages.custom-manager requires a clean command")
+		}
+
+		if d.Packages.CustomManager.Install.Command == "" {
+			return fmt.Errorf("packages.custom-manager requires an install command")
+		}
+
+		if d.Packages.CustomManager.Remove.Command == "" {
+			return fmt.Errorf("packages.custom-manager requires a remove command")
+		}
+
+		if d.Packages.CustomManager.Refresh.Command == "" {
+			return fmt.Errorf("packages.custom-manager requires a refresh command")
+		}
+
+		if d.Packages.CustomManager.Update.Command == "" {
+			return fmt.Errorf("packages.custom-manager requires an update command")
+		}
 	}
 
 	validGenerators := []string{
diff --git a/shared/definition_test.go b/shared/definition_test.go
index 49b6a6a..a4099d9 100644
--- a/shared/definition_test.go
+++ b/shared/definition_test.go
@@ -88,6 +88,39 @@ func TestValidateDefinition(t *testing.T) {
 			"",
 			false,
 		},
+		{
+			"valid Defintion with packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{
+						Install: CustomManagerCmd{
+							Command: "install",
+						},
+						Remove: CustomManagerCmd{
+							Command: "remove",
+						},
+						Clean: CustomManagerCmd{
+							Command: "clean",
+						},
+						Update: CustomManagerCmd{
+							Command: "update",
+						},
+						Refresh: CustomManagerCmd{
+							Command: "refresh",
+						},
+					},
+				},
+			},
+			"",
+			false,
+		},
 		{
 			"invalid ArchitectureMap",
 			Definition{
@@ -178,6 +211,172 @@ func TestValidateDefinition(t *testing.T) {
 			"packages.manager must be one of .+",
 			true,
 		},
+		{
+			"missing clean command in packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{},
+				},
+			},
+			"packages.custom-manager requires a clean command",
+			true,
+		},
+		{
+			"missing install command in packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{
+						Clean: CustomManagerCmd{
+							Command: "clean",
+						},
+					},
+				},
+			},
+			"packages.custom-manager requires an install command",
+			true,
+		},
+		{
+			"missing remove command in packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{
+						Clean: CustomManagerCmd{
+							Command: "clean",
+						},
+						Install: CustomManagerCmd{
+							Command: "install",
+						},
+					},
+				},
+			},
+			"packages.custom-manager requires a remove command",
+			true,
+		},
+		{
+			"missing refresh command in packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{
+						Clean: CustomManagerCmd{
+							Command: "clean",
+						},
+						Install: CustomManagerCmd{
+							Command: "install",
+						},
+						Remove: CustomManagerCmd{
+							Command: "remove",
+						},
+					},
+				},
+			},
+			"packages.custom-manager requires a refresh command",
+			true,
+		},
+		{
+			"missing update command in packages.custom-manager",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					CustomManager: &DefinitionPackagesCustomManager{
+						Clean: CustomManagerCmd{
+							Command: "clean",
+						},
+						Install: CustomManagerCmd{
+							Command: "install",
+						},
+						Remove: CustomManagerCmd{
+							Command: "remove",
+						},
+						Refresh: CustomManagerCmd{
+							Command: "refresh",
+						},
+					},
+				},
+			},
+			"packages.custom-manager requires an update command",
+			true,
+		},
+		{
+			"package.manager and package.custom-manager set",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{
+					Manager:       "apt",
+					CustomManager: &DefinitionPackagesCustomManager{},
+				},
+			},
+			"cannot have both packages.manager and packages.custom-manager set",
+			true,
+		},
+		{
+			"package.manager and package.custom-manager unset",
+			Definition{
+				Image: DefinitionImage{
+					Distribution: "ubuntu",
+					Release:      "artful",
+				},
+				Source: DefinitionSource{
+					Downloader: "debootstrap",
+					URL:        "https://ubuntu.com",
+					Keys:       []string{"0xCODE"},
+				},
+				Packages: DefinitionPackages{},
+			},
+			"packages.manager or packages.custom-manager needs to be set",
+			true,
+		},
 		{
 			"invalid action trigger",
 			Definition{

From de3d67bfedc024d92d416d69622273632dede151 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 19 Mar 2019 15:36:38 +0100
Subject: [PATCH 2/5] managers: Add custom package manager

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 managers/apk.go     |  8 +++++++-
 managers/apt.go     |  8 +++++++-
 managers/dnf.go     |  8 +++++++-
 managers/equo.go    |  8 +++++++-
 managers/manager.go | 42 ++++++++++++++++++++++++++++++++++++------
 managers/pacman.go  |  8 +++++++-
 managers/portage.go |  8 +++++++-
 managers/yum.go     |  8 +++++++-
 managers/zypper.go  |  8 +++++++-
 9 files changed, 92 insertions(+), 14 deletions(-)

diff --git a/managers/apk.go b/managers/apk.go
index b043289..7d77106 100644
--- a/managers/apk.go
+++ b/managers/apk.go
@@ -3,7 +3,13 @@ package managers
 // NewApk creates a new Manager instance.
 func NewApk() *Manager {
 	return &Manager{
-		command: "apk",
+		commands: ManagerCommands{
+			clean:   "apk",
+			install: "apk",
+			refresh: "apk",
+			remove:  "apk",
+			update:  "apk",
+		},
 		flags: ManagerFlags{
 			global: []string{
 				"--no-cache",
diff --git a/managers/apt.go b/managers/apt.go
index 8d63beb..22d7659 100644
--- a/managers/apt.go
+++ b/managers/apt.go
@@ -3,7 +3,13 @@ package managers
 // NewApt creates a new Manager instance.
 func NewApt() *Manager {
 	return &Manager{
-		command: "apt-get",
+		commands: ManagerCommands{
+			clean:   "apt-get",
+			install: "apt-get",
+			refresh: "apt-get",
+			remove:  "apt-get",
+			update:  "apt-get",
+		},
 		flags: ManagerFlags{
 			clean: []string{
 				"clean",
diff --git a/managers/dnf.go b/managers/dnf.go
index c575e8a..83d4e0a 100644
--- a/managers/dnf.go
+++ b/managers/dnf.go
@@ -3,7 +3,13 @@ package managers
 // NewDnf creates a new Manager instance.
 func NewDnf() *Manager {
 	return &Manager{
-		command: "dnf",
+		commands: ManagerCommands{
+			clean:   "dnf",
+			install: "dnf",
+			refresh: "dnf",
+			remove:  "dnf",
+			update:  "dnf",
+		},
 		flags: ManagerFlags{
 			global: []string{
 				"-y",
diff --git a/managers/equo.go b/managers/equo.go
index 1c9c6c1..2ae644c 100644
--- a/managers/equo.go
+++ b/managers/equo.go
@@ -40,7 +40,13 @@ func equoRepoCaller(repo shared.DefinitionPackagesRepository) error {
 // NewEquo creates a new Manager instance
 func NewEquo() *Manager {
 	return &Manager{
-		command: "equo",
+		commands: ManagerCommands{
+			clean:   "equo",
+			install: "equo",
+			refresh: "equo",
+			remove:  "equo",
+			update:  "equo",
+		},
 		flags: ManagerFlags{
 			global: []string{},
 			clean: []string{
diff --git a/managers/manager.go b/managers/manager.go
index 10f1d04..5823052 100644
--- a/managers/manager.go
+++ b/managers/manager.go
@@ -17,9 +17,18 @@ type ManagerHooks struct {
 	clean func() error
 }
 
+// ManagerCommands represents all commands.
+type ManagerCommands struct {
+	clean   string
+	install string
+	refresh string
+	remove  string
+	update  string
+}
+
 // A Manager represents a package manager.
 type Manager struct {
-	command     string
+	commands    ManagerCommands
 	flags       ManagerFlags
 	hooks       ManagerHooks
 	RepoHandler func(repoAction shared.DefinitionPackagesRepository) error
@@ -49,6 +58,27 @@ func Get(name string) *Manager {
 	return nil
 }
 
+// GetCustom returns a custom Manager specified by a Definition.
+func GetCustom(def shared.DefinitionPackagesCustomManager) *Manager {
+	return &Manager{
+		commands: ManagerCommands{
+			clean:   def.Clean.Command,
+			install: def.Install.Command,
+			refresh: def.Refresh.Command,
+			remove:  def.Remove.Command,
+			update:  def.Update.Command,
+		},
+		flags: ManagerFlags{
+			clean:   def.Clean.Flags,
+			install: def.Install.Flags,
+			refresh: def.Refresh.Flags,
+			remove:  def.Remove.Flags,
+			update:  def.Update.Flags,
+			global:  def.Flags,
+		},
+	}
+}
+
 // Install installs packages to the rootfs.
 func (m Manager) Install(pkgs []string) error {
 	if len(m.flags.install) == 0 || pkgs == nil || len(pkgs) == 0 {
@@ -58,7 +88,7 @@ func (m Manager) Install(pkgs []string) error {
 	args := append(m.flags.global, m.flags.install...)
 	args = append(args, pkgs...)
 
-	return shared.RunCommand(m.command, args...)
+	return shared.RunCommand(m.commands.install, args...)
 }
 
 // Remove removes packages from the rootfs.
@@ -70,7 +100,7 @@ func (m Manager) Remove(pkgs []string) error {
 	args := append(m.flags.global, m.flags.remove...)
 	args = append(args, pkgs...)
 
-	return shared.RunCommand(m.command, args...)
+	return shared.RunCommand(m.commands.remove, args...)
 }
 
 // Clean cleans up cached files used by the package managers.
@@ -83,7 +113,7 @@ func (m Manager) Clean() error {
 
 	args := append(m.flags.global, m.flags.clean...)
 
-	err = shared.RunCommand(m.command, args...)
+	err = shared.RunCommand(m.commands.clean, args...)
 	if err != nil {
 		return err
 	}
@@ -103,7 +133,7 @@ func (m Manager) Refresh() error {
 
 	args := append(m.flags.global, m.flags.refresh...)
 
-	return shared.RunCommand(m.command, args...)
+	return shared.RunCommand(m.commands.refresh, args...)
 }
 
 // Update updates all packages.
@@ -114,7 +144,7 @@ func (m Manager) Update() error {
 
 	args := append(m.flags.global, m.flags.update...)
 
-	return shared.RunCommand(m.command, args...)
+	return shared.RunCommand(m.commands.update, args...)
 }
 
 // SetInstallFlags overrides the default install flags.
diff --git a/managers/pacman.go b/managers/pacman.go
index 2b67c7d..6247386 100644
--- a/managers/pacman.go
+++ b/managers/pacman.go
@@ -26,7 +26,13 @@ func NewPacman() *Manager {
 	}
 
 	return &Manager{
-		command: "pacman",
+		commands: ManagerCommands{
+			clean:   "pacman",
+			install: "pacman",
+			refresh: "pacman",
+			remove:  "pacman",
+			update:  "pacman",
+		},
 		flags: ManagerFlags{
 			clean: []string{
 				"-Sc",
diff --git a/managers/portage.go b/managers/portage.go
index dd12dbb..d29bbe1 100644
--- a/managers/portage.go
+++ b/managers/portage.go
@@ -3,7 +3,13 @@ package managers
 // NewPortage creates a new Manager instance.
 func NewPortage() *Manager {
 	return &Manager{
-		command: "emerge",
+		commands: ManagerCommands{
+			clean:   "emerge",
+			install: "emerge",
+			refresh: "emerge",
+			remove:  "emerge",
+			update:  "emerge",
+		},
 		flags: ManagerFlags{
 			global: []string{},
 			clean:  []string{},
diff --git a/managers/yum.go b/managers/yum.go
index 0016107..767fd32 100644
--- a/managers/yum.go
+++ b/managers/yum.go
@@ -3,7 +3,13 @@ package managers
 // NewYum creates a new Manager instance.
 func NewYum() *Manager {
 	return &Manager{
-		command: "yum",
+		commands: ManagerCommands{
+			clean:   "yum",
+			install: "yum",
+			refresh: "yum",
+			remove:  "yum",
+			update:  "yum",
+		},
 		flags: ManagerFlags{
 			clean: []string{
 				"clean", "all",
diff --git a/managers/zypper.go b/managers/zypper.go
index 6f54213..936d8d0 100644
--- a/managers/zypper.go
+++ b/managers/zypper.go
@@ -3,7 +3,13 @@ package managers
 // NewZypper create a new Manager instance.
 func NewZypper() *Manager {
 	return &Manager{
-		command: "zypper",
+		commands: ManagerCommands{
+			clean:   "zypper",
+			install: "zypper",
+			refresh: "zypper",
+			remove:  "zypper",
+			update:  "zypper",
+		},
 		flags: ManagerFlags{
 			global: []string{
 				"--non-interactive",

From 89461ddae71924a758783338c3dc187fdc08ab4f Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 19 Mar 2019 19:35:28 +0100
Subject: [PATCH 3/5] chroot: Support custom package manager

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 distrobuilder/chroot.go | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index 2485da8..cd3c45a 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -12,10 +12,15 @@ import (
 func managePackages(def shared.DefinitionPackages, actions []shared.DefinitionAction,
 	release string, architecture string) error {
 	var err error
+	var manager *managers.Manager
 
-	manager := managers.Get(def.Manager)
-	if manager == nil {
-		return fmt.Errorf("Couldn't get manager")
+	if def.Manager != "" {
+		manager = managers.Get(def.Manager)
+		if manager == nil {
+			return fmt.Errorf("Couldn't get manager")
+		}
+	} else {
+		manager = managers.GetCustom(*def.CustomManager)
 	}
 
 	// Handle repositories actions

From 75a050ad3c279f2adb4f75127c3ba1dbf83599bc Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 19 Mar 2019 19:38:07 +0100
Subject: [PATCH 4/5] sources: Add Plamo Linux

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 sources/plamolinux-http.go | 137 +++++++++++++++++++++++++++++++++++++
 sources/source.go          |   2 +
 2 files changed, 139 insertions(+)
 create mode 100644 sources/plamolinux-http.go

diff --git a/sources/plamolinux-http.go b/sources/plamolinux-http.go
new file mode 100644
index 0000000..a4751fa
--- /dev/null
+++ b/sources/plamolinux-http.go
@@ -0,0 +1,137 @@
+package sources
+
+import (
+	"fmt"
+	"net/url"
+	"path"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"gopkg.in/antchfx/htmlquery.v1"
+
+	"github.com/lxc/distrobuilder/shared"
+)
+
+// PlamoLinuxHTTP represents the Plamo Linux downloader.
+type PlamoLinuxHTTP struct {
+}
+
+// NewPlamoLinuxHTTP creates a new PlamoLinuxHTTP instance.
+func NewPlamoLinuxHTTP() *PlamoLinuxHTTP {
+	return &PlamoLinuxHTTP{}
+}
+
+// Run downloads Plamo Linux.
+func (s *PlamoLinuxHTTP) Run(definition shared.Definition, rootfsDir string) error {
+	release, err := strconv.Atoi(definition.Image.Release)
+	if err != nil {
+		return fmt.Errorf("Failed to determine release: %v", err)
+	}
+
+	u, err := url.Parse(definition.Source.URL)
+	if err != nil {
+		return err
+	}
+
+	mirrorPath := path.Join(u.Path, fmt.Sprintf("Plamo-%s.x", definition.Image.Release),
+		definition.Image.ArchitectureMapped, "plamo")
+
+	paths := []string{
+		path.Join(mirrorPath, "00_base"),
+	}
+
+	if release < 7 {
+		paths = append(paths, path.Join(mirrorPath, "01_minimum"))
+
+	}
+
+	var pkgDir string
+
+	for _, p := range paths {
+		u.Path = p
+
+		pkgDir, err = s.downloadFiles(definition.Image, u.String())
+		if err != nil {
+			return fmt.Errorf("Failed to download packages: %v", err)
+		}
+	}
+
+	var pkgTool string
+
+	// Find package tool
+	if release < 7 {
+		pkgTool = "hdsetup"
+	} else {
+		pkgTool = "pkgtools"
+	}
+
+	matches, err := filepath.Glob(filepath.Join(pkgDir, fmt.Sprintf("%s-*.txz", pkgTool)))
+	if err != nil {
+		return err
+	}
+
+	if len(matches) == 0 {
+		return fmt.Errorf("Couldn't find any matching package")
+	} else if len(matches) > 1 {
+		return fmt.Errorf("Found more than one matching package")
+	}
+
+	err = shared.RunCommand("tar", "-pxJf", matches[0], "-C", rootfsDir, "sbin/")
+	if err != nil {
+		return err
+	}
+
+	return shared.RunScript(fmt.Sprintf(`#!/bin/sh
+
+dlcache=%s
+rootfs_dir=%s
+
+sed -i "/ldconfig/!s@/sbin@$rootfs_dir&@g" $rootfs_dir/sbin/installpkg*
+PATH=$rootfs_dir/sbin:$PATH
+export LC_ALL=C
+
+for p in $(ls -cr $dlcache/*.t?z) ; do
+    installpkg -root $rootfs_dir -priority ADD $p
+done
+`, pkgDir, rootfsDir))
+}
+
+func (s *PlamoLinuxHTTP) downloadFiles(def shared.DefinitionImage, URL string) (string, error) {
+	doc, err := htmlquery.LoadURL(URL)
+	if err != nil {
+		return "", err
+	}
+
+	if doc == nil {
+		return "", fmt.Errorf("Failed to load URL")
+	}
+
+	nodes := htmlquery.Find(doc, `//a/@href`)
+
+	var dir string
+
+	for _, n := range nodes {
+		target := htmlquery.InnerText(n)
+
+		if strings.HasSuffix(target, ".txz") {
+			// package
+			dir, err = shared.DownloadHash(def, fmt.Sprintf("%s/%s", URL, target), "", nil)
+			if err != nil {
+				return "", err
+			}
+		} else if strings.HasSuffix(target, ".txz/") {
+			// directory
+			u, err := url.Parse(URL)
+			if err != nil {
+				return "", err
+			}
+
+			u.Path = path.Join(u.Path, target)
+
+			return s.downloadFiles(def, u.String())
+		}
+	}
+
+	return dir, nil
+}
diff --git a/sources/source.go b/sources/source.go
index a284e68..2d4bc3b 100644
--- a/sources/source.go
+++ b/sources/source.go
@@ -32,6 +32,8 @@ func Get(name string) Downloader {
 		return NewOracleLinuxHTTP()
 	case "opensuse-http":
 		return NewOpenSUSEHTTP()
+	case "plamolinux-http":
+		return NewPlamoLinuxHTTP()
 	}
 
 	return nil

From e83298b7fa4e614ddf4ddd5052f078ede133b4d7 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 19 Mar 2019 19:40:07 +0100
Subject: [PATCH 5/5] doc: Add Plamo Linux example

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 doc/examples/plamolinux | 62 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 doc/examples/plamolinux

diff --git a/doc/examples/plamolinux b/doc/examples/plamolinux
new file mode 100644
index 0000000..b782776
--- /dev/null
+++ b/doc/examples/plamolinux
@@ -0,0 +1,62 @@
+image:
+  distribution: plamolinux
+  release: 7
+  description: Plamo Linux
+  expiry: 30d
+  architecture: x86_64
+
+source:
+  downloader: plamolinux-http
+  url: https://repository.plamolinux.org/pub/linux/Plamo
+
+targets:
+  lxc:
+    create-message: |
+      You just created a Plamo Linux container (release={{ image.release }}, arch={{ image.architecture }})
+
+    config:
+      - type: all
+        before: 5
+        content: |-
+          lxc.include = LXC_TEMPLATE_CONFIG/plamolinux.common.conf
+
+      - type: user
+        before: 5
+        content: |-
+          lxc.include = LXC_TEMPLATE_CONFIG/plamolinux.userns.conf
+
+      - type: all
+        after: 4
+        content: |-
+          lxc.include = LXC_TEMPLATE_CONFIG/common.conf
+
+      - type: user
+        after: 4
+        content: |-
+          lxc.include = LXC_TEMPLATE_CONFIG/userns.conf
+
+      - type: all
+        content: |-
+          lxc.arch = {{ image.architecture_kernel }}
+
+files:
+  - name: hostname
+    path: /etc/hostname
+    generator: hostname
+
+  - name: hosts
+    path: /etc/hosts
+    generator: hosts
+
+packages:
+  custom-manager:
+    install:
+      cmd: installpkg
+    remove:
+      cmd: removepkg
+    update:
+      cmd: updatepkg
+    clean:
+      cmd: true 
+    refresh:
+      cmd: true


More information about the lxc-devel mailing list