Merge pull request #139 from monstermunchkin/issues/135-opensuse

Add openSUSE
This commit is contained in:
Stéphane Graber 2019-02-27 18:14:28 +01:00 committed by GitHub
commit f1f8733941
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 284 additions and 0 deletions

67
doc/examples/opensuse Normal file
View File

@ -0,0 +1,67 @@
image:
distribution: openSUSE
release: 15.0
description: openSUSE Leap {{ image.release }}
expiry: 30d
architecture: x86_64
source:
downloader: opensuse-http
url: https://download.opensuse.org
keyserver: keyserver.ubuntu.com
keys:
- 0xA193FBB572174FC2
targets:
lxc:
create-message: |
You just created an openSUSE Leap container (release={{ image.release }}, arch={{ image.architecture }})
config:
- type: all
before: 5
content: |-
lxc.include = LXC_TEMPLATE_CONFIG/opensuse.common.conf
- type: user
before: 5
content: |-
lxc.include = LXC_TEMPLATE_CONFIG/opensuse.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:
- path: /etc/hostname
generator: hostname
- path: /etc/hosts
generator: hosts
packages:
manager: zypper
update: true
cleanup: true
sets:
- packages:
- systemd
- neovim
action: install
environment:
variables:
- key: HOME
value: /root

View File

@ -42,6 +42,8 @@ func Get(name string) *Manager {
return NewYum()
case "equo":
return NewEquo()
case "zypper":
return NewZypper()
}
return nil

30
managers/zypper.go Normal file
View File

@ -0,0 +1,30 @@
package managers
// NewZypper create a new Manager instance.
func NewZypper() *Manager {
return &Manager{
command: "zypper",
flags: ManagerFlags{
global: []string{
"--non-interactive",
"--gpg-auto-import-keys",
},
clean: []string{
"clean",
"-a",
},
install: []string{
"install",
},
remove: []string{
"remove",
},
refresh: []string{
"refresh",
},
update: []string{
"update",
},
},
}
}

View File

@ -240,6 +240,7 @@ func (d *Definition) Validate() error {
"sabayon-http",
"docker-http",
"oraclelinux-http",
"opensuse-http",
}
if !shared.StringInSlice(strings.TrimSpace(d.Source.Downloader), validDownloaders) {
return fmt.Errorf("source.downloader must be one of %v", validDownloaders)
@ -253,6 +254,7 @@ func (d *Definition) Validate() error {
"portage",
"yum",
"equo",
"zypper",
}
if !shared.StringInSlice(strings.TrimSpace(d.Packages.Manager), validManagers) {
return fmt.Errorf("packages.manager must be one of %v", validManagers)

View File

@ -78,6 +78,25 @@ func RunScript(content string) error {
return cmd.Run()
}
// GetSignedContent verifies the provided file, and returns its decrypted (plain) content.
func GetSignedContent(signedFile string, keys []string, keyserver string) ([]byte, error) {
keyring, err := CreateGPGKeyring(keyserver, keys)
if err != nil {
return nil, err
}
gpgDir := path.Dir(keyring)
defer os.RemoveAll(gpgDir)
out, err := exec.Command("gpg", "--homedir", gpgDir, "--keyring", keyring,
"--decrypt", signedFile).Output()
if err != nil {
return nil, fmt.Errorf("Failed to get file content: %v", err)
}
return out, nil
}
// VerifyFile verifies a file using gpg.
func VerifyFile(signedFile, signatureFile string, keys []string, keyserver string) (bool, error) {
keyring, err := CreateGPGKeyring(keyserver, keys)

162
sources/opensuse-http.go Normal file
View File

@ -0,0 +1,162 @@
package sources
import (
"crypto/sha256"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"github.com/lxc/distrobuilder/shared"
lxd "github.com/lxc/lxd/shared"
"gopkg.in/antchfx/htmlquery.v1"
)
// OpenSUSEHTTP represents the OpenSUSE HTTP downloader.
type OpenSUSEHTTP struct{}
// NewOpenSUSEHTTP creates a new OpenSUSEHTTP instance.
func NewOpenSUSEHTTP() *OpenSUSEHTTP {
return &OpenSUSEHTTP{}
}
// Run downloads an OpenSUSE tarball.
func (s *OpenSUSEHTTP) Run(definition shared.Definition, rootfsDir string) error {
var baseURL string
var fname string
tarballPath := s.getPathToTarball(definition.Source.URL, definition.Image.Release,
definition.Image.ArchitectureMapped)
resp, err := http.Head(tarballPath)
if err != nil {
return fmt.Errorf("Couldn't resolve URL: %v", err)
}
baseURL, fname = path.Split(resp.Request.URL.String())
url, err := url.Parse(fmt.Sprintf("%s/%s", baseURL, fname))
if err != nil {
return err
}
fpath, err := shared.DownloadHash(definition.Image, url.String(), "", nil)
if err != nil {
return fmt.Errorf("Error downloading openSUSE image: %s", err)
}
if definition.Source.SkipVerification {
// Unpack
return lxd.Unpack(filepath.Join(fpath, fname), rootfsDir, false, false, nil)
}
checksumPath := fmt.Sprintf("%s.sha256", tarballPath)
checksumFile := path.Base(checksumPath)
shared.DownloadHash(definition.Image, checksumPath, "", nil)
valid, err := shared.VerifyFile(filepath.Join(fpath, checksumFile), "",
definition.Source.Keys, definition.Source.Keyserver)
if err != nil {
return err
}
if !valid {
return errors.New("Failed to verify tarball")
}
// Manually verify the checksum
checksum, err := shared.GetSignedContent(filepath.Join(fpath, checksumFile),
definition.Source.Keys, definition.Source.Keyserver)
if err != nil {
return fmt.Errorf("Failed to read signed file: %v", err)
}
imagePath := filepath.Join(fpath, fname)
image, err := os.Open(imagePath)
if err != nil {
return fmt.Errorf("Failed to verify image: %v", err)
}
hash := sha256.New()
_, err = io.Copy(hash, image)
if err != nil {
image.Close()
return fmt.Errorf("Failed to verify image: %v", err)
}
image.Close()
result := fmt.Sprintf("%x", hash.Sum(nil))
checksumStr := strings.TrimSpace(string(checksum))
if result != checksumStr {
return fmt.Errorf("Hash mismatch for %s: %s != %s", imagePath, result, checksumStr)
}
// Unpack
return lxd.Unpack(filepath.Join(fpath, fname), rootfsDir, false, false, nil)
}
func (s *OpenSUSEHTTP) getLatestBuild(URL string) string {
doc, err := htmlquery.LoadURL(URL)
if err != nil {
return ""
}
if doc == nil {
return ""
}
nodes := htmlquery.Find(doc, `//a[starts-with(text(),'opensuse')][ends-with(text(), 'tar.xz')][@href]/text()`)
if nodes == nil {
return ""
}
return nodes[len(nodes)-1].Data
}
func (s *OpenSUSEHTTP) getPathToTarball(baseURL string, release string, arch string) string {
u, err := url.Parse(baseURL)
if err != nil {
return ""
}
u.Path = path.Join(u.Path, "repositories", "Virtualization:", "containers:", "images:")
if strings.ToLower(release) == "tumbleweed" {
u.Path = path.Join(u.Path, "openSUSE-Tumbleweed")
switch arch {
case "i686", "x86_64":
u.Path = path.Join(u.Path, "container")
case "aarch64":
u.Path = path.Join(u.Path, "container_ARM")
case "ppc64le":
u.Path = path.Join(u.Path, "container_PowerPC")
default:
return ""
}
u.Path = path.Join(u.Path, fmt.Sprintf("opensuse-tumbleweed-image.%s-lxc.tar.xz",
arch))
} else {
u.Path = path.Join(u.Path, fmt.Sprintf("openSUSE-Leap-%s", release))
switch arch {
case "x86_64":
u.Path = path.Join(u.Path, "containers")
case "aarch64", "ppc64le":
u.Path = path.Join(u.Path, "containers_ports")
}
u.Path = path.Join(u.Path, s.getLatestBuild(u.String()))
}
return u.String()
}

View File

@ -30,6 +30,8 @@ func Get(name string) Downloader {
return NewDockerHTTP()
case "oraclelinux-http":
return NewOracleLinuxHTTP()
case "opensuse-http":
return NewOpenSUSEHTTP()
}
return nil