Merge pull request #139 from monstermunchkin/issues/135-opensuse
Add openSUSE
This commit is contained in:
commit
f1f8733941
67
doc/examples/opensuse
Normal file
67
doc/examples/opensuse
Normal 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
|
@ -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
30
managers/zypper.go
Normal 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",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
162
sources/opensuse-http.go
Normal 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()
|
||||
}
|
@ -30,6 +30,8 @@ func Get(name string) Downloader {
|
||||
return NewDockerHTTP()
|
||||
case "oraclelinux-http":
|
||||
return NewOracleLinuxHTTP()
|
||||
case "opensuse-http":
|
||||
return NewOpenSUSEHTTP()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user