2018-02-02 17:36:48 +01:00
package sources
import (
2018-12-19 20:29:46 +01:00
"crypto/sha256"
2018-03-08 09:27:23 +01:00
"errors"
2018-02-02 17:36:48 +01:00
"fmt"
"io/ioutil"
"net/http"
2018-03-08 09:27:23 +01:00
"net/url"
2018-02-02 17:36:48 +01:00
"os"
"path"
"path/filepath"
"regexp"
"strings"
lxd "github.com/lxc/lxd/shared"
2018-03-06 11:16:22 +01:00
"github.com/lxc/distrobuilder/shared"
2018-02-02 17:36:48 +01:00
)
// UbuntuHTTP represents the Ubuntu HTTP downloader.
type UbuntuHTTP struct {
fname string
}
// NewUbuntuHTTP creates a new UbuntuHTTP instance.
func NewUbuntuHTTP ( ) * UbuntuHTTP {
return & UbuntuHTTP { }
}
// Run downloads the tarball and unpacks it.
2018-03-16 12:58:14 +01:00
func ( s * UbuntuHTTP ) Run ( definition shared . Definition , rootfsDir string ) error {
baseURL := fmt . Sprintf ( "%s/releases/%s/release/" , definition . Source . URL ,
definition . Image . Release )
2018-02-14 17:56:44 +01:00
2018-03-16 12:58:14 +01:00
if strings . ContainsAny ( definition . Image . Release , "0123456789" ) {
s . fname = fmt . Sprintf ( "ubuntu-base-%s-base-%s.tar.gz" ,
2018-03-21 15:25:09 -04:00
definition . Image . Release , definition . Image . ArchitectureMapped )
2018-02-02 17:36:48 +01:00
} else {
// if release is non-numerical, find the latest release
2018-03-16 12:58:14 +01:00
s . fname = getLatestRelease ( definition . Source . URL ,
2018-03-21 15:25:09 -04:00
definition . Image . Release , definition . Image . ArchitectureMapped )
2018-02-02 17:36:48 +01:00
if s . fname == "" {
return fmt . Errorf ( "Couldn't find latest release" )
}
}
2018-03-08 09:27:23 +01:00
url , err := url . Parse ( baseURL )
2018-02-14 17:56:44 +01:00
if err != nil {
return err
}
2018-03-08 09:27:23 +01:00
2019-02-27 10:36:45 +01:00
var fpath string
2018-03-08 09:27:23 +01:00
checksumFile := ""
// Force gpg checks when using http
2018-07-02 17:37:31 +02:00
if ! definition . Source . SkipVerification && url . Scheme != "https" {
2018-03-15 00:14:15 -04:00
if len ( definition . Source . Keys ) == 0 {
2018-03-08 09:27:23 +01:00
return errors . New ( "GPG keys are required if downloading from HTTP" )
}
checksumFile = baseURL + "SHA256SUMS"
2019-02-27 10:36:45 +01:00
fpath , err = shared . DownloadHash ( definition . Image , baseURL + "SHA256SUMS.gpg" , "" , nil )
if err != nil {
return err
}
shared . DownloadHash ( definition . Image , checksumFile , "" , nil )
2018-03-08 09:27:23 +01:00
valid , err := shared . VerifyFile (
2019-02-27 10:36:45 +01:00
filepath . Join ( fpath , "SHA256SUMS" ) ,
filepath . Join ( fpath , "SHA256SUMS.gpg" ) ,
2018-03-15 00:14:15 -04:00
definition . Source . Keys ,
definition . Source . Keyserver )
2018-03-08 09:27:23 +01:00
if err != nil {
return err
}
if ! valid {
return fmt . Errorf ( "Failed to validate tarball" )
}
2018-02-14 17:56:44 +01:00
}
2019-02-27 10:36:45 +01:00
fpath , err = shared . DownloadHash ( definition . Image , baseURL + s . fname , checksumFile , sha256 . New ( ) )
2018-02-02 17:36:48 +01:00
if err != nil {
return fmt . Errorf ( "Error downloading Ubuntu image: %s" , err )
}
2019-02-27 10:36:45 +01:00
err = s . unpack ( filepath . Join ( fpath , s . fname ) , rootfsDir )
2018-03-07 12:14:24 +01:00
if err != nil {
return err
}
2018-03-15 00:14:15 -04:00
if definition . Source . AptSources != "" {
// Run the template
out , err := shared . RenderTemplate ( definition . Source . AptSources , definition )
2018-03-07 12:14:24 +01:00
if err != nil {
return err
}
2018-03-14 21:26:50 -04:00
// Append final new line if missing
if ! strings . HasSuffix ( out , "\n" ) {
out += "\n"
}
2018-03-07 12:14:24 +01:00
// Replace content of sources.list with the templated content.
file , err := os . Create ( filepath . Join ( rootfsDir , "etc" , "apt" , "sources.list" ) )
if err != nil {
return err
}
defer file . Close ( )
file . WriteString ( out )
}
return nil
2018-02-02 17:36:48 +01:00
}
func ( s UbuntuHTTP ) unpack ( filePath , rootDir string ) error {
os . RemoveAll ( rootDir )
os . MkdirAll ( rootDir , 0755 )
2019-02-03 09:10:53 +01:00
err := lxd . Unpack ( filePath , rootDir , false , false , nil )
2018-02-02 17:36:48 +01:00
if err != nil {
return fmt . Errorf ( "Failed to unpack tarball: %s" , err )
}
return nil
}
func getLatestRelease ( URL , release , arch string ) string {
resp , err := http . Get ( URL + path . Join ( "/" , "releases" , release , "release" ) )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return ""
}
defer resp . Body . Close ( )
body , err := ioutil . ReadAll ( resp . Body )
regex := regexp . MustCompile ( fmt . Sprintf ( "ubuntu-base-\\d{2}\\.\\d{2}(\\.\\d+)?-base-%s.tar.gz" , arch ) )
releases := regex . FindAllString ( string ( body ) , - 1 )
if len ( releases ) > 1 {
return string ( releases [ len ( releases ) - 1 ] )
}
return ""
}