2018-02-02 17:36:48 +01:00
package shared
import (
2018-03-07 18:43:10 +01:00
"bytes"
2018-02-14 17:59:39 +01:00
"fmt"
2018-02-02 17:36:48 +01:00
"io"
2018-03-06 14:36:52 +01:00
"io/ioutil"
2018-02-02 17:36:48 +01:00
"os"
"os/exec"
2018-03-06 14:36:52 +01:00
"path"
2018-02-14 17:59:39 +01:00
"path/filepath"
2018-02-22 16:34:47 +01:00
"regexp"
"strconv"
2018-03-13 15:05:45 -04:00
"strings"
2018-02-22 16:34:47 +01:00
"time"
2018-02-14 17:59:39 +01:00
lxd "github.com/lxc/lxd/shared"
2018-03-07 11:40:50 +01:00
"gopkg.in/flosch/pongo2.v3"
2018-02-02 17:36:48 +01:00
)
2018-03-08 10:57:58 +01:00
// EnvVariable represents a environment variable.
type EnvVariable struct {
Key string
Value string
Set bool
}
2018-02-02 17:36:48 +01:00
// Copy copies a file.
func Copy ( src , dest string ) error {
var err error
srcFile , err := os . Open ( src )
if err != nil {
return err
}
defer srcFile . Close ( )
destFile , err := os . Create ( dest )
if err != nil {
return err
}
defer destFile . Close ( )
_ , err = io . Copy ( destFile , srcFile )
if err != nil {
return err
}
return destFile . Sync ( )
}
// RunCommand runs a command hereby setting the SHELL and PATH env variables,
// and redirecting the process's stdout and stderr to the real stdout and stderr
// respectively.
func RunCommand ( name string , arg ... string ) error {
cmd := exec . Command ( name , arg ... )
cmd . Stdin = os . Stdin
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
return cmd . Run ( )
}
2018-02-14 17:59:39 +01:00
2018-03-07 18:43:10 +01:00
// RunScript runs a script hereby setting the SHELL and PATH env variables,
// and redirecting the process's stdout and stderr to the real stdout and stderr
// respectively.
func RunScript ( content string ) error {
cmd := exec . Command ( "sh" )
2018-03-08 10:57:58 +01:00
cmd . Stdin = bytes . NewBufferString ( content )
2018-03-07 18:43:10 +01:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
return cmd . Run ( )
}
2018-02-14 17:59:39 +01:00
// VerifyFile verifies a file using gpg.
2018-02-16 16:05:42 +01:00
func VerifyFile ( signedFile , signatureFile string , keys [ ] string , keyserver string ) ( bool , error ) {
2018-03-06 14:36:52 +01:00
keyring , err := CreateGPGKeyring ( keyserver , keys )
2018-02-14 17:59:39 +01:00
if err != nil {
return false , err
}
2018-03-06 14:36:52 +01:00
gpgDir := path . Dir ( keyring )
2018-02-14 17:59:39 +01:00
defer os . RemoveAll ( gpgDir )
if signatureFile != "" {
2018-03-06 14:36:52 +01:00
out , err := lxd . RunCommand ( "gpg" , "--homedir" , gpgDir , "--keyring" , keyring ,
"--verify" , signatureFile , signedFile )
2018-02-14 17:59:39 +01:00
if err != nil {
return false , fmt . Errorf ( "Failed to verify: %s" , out )
}
} else {
2018-03-06 14:36:52 +01:00
out , err := lxd . RunCommand ( "gpg" , "--homedir" , gpgDir , "--keyring" , keyring ,
"--verify" , signedFile )
2018-02-14 17:59:39 +01:00
if err != nil {
return false , fmt . Errorf ( "Failed to verify: %s" , out )
}
}
return true , nil
}
2018-02-12 16:19:29 +01:00
2018-02-28 14:57:28 +01:00
// CreateGPGKeyring creates a new GPG keyring.
func CreateGPGKeyring ( keyserver string , keys [ ] string ) ( string , error ) {
2018-03-06 14:36:52 +01:00
gpgDir , err := ioutil . TempDir ( os . TempDir ( ) , "distrobuilder." )
if err != nil {
return "" , fmt . Errorf ( "Failed to create gpg directory: %s" , err )
}
2018-02-28 14:57:28 +01:00
2018-03-06 14:36:52 +01:00
err = os . MkdirAll ( gpgDir , 0700 )
2018-02-28 14:57:28 +01:00
if err != nil {
return "" , err
}
2018-02-28 15:58:13 +01:00
args := [ ] string { "--homedir" , gpgDir }
if keyserver != "" {
args = append ( args , "--keyserver" , keyserver )
}
args = append ( args , append ( [ ] string { "--recv-keys" } , keys ... ) ... )
out , err := lxd . RunCommand ( "gpg" , args ... )
2018-02-28 14:57:28 +01:00
if err != nil {
os . RemoveAll ( gpgDir )
return "" , fmt . Errorf ( "Failed to create keyring: %s" , out )
2018-03-06 12:15:51 +01:00
}
// Export keys to support gpg1 and gpg2
out , err = lxd . RunCommand ( "gpg" , "--homedir" , gpgDir , "--export" , "--output" ,
2018-03-06 14:36:52 +01:00
filepath . Join ( gpgDir , "distrobuilder.gpg" ) )
2018-03-06 12:15:51 +01:00
if err != nil {
os . RemoveAll ( gpgDir )
return "" , fmt . Errorf ( "Failed to export keyring: %s" , out )
2018-02-28 14:57:28 +01:00
}
2018-03-06 14:36:52 +01:00
return filepath . Join ( gpgDir , "distrobuilder.gpg" ) , nil
2018-02-28 14:57:28 +01:00
}
2018-03-07 10:44:33 +01:00
// Pack creates an uncompressed tarball.
func Pack ( filename , compression , path string , args ... string ) error {
err := RunCommand ( "tar" , append ( [ ] string { "-cf" , filename , "-C" , path } , args ... ) ... )
if err != nil {
return err
}
return compressTarball ( filename , compression )
}
// PackUpdate updates an existing tarball.
func PackUpdate ( filename , compression , path string , args ... string ) error {
err := RunCommand ( "tar" , append ( [ ] string { "-uf" , filename , "-C" , path } , args ... ) ... )
if err != nil {
return err
}
return compressTarball ( filename , compression )
}
// compressTarball compresses a tarball, or not.
func compressTarball ( filename , compression string ) error {
switch compression {
case "lzop" :
// lzo does not remove the uncompressed file per default
defer os . Remove ( filename )
fallthrough
case "bzip2" , "xz" , "lzip" , "lzma" , "gzip" :
return RunCommand ( compression , "-f" , filename )
}
// Do not compress
return nil
2018-02-12 16:19:29 +01:00
}
2018-02-22 16:34:47 +01:00
//GetExpiryDate returns an expiry date based on the creationDate and format.
func GetExpiryDate ( creationDate time . Time , format string ) time . Time {
regex := regexp . MustCompile ( ` (?:(\d+)(s|m|h|d|w))* ` )
expiryDate := creationDate
for _ , match := range regex . FindAllStringSubmatch ( format , - 1 ) {
// Ignore empty matches
if match [ 0 ] == "" {
continue
}
var duration time . Duration
switch match [ 2 ] {
case "s" :
duration = time . Second
case "m" :
duration = time . Minute
case "h" :
duration = time . Hour
case "d" :
duration = 24 * time . Hour
case "w" :
duration = 7 * 24 * time . Hour
}
// Ignore any error since it will be an integer.
value , _ := strconv . Atoi ( match [ 1 ] )
expiryDate = expiryDate . Add ( time . Duration ( value ) * duration )
}
return expiryDate
}
2018-03-07 11:40:50 +01:00
// RenderTemplate renders a pongo2 template.
func RenderTemplate ( template string , ctx pongo2 . Context ) ( string , error ) {
var (
err error
ret string
)
// Load template from string
2018-03-13 15:05:45 -04:00
tpl , err := pongo2 . FromString ( "{% autoescape off %}" + template + "{% endautoescape %}" )
2018-03-07 11:40:50 +01:00
if err != nil {
return ret , err
}
// Get rendered template
ret , err = tpl . Execute ( ctx )
if err != nil {
return ret , err
}
2018-03-13 15:05:45 -04:00
// Looks like we're nesting templates so run pongo again
if strings . Contains ( ret , "{{" ) || strings . Contains ( ret , "{%" ) {
return RenderTemplate ( ret , ctx )
}
2018-03-07 11:40:50 +01:00
return ret , err
}
2018-03-08 10:57:58 +01:00
// SetEnvVariables sets the provided environment variables and returns the
// old ones.
func SetEnvVariables ( env [ ] EnvVariable ) [ ] EnvVariable {
oldEnv := make ( [ ] EnvVariable , len ( env ) )
for i := 0 ; i < len ( env ) ; i ++ {
// Check whether the env variables are set at the moment
oldVal , set := os . LookupEnv ( env [ i ] . Key )
// Store old env variables
oldEnv [ i ] . Key = env [ i ] . Key
oldEnv [ i ] . Value = oldVal
oldEnv [ i ] . Set = set
if env [ i ] . Set {
os . Setenv ( env [ i ] . Key , env [ i ] . Value )
} else {
os . Unsetenv ( env [ i ] . Key )
}
}
return oldEnv
}