2021-04-07 13:54:38 +00:00
// Copyright © 2019-2021 Ettore Di Giacinto <mudler@sabayon.org>
2019-11-04 16:16:13 +00:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package compiler
import (
2019-11-14 16:45:21 +00:00
"fmt"
2019-11-04 16:16:13 +00:00
"io/ioutil"
2019-11-10 09:48:07 +00:00
"os"
2021-04-12 17:00:36 +00:00
"path"
2019-11-10 09:48:07 +00:00
"path/filepath"
2020-10-04 17:16:01 +00:00
2019-12-03 16:26:53 +00:00
"regexp"
"strings"
2019-11-11 09:22:55 +00:00
"sync"
2020-07-12 13:27:50 +00:00
"time"
2019-11-04 16:16:13 +00:00
2021-04-14 14:49:43 +00:00
"github.com/imdario/mergo"
2020-11-13 17:25:44 +00:00
bus "github.com/mudler/luet/pkg/bus"
2021-04-12 17:00:36 +00:00
"github.com/mudler/luet/pkg/compiler/backend"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact"
"github.com/mudler/luet/pkg/compiler/types/options"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
2019-11-04 16:16:13 +00:00
"github.com/mudler/luet/pkg/helpers"
2019-12-06 15:28:42 +00:00
. "github.com/mudler/luet/pkg/logger"
2019-11-04 16:16:13 +00:00
pkg "github.com/mudler/luet/pkg/package"
2019-11-11 18:19:13 +00:00
"github.com/mudler/luet/pkg/solver"
2019-11-11 09:22:55 +00:00
"github.com/pkg/errors"
2021-04-14 14:49:43 +00:00
"gopkg.in/yaml.v2"
2019-11-04 16:16:13 +00:00
)
const BuildFile = "build.yaml"
2020-10-04 17:16:01 +00:00
const DefinitionFile = "definition.yaml"
2020-11-14 23:13:46 +00:00
const CollectionFile = "collection.yaml"
2019-11-04 16:16:13 +00:00
2021-04-12 17:00:36 +00:00
type ArtifactIndex [ ] * artifact . PackageArtifact
func ( i ArtifactIndex ) CleanPath ( ) ArtifactIndex {
newIndex := ArtifactIndex { }
for _ , art := range i {
2021-04-14 14:49:43 +00:00
copy := art . ShallowCopy ( )
copy . Path = path . Base ( art . Path )
newIndex = append ( newIndex , copy )
2021-04-12 17:00:36 +00:00
}
return newIndex
}
2019-11-04 16:16:13 +00:00
type LuetCompiler struct {
2021-04-12 17:00:36 +00:00
//*tree.CompilerRecipe
Backend CompilerBackend
Database pkg . PackageDatabase
Options options . Compiler
2019-11-04 16:16:13 +00:00
}
2021-04-12 17:00:36 +00:00
func NewCompiler ( p ... options . Option ) * LuetCompiler {
c := options . NewDefaultCompiler ( )
c . Apply ( p ... )
2021-04-07 13:54:38 +00:00
2021-04-12 17:00:36 +00:00
return & LuetCompiler { Options : * c }
2019-11-04 16:16:13 +00:00
}
2021-04-07 13:54:38 +00:00
2021-04-12 17:00:36 +00:00
func NewLuetCompiler ( backend CompilerBackend , db pkg . PackageDatabase , compilerOpts ... options . Option ) * LuetCompiler {
// The CompilerRecipe will gives us a tree with only build deps listed.
2021-04-07 13:54:38 +00:00
2021-04-12 17:00:36 +00:00
c := NewCompiler ( compilerOpts ... )
// c.Options.BackendType
c . Backend = backend
c . Database = db
// c.CompilerRecipe = &tree.CompilerRecipe{
// Recipe: tree.Recipe{Database: db},
// }
2019-12-30 11:53:32 +00:00
2021-04-12 17:00:36 +00:00
return c
2019-12-30 13:52:34 +00:00
}
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) compilerWorker ( i int , wg * sync . WaitGroup , cspecs chan * compilerspec . LuetCompilationSpec , a * [ ] * artifact . PackageArtifact , m * sync . Mutex , concurrency int , keepPermissions bool , errors chan error ) {
2019-11-11 09:22:55 +00:00
defer wg . Done ( )
for s := range cspecs {
2019-11-15 23:38:07 +00:00
ar , err := cs . compile ( concurrency , keepPermissions , s )
2019-11-11 09:22:55 +00:00
if err != nil {
errors <- err
}
m . Lock ( )
* a = append ( * a , ar )
m . Unlock ( )
}
}
2019-12-30 11:53:32 +00:00
2021-04-07 13:54:38 +00:00
// CompileWithReverseDeps compiles the supplied compilationspecs and their reverse dependencies
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) CompileWithReverseDeps ( keepPermissions bool , ps * compilerspec . LuetCompilationspecs ) ( [ ] * artifact . PackageArtifact , [ ] error ) {
2019-12-30 13:46:45 +00:00
artifacts , err := cs . CompileParallel ( keepPermissions , ps )
2019-11-15 17:11:26 +00:00
if len ( err ) != 0 {
return artifacts , err
}
2019-11-16 13:16:44 +00:00
Info ( ":ant: Resolving reverse dependencies" )
2021-04-12 17:00:36 +00:00
toCompile := compilerspec . NewLuetCompilationspecs ( )
2019-11-15 17:11:26 +00:00
for _ , a := range artifacts {
2019-11-29 18:01:49 +00:00
2021-04-12 17:00:36 +00:00
revdeps := a . CompileSpec . GetPackage ( ) . Revdeps ( cs . Database )
2019-11-15 17:11:26 +00:00
for _ , r := range revdeps {
spec , asserterr := cs . FromPackage ( r )
if err != nil {
return nil , append ( err , asserterr )
}
spec . SetOutputPath ( ps . All ( ) [ 0 ] . GetOutputPath ( ) )
toCompile . Add ( spec )
}
}
uniques := toCompile . Unique ( ) . Remove ( ps )
for _ , u := range uniques . All ( ) {
2019-11-16 13:40:58 +00:00
Info ( " :arrow_right_hook:" , u . GetPackage ( ) . GetName ( ) , ":leaves:" , u . GetPackage ( ) . GetVersion ( ) , "(" , u . GetPackage ( ) . GetCategory ( ) , ")" )
2019-11-15 17:11:26 +00:00
}
2019-12-30 13:46:45 +00:00
artifacts2 , err := cs . CompileParallel ( keepPermissions , uniques )
2019-11-15 17:11:26 +00:00
return append ( artifacts , artifacts2 ... ) , err
}
2019-11-11 09:22:55 +00:00
2021-04-07 13:54:38 +00:00
// CompileParallel compiles the supplied compilationspecs in parallel
// to note, no specific heuristic is implemented, and the specs are run in parallel as they are.
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) CompileParallel ( keepPermissions bool , ps * compilerspec . LuetCompilationspecs ) ( [ ] * artifact . PackageArtifact , [ ] error ) {
all := make ( chan * compilerspec . LuetCompilationSpec )
artifacts := [ ] * artifact . PackageArtifact { }
2019-11-11 09:22:55 +00:00
mutex := & sync . Mutex { }
2019-11-15 17:11:26 +00:00
errors := make ( chan error , ps . Len ( ) )
2019-11-11 09:22:55 +00:00
var wg = new ( sync . WaitGroup )
2021-04-12 17:00:36 +00:00
for i := 0 ; i < cs . Options . Concurrency ; i ++ {
2019-11-11 09:22:55 +00:00
wg . Add ( 1 )
2021-04-12 17:00:36 +00:00
go cs . compilerWorker ( i , wg , all , & artifacts , mutex , cs . Options . Concurrency , keepPermissions , errors )
2019-11-11 09:22:55 +00:00
}
2019-11-15 17:11:26 +00:00
for _ , p := range ps . All ( ) {
2019-11-15 23:38:07 +00:00
asserts , err := cs . ComputeDepTree ( p )
if err != nil {
panic ( err )
}
p . SetSourceAssertion ( asserts )
2019-11-11 09:22:55 +00:00
all <- p
}
close ( all )
wg . Wait ( )
close ( errors )
var allErrors [ ] error
for e := range errors {
allErrors = append ( allErrors , e )
}
return artifacts , allErrors
}
2020-11-08 14:35:24 +00:00
func ( cs * LuetCompiler ) stripFromRootfs ( includes [ ] string , rootfs string , include bool ) error {
2019-12-03 16:26:53 +00:00
var includeRegexp [ ] * regexp . Regexp
for _ , i := range includes {
r , e := regexp . Compile ( i )
if e != nil {
return errors . Wrap ( e , "Could not compile regex in the include of the package" )
}
includeRegexp = append ( includeRegexp , r )
}
toRemove := [ ] string { }
// the function that handles each file or dir
var ff = func ( currentpath string , info os . FileInfo , err error ) error {
// if info.Name() != DefinitionFile {
// return nil // Skip with no errors
// }
if currentpath == rootfs {
return nil
}
abspath := strings . ReplaceAll ( currentpath , rootfs , "" )
match := false
for _ , i := range includeRegexp {
if i . MatchString ( abspath ) {
match = true
}
}
2020-11-08 14:35:24 +00:00
if include && ! match || ! include && match {
2019-12-03 16:26:53 +00:00
toRemove = append ( toRemove , currentpath )
}
return nil
}
err := filepath . Walk ( rootfs , ff )
if err != nil {
return err
}
for _ , s := range toRemove {
e := os . RemoveAll ( s )
if e != nil {
Warning ( "Failed removing" , s , e . Error ( ) )
return e
}
}
return nil
}
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) unpackFs ( concurrency int , keepPermissions bool , p * compilerspec . LuetCompilationSpec , runnerOpts backend . Options ) ( * artifact . PackageArtifact , error ) {
2021-01-24 11:27:07 +00:00
rootfs , err := ioutil . TempDir ( p . GetOutputPath ( ) , "rootfs" )
if err != nil {
return nil , errors . Wrap ( err , "Could not create tempdir" )
}
defer os . RemoveAll ( rootfs ) // clean up
2021-04-12 17:00:36 +00:00
err = cs . Backend . ExtractRootfs ( backend . Options {
2021-01-24 11:27:07 +00:00
ImageName : runnerOpts . ImageName , Destination : rootfs } , keepPermissions )
if err != nil {
return nil , errors . Wrap ( err , "Could not extract rootfs" )
}
2020-11-10 17:14:18 +00:00
if p . GetPackageDir ( ) != "" {
Info ( ":tophat: Packing from output dir" , p . GetPackageDir ( ) )
rootfs = filepath . Join ( rootfs , p . GetPackageDir ( ) )
}
if len ( p . GetIncludes ( ) ) > 0 {
// strip from includes
cs . stripFromRootfs ( p . GetIncludes ( ) , rootfs , true )
}
if len ( p . GetExcludes ( ) ) > 0 {
2021-01-24 11:27:07 +00:00
// strip from excludes
2020-11-10 17:14:18 +00:00
cs . stripFromRootfs ( p . GetExcludes ( ) , rootfs , false )
}
2021-04-12 17:00:36 +00:00
a := artifact . NewPackageArtifact ( p . Rel ( p . GetPackage ( ) . GetFingerPrint ( ) + ".package.tar" ) )
a . CompressionType = cs . Options . CompressionType
2020-11-10 17:14:18 +00:00
2021-04-12 17:00:36 +00:00
if err := a . Compress ( rootfs , concurrency ) ; err != nil {
2020-11-10 17:14:18 +00:00
return nil , errors . Wrap ( err , "Error met while creating package archive" )
}
2021-04-12 17:00:36 +00:00
a . CompileSpec = p
return a , nil
2020-11-10 17:14:18 +00:00
}
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) unpackDelta ( concurrency int , keepPermissions bool , p * compilerspec . LuetCompilationSpec , builderOpts , runnerOpts backend . Options ) ( * artifact . PackageArtifact , error ) {
2021-01-24 11:27:07 +00:00
rootfs , err := ioutil . TempDir ( p . GetOutputPath ( ) , "rootfs" )
if err != nil {
return nil , errors . Wrap ( err , "Could not create tempdir" )
}
defer os . RemoveAll ( rootfs ) // clean up
2020-11-10 17:14:18 +00:00
pkgTag := ":package: " + p . GetPackage ( ) . HumanReadableString ( )
2020-12-15 16:01:56 +00:00
if cs . Options . PullFirst && ! cs . Backend . ImageExists ( builderOpts . ImageName ) && cs . Backend . ImageAvailable ( builderOpts . ImageName ) {
err := cs . Backend . DownloadImage ( builderOpts )
if err != nil {
return nil , errors . Wrap ( err , "Could not pull image" )
}
}
2021-01-24 11:27:07 +00:00
2020-11-10 17:14:18 +00:00
Info ( pkgTag , ":hammer: Generating delta" )
2021-04-12 17:00:36 +00:00
diffs , err := GenerateChanges ( cs . Backend , builderOpts , runnerOpts )
2020-11-10 17:14:18 +00:00
if err != nil {
return nil , errors . Wrap ( err , "Could not generate changes from layers" )
}
2021-01-24 11:41:56 +00:00
2021-01-24 18:04:57 +00:00
Debug ( "Extracting image to grab files from delta" )
2021-04-12 17:00:36 +00:00
if err := cs . Backend . ExtractRootfs ( backend . Options {
2021-01-24 11:41:56 +00:00
ImageName : runnerOpts . ImageName , Destination : rootfs } , keepPermissions ) ; err != nil {
return nil , errors . Wrap ( err , "Could not extract rootfs" )
}
2021-04-12 17:00:36 +00:00
artifact , err := artifact . ExtractArtifactFromDelta ( rootfs , p . Rel ( p . GetPackage ( ) . GetFingerPrint ( ) + ".package.tar" ) , diffs , concurrency , keepPermissions , p . GetIncludes ( ) , p . GetExcludes ( ) , cs . Options . CompressionType )
2020-11-10 17:14:18 +00:00
if err != nil {
return nil , errors . Wrap ( err , "Could not generate deltas" )
}
2021-04-12 17:00:36 +00:00
artifact . CompileSpec = p
2020-11-10 17:14:18 +00:00
return artifact , nil
}
func ( cs * LuetCompiler ) buildPackageImage ( image , buildertaggedImage , packageImage string ,
2020-11-10 17:40:35 +00:00
concurrency int , keepPermissions bool ,
2021-04-12 17:00:36 +00:00
p * compilerspec . LuetCompilationSpec ) ( backend . Options , backend . Options , error ) {
2020-11-10 17:14:18 +00:00
2021-04-12 17:00:36 +00:00
var runnerOpts , builderOpts backend . Options
2020-06-23 16:44:29 +00:00
2020-10-30 18:15:04 +00:00
pkgTag := ":package: " + p . GetPackage ( ) . HumanReadableString ( )
2020-06-23 16:44:29 +00:00
// Use packageImage as salt into the fp being used
// so the hash is unique also in cases where
// some package deps does have completely different
// depgraphs
2021-01-29 09:50:32 +00:00
// TODO: We should use the image tag, or pass by the package assertion hash which is unique
2020-06-23 16:44:29 +00:00
// and identifies the deptree of the package.
2021-01-29 09:50:32 +00:00
fp := p . GetPackage ( ) . HashFingerprint ( helpers . StripRegistryFromImage ( packageImage ) )
2020-06-23 16:44:29 +00:00
2020-06-03 19:00:30 +00:00
if buildertaggedImage == "" {
2021-04-12 17:00:36 +00:00
buildertaggedImage = cs . Options . PushImageRepository + ":builder-" + fp
2020-06-23 16:44:29 +00:00
Debug ( pkgTag , "Creating intermediary image" , buildertaggedImage , "from" , image )
2020-06-03 19:00:30 +00:00
}
2020-06-23 16:44:29 +00:00
// TODO: Cleanup, not actually hit
2020-06-03 19:00:30 +00:00
if packageImage == "" {
2021-04-07 13:54:38 +00:00
return runnerOpts , builderOpts , errors . New ( "no package image given" )
2020-06-03 19:00:30 +00:00
}
2020-06-23 16:44:29 +00:00
2019-11-11 18:19:13 +00:00
p . SetSeedImage ( image ) // In this case, we ignore the build deps as we suppose that the image has them - otherwise we recompose the tree with a solver,
// and we build all the images first.
2019-12-02 15:33:59 +00:00
2019-11-17 12:27:32 +00:00
err := os . MkdirAll ( p . Rel ( "build" ) , os . ModePerm )
if err != nil {
2020-11-10 17:14:18 +00:00
return builderOpts , runnerOpts , errors . Wrap ( err , "Error met while creating tempdir for building" )
2019-11-17 12:27:32 +00:00
}
buildDir , err := ioutil . TempDir ( p . Rel ( "build" ) , "pack" )
if err != nil {
2020-11-10 17:14:18 +00:00
return builderOpts , runnerOpts , errors . Wrap ( err , "Error met while creating tempdir for building" )
2019-11-17 12:27:32 +00:00
}
defer os . RemoveAll ( buildDir ) // clean up
2019-11-05 16:36:22 +00:00
2019-11-11 18:19:13 +00:00
// First we copy the source definitions into the output - we create a copy which the builds will need (we need to cache this phase somehow)
2019-11-17 12:27:32 +00:00
err = helpers . CopyDir ( p . GetPackage ( ) . GetPath ( ) , buildDir )
2019-11-11 18:19:13 +00:00
if err != nil {
2020-11-10 17:14:18 +00:00
return builderOpts , runnerOpts , errors . Wrap ( err , "Could not copy package sources" )
2019-11-11 18:19:13 +00:00
}
2020-02-13 13:15:43 +00:00
// Copy file into the build context, the compilespec might have requested to do so.
if len ( p . GetRetrieve ( ) ) > 0 {
err := p . CopyRetrieves ( buildDir )
if err != nil {
Warning ( "Failed copying retrieves" , err . Error ( ) )
}
}
2019-11-11 18:19:13 +00:00
// First we create the builder image
2020-11-10 17:40:35 +00:00
if err := p . WriteBuildImageDefinition ( filepath . Join ( buildDir , p . GetPackage ( ) . GetFingerPrint ( ) + "-builder.dockerfile" ) ) ; err != nil {
return builderOpts , runnerOpts , errors . Wrap ( err , "Could not generate image definition" )
}
2020-12-07 17:58:14 +00:00
if len ( p . GetPreBuildSteps ( ) ) == 0 {
buildertaggedImage = image
}
2020-11-10 17:14:18 +00:00
// Then we write the step image, which uses the builder one
2020-11-10 17:40:35 +00:00
if err := p . WriteStepImageDefinition ( buildertaggedImage , filepath . Join ( buildDir , p . GetPackage ( ) . GetFingerPrint ( ) + ".dockerfile" ) ) ; err != nil {
return builderOpts , runnerOpts , errors . Wrap ( err , "Could not generate image definition" )
}
2020-11-10 17:14:18 +00:00
2021-04-12 17:00:36 +00:00
builderOpts = backend . Options {
2019-11-11 18:19:13 +00:00
ImageName : buildertaggedImage ,
SourcePath : buildDir ,
DockerFileName : p . GetPackage ( ) . GetFingerPrint ( ) + "-builder.dockerfile" ,
Destination : p . Rel ( p . GetPackage ( ) . GetFingerPrint ( ) + "-builder.image.tar" ) ,
2021-04-12 17:00:36 +00:00
BackendArgs : cs . Options . BackendArgs ,
2019-11-11 18:19:13 +00:00
}
2021-04-12 17:00:36 +00:00
runnerOpts = backend . Options {
2020-11-10 17:14:18 +00:00
ImageName : packageImage ,
SourcePath : buildDir ,
DockerFileName : p . GetPackage ( ) . GetFingerPrint ( ) + ".dockerfile" ,
Destination : p . Rel ( p . GetPackage ( ) . GetFingerPrint ( ) + ".image.tar" ) ,
2021-04-12 17:00:36 +00:00
BackendArgs : cs . Options . BackendArgs ,
2020-11-10 17:14:18 +00:00
}
2019-11-10 09:48:07 +00:00
2021-04-12 17:00:36 +00:00
buildAndPush := func ( opts backend . Options ) error {
2020-11-10 17:40:35 +00:00
buildImage := true
if cs . Options . PullFirst {
2020-11-28 17:03:43 +00:00
err := cs . Backend . DownloadImage ( opts )
if err == nil {
2020-11-10 17:40:35 +00:00
buildImage = false
2020-11-28 17:03:43 +00:00
} else {
2020-12-02 17:24:32 +00:00
Warning ( "Failed to download '" + opts . ImageName + "'. Will keep going and build the image unless you use --fatal" )
2020-11-28 17:03:43 +00:00
Warning ( err . Error ( ) )
2020-11-10 17:14:18 +00:00
}
2020-02-15 13:45:05 +00:00
}
2020-11-10 17:40:35 +00:00
if buildImage {
if err := cs . Backend . BuildImage ( opts ) ; err != nil {
2021-04-07 13:54:38 +00:00
return errors . Wrapf ( err , "Could not build image: %s %s" , image , opts . DockerFileName )
2020-11-10 17:14:18 +00:00
}
2020-11-10 17:40:35 +00:00
if cs . Options . Push {
if err = cs . Backend . Push ( opts ) ; err != nil {
2021-04-07 13:54:38 +00:00
return errors . Wrapf ( err , "Could not push image: %s %s" , image , opts . DockerFileName )
2020-11-10 17:40:35 +00:00
}
2020-11-10 17:14:18 +00:00
}
2020-11-10 17:40:35 +00:00
}
return nil
2019-11-11 18:19:13 +00:00
}
2020-12-06 22:50:51 +00:00
if len ( p . GetPreBuildSteps ( ) ) != 0 {
Info ( pkgTag , ":whale: Generating 'builder' image from" , image , "as" , buildertaggedImage , "with prelude steps" )
if err := buildAndPush ( builderOpts ) ; err != nil {
2021-04-07 13:54:38 +00:00
return builderOpts , runnerOpts , errors . Wrapf ( err , "Could not push image: %s %s" , image , builderOpts . DockerFileName )
2020-12-06 22:50:51 +00:00
}
2019-12-02 15:33:59 +00:00
}
2020-12-06 22:50:51 +00:00
2020-12-07 18:39:56 +00:00
// Even if we might not have any steps to build, we do that so we can tag the image used in this moment and use that to cache it in a registry, or in the system.
// acting as a docker tag.
Info ( pkgTag , ":whale: Generating 'package' image from" , buildertaggedImage , "as" , packageImage , "with build steps" )
if err := buildAndPush ( runnerOpts ) ; err != nil {
2021-04-07 13:54:38 +00:00
return builderOpts , runnerOpts , errors . Wrapf ( err , "Could not push image: %s %s" , image , runnerOpts . DockerFileName )
2020-02-21 21:00:35 +00:00
}
2020-11-10 17:40:35 +00:00
2020-11-10 17:14:18 +00:00
return builderOpts , runnerOpts , nil
}
2020-02-21 21:00:35 +00:00
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) genArtifact ( p * compilerspec . LuetCompilationSpec , builderOpts , runnerOpts backend . Options , concurrency int , keepPermissions bool ) ( * artifact . PackageArtifact , error ) {
2019-11-10 09:48:07 +00:00
2021-04-12 17:00:36 +00:00
// generate *artifact.PackageArtifact
var a * artifact . PackageArtifact
2020-11-10 17:14:18 +00:00
var rootfs string
var err error
pkgTag := ":package: " + p . GetPackage ( ) . HumanReadableString ( )
2019-11-17 14:45:55 +00:00
2021-01-03 19:08:04 +00:00
// We can't generate delta in this case. It implies the package is a virtual, and nothing has to be done really
if p . EmptyPackage ( ) {
2020-12-06 22:50:51 +00:00
fakePackage := p . Rel ( p . GetPackage ( ) . GetFingerPrint ( ) + ".package.tar" )
2020-12-07 16:20:32 +00:00
2021-01-03 19:08:04 +00:00
rootfs , err = ioutil . TempDir ( p . GetOutputPath ( ) , "rootfs" )
2020-12-06 22:50:51 +00:00
if err != nil {
2021-01-03 19:08:04 +00:00
return nil , errors . Wrap ( err , "Could not create tempdir" )
2020-12-06 22:50:51 +00:00
}
2021-01-03 19:08:04 +00:00
defer os . RemoveAll ( rootfs ) // clean up
2020-12-06 22:50:51 +00:00
2021-04-12 17:00:36 +00:00
a := artifact . NewPackageArtifact ( fakePackage )
a . CompressionType = cs . Options . CompressionType
2021-01-03 19:08:04 +00:00
2021-04-12 17:00:36 +00:00
if err := a . Compress ( rootfs , concurrency ) ; err != nil {
2021-01-03 19:08:04 +00:00
return nil , errors . Wrap ( err , "Error met while creating package archive" )
}
2021-04-12 17:00:36 +00:00
a . CompileSpec = p
a . CompileSpec . GetPackage ( ) . SetBuildTimestamp ( time . Now ( ) . String ( ) )
2021-01-03 19:08:04 +00:00
2021-04-12 17:00:36 +00:00
err = a . WriteYaml ( p . GetOutputPath ( ) )
2021-01-03 19:08:04 +00:00
if err != nil {
2021-04-12 17:00:36 +00:00
return a , errors . Wrap ( err , "Failed while writing metadata file" )
2021-01-03 19:08:04 +00:00
}
Info ( pkgTag , " :white_check_mark: done (empty virtual package)" )
2021-04-12 17:00:36 +00:00
return a , nil
2020-12-06 22:50:51 +00:00
}
2021-01-03 19:08:04 +00:00
if p . UnpackedPackage ( ) {
2020-11-10 17:14:18 +00:00
// Take content of container as a base for our package files
2021-04-12 17:00:36 +00:00
a , err = cs . unpackFs ( concurrency , keepPermissions , p , runnerOpts )
2019-11-17 11:08:13 +00:00
if err != nil {
2020-12-15 16:01:56 +00:00
return nil , errors . Wrap ( err , "Error met while extracting image" )
2019-11-17 11:08:13 +00:00
}
2019-11-17 14:45:55 +00:00
} else {
2020-11-10 17:14:18 +00:00
// Generate delta between the two images
2021-04-12 17:00:36 +00:00
a , err = cs . unpackDelta ( concurrency , keepPermissions , p , builderOpts , runnerOpts )
2020-08-05 17:09:45 +00:00
if err != nil {
2020-12-15 16:01:56 +00:00
return nil , errors . Wrap ( err , "Error met while generating delta" )
2019-11-17 14:45:55 +00:00
}
2019-11-11 18:19:13 +00:00
}
2019-11-17 14:45:55 +00:00
2021-04-12 17:00:36 +00:00
filelist , err := a . FileList ( )
2020-04-13 08:52:41 +00:00
if err != nil {
2021-04-12 17:00:36 +00:00
return a , errors . Wrap ( err , "Failed getting package list" )
2020-04-13 08:52:41 +00:00
}
2021-04-12 17:00:36 +00:00
a . Files = filelist
a . CompileSpec . GetPackage ( ) . SetBuildTimestamp ( time . Now ( ) . String ( ) )
2020-07-12 13:27:50 +00:00
2021-04-12 17:00:36 +00:00
err = a . WriteYaml ( p . GetOutputPath ( ) )
2019-11-22 20:01:29 +00:00
if err != nil {
2021-04-12 17:00:36 +00:00
return a , errors . Wrap ( err , "Failed while writing metadata file" )
2019-11-22 20:01:29 +00:00
}
2019-11-26 19:11:51 +00:00
Info ( pkgTag , " :white_check_mark: Done" )
2021-04-12 17:00:36 +00:00
return a , nil
2019-11-11 18:19:13 +00:00
}
2019-11-17 11:08:13 +00:00
2021-04-07 13:54:38 +00:00
func ( cs * LuetCompiler ) waitForImages ( images [ ] string ) {
if cs . Options . PullFirst && cs . Options . Wait {
available , _ := oneOfImagesAvailable ( images , cs . Backend )
if ! available {
Info ( fmt . Sprintf ( "Waiting for image %s to be available... :zzz:" , images ) )
Spinner ( 22 )
defer SpinnerStop ( )
for ! available {
available , _ = oneOfImagesAvailable ( images , cs . Backend )
Info ( fmt . Sprintf ( "Image %s not available yet, sleeping" , images ) )
time . Sleep ( 5 * time . Second )
}
}
}
}
func oneOfImagesExists ( images [ ] string , b CompilerBackend ) ( bool , string ) {
for _ , i := range images {
if exists := b . ImageExists ( i ) ; exists {
return true , i
}
}
return false , ""
}
func oneOfImagesAvailable ( images [ ] string , b CompilerBackend ) ( bool , string ) {
for _ , i := range images {
if exists := b . ImageAvailable ( i ) ; exists {
return true , i
}
}
return false , ""
}
func ( cs * LuetCompiler ) resolveExistingImageHash ( imageHash string ) string {
var resolvedImage string
2021-04-12 17:00:36 +00:00
toChecklist := append ( [ ] string { fmt . Sprintf ( "%s:%s" , cs . Options . PushImageRepository , imageHash ) } ,
genImageList ( cs . Options . PullImageRepository , imageHash ) ... )
2021-04-07 13:54:38 +00:00
if exists , which := oneOfImagesExists ( toChecklist , cs . Backend ) ; exists {
resolvedImage = which
}
if cs . Options . PullFirst {
if exists , which := oneOfImagesAvailable ( toChecklist , cs . Backend ) ; exists {
resolvedImage = which
2020-12-18 22:19:18 +00:00
}
}
2021-04-07 13:54:38 +00:00
if resolvedImage == "" {
2021-04-12 17:00:36 +00:00
resolvedImage = fmt . Sprintf ( "%s:%s" , cs . Options . PushImageRepository , imageHash )
2021-04-07 13:54:38 +00:00
}
return resolvedImage
}
2021-04-12 17:00:36 +00:00
func LoadArtifactFromYaml ( spec * compilerspec . LuetCompilationSpec ) ( * artifact . PackageArtifact , error ) {
2021-04-14 14:49:43 +00:00
metaFile := spec . GetPackage ( ) . GetMetadataFilePath ( )
2021-04-12 17:00:36 +00:00
dat , err := ioutil . ReadFile ( spec . Rel ( metaFile ) )
if err != nil {
return nil , errors . Wrap ( err , "Error reading file " + metaFile )
}
art , err := artifact . NewPackageArtifactFromYaml ( dat )
if err != nil {
return nil , errors . Wrap ( err , "Error writing file " + metaFile )
}
// It is relative, set it back to abs
art . Path = spec . Rel ( art . Path )
return art , nil
}
func ( cs * LuetCompiler ) getImageArtifact ( hash string , p * compilerspec . LuetCompilationSpec ) ( * artifact . PackageArtifact , error ) {
2021-04-07 13:54:38 +00:00
// we check if there is an available image with the given hash and
// we return a full artifact if can be loaded locally.
2021-04-12 17:00:36 +00:00
toChecklist := append ( [ ] string { fmt . Sprintf ( "%s:%s" , cs . Options . PushImageRepository , hash ) } ,
genImageList ( cs . Options . PullImageRepository , hash ) ... )
2021-04-07 13:54:38 +00:00
exists , _ := oneOfImagesExists ( toChecklist , cs . Backend )
if art , err := LoadArtifactFromYaml ( p ) ; err == nil && exists { // If YAML is correctly loaded, and both images exists, no reason to rebuild.
2021-04-12 17:00:36 +00:00
Debug ( "Package reloaded from YAML. Skipping build" )
2021-04-07 13:54:38 +00:00
return art , nil
}
cs . waitForImages ( toChecklist )
available , _ := oneOfImagesAvailable ( toChecklist , cs . Backend )
if exists || ( cs . Options . PullFirst && available ) {
Debug ( "Image available, returning empty artifact" )
2021-04-12 17:00:36 +00:00
return & artifact . PackageArtifact { } , nil
2021-04-07 13:54:38 +00:00
}
return nil , errors . New ( "artifact not found" )
2020-12-18 22:19:18 +00:00
}
2021-04-07 13:54:38 +00:00
// compileWithImage compiles a PackageTagHash image using the image source, and tagging an indermediate
// image buildertaggedImage.
// Images that can be resolved from repositories are prefered over the local ones if PullFirst is set to true
// avoiding to rebuild images as much as possible
func ( cs * LuetCompiler ) compileWithImage ( image , buildertaggedImage string , packageTagHash string ,
2020-11-10 17:14:18 +00:00
concurrency int ,
keepPermissions , keepImg bool ,
2021-04-12 17:00:36 +00:00
p * compilerspec . LuetCompilationSpec , generateArtifact bool ) ( * artifact . PackageArtifact , error ) {
2020-11-10 17:14:18 +00:00
2021-01-03 19:08:04 +00:00
// If it is a virtual, check if we have to generate an empty artifact or not.
2021-02-09 18:05:16 +00:00
if generateArtifact && p . IsVirtual ( ) {
2021-04-12 17:00:36 +00:00
return cs . genArtifact ( p , backend . Options { } , backend . Options { } , concurrency , keepPermissions )
2021-02-09 18:05:16 +00:00
} else if p . IsVirtual ( ) {
2021-04-12 17:00:36 +00:00
return & artifact . PackageArtifact { } , nil
2021-01-03 19:08:04 +00:00
}
2020-12-14 17:41:39 +00:00
if ! generateArtifact {
2021-04-07 13:54:38 +00:00
// try to avoid regenerating the image if possible by checking the hash in the
// given repositories
// It is best effort. If we fail resolving, we will generate the images and keep going
if art , err := cs . getImageArtifact ( packageTagHash , p ) ; err == nil {
return art , nil
2020-12-14 17:32:32 +00:00
}
2020-11-10 17:14:18 +00:00
}
2021-04-07 13:54:38 +00:00
// always going to point at the destination from the repo defined
2021-04-12 17:00:36 +00:00
packageImage := fmt . Sprintf ( "%s:%s" , cs . Options . PushImageRepository , packageTagHash )
2020-11-10 17:40:35 +00:00
builderOpts , runnerOpts , err := cs . buildPackageImage ( image , buildertaggedImage , packageImage , concurrency , keepPermissions , p )
2020-11-10 17:14:18 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed building package image" )
}
2020-11-10 17:40:35 +00:00
if ! keepImg {
defer func ( ) {
// We keep them around, so to not reload them from the tar (which should be the "correct way") and we automatically share the same layers
if err := cs . Backend . RemoveImage ( builderOpts ) ; err != nil {
Warning ( "Could not remove image " , builderOpts . ImageName )
}
if err := cs . Backend . RemoveImage ( runnerOpts ) ; err != nil {
Warning ( "Could not remove image " , runnerOpts . ImageName )
}
} ( )
}
2020-11-10 17:14:18 +00:00
if ! generateArtifact {
2021-04-12 17:00:36 +00:00
return & artifact . PackageArtifact { } , nil
2020-11-10 17:14:18 +00:00
}
return cs . genArtifact ( p , builderOpts , runnerOpts , concurrency , keepPermissions )
}
2021-04-07 13:54:38 +00:00
// FromDatabase returns all the available compilation specs from a database. If the minimum flag is returned
// it will be computed a minimal subset that will guarantees that all packages are built ( if not targeting a single package explictly )
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) FromDatabase ( db pkg . PackageDatabase , minimum bool , dst string ) ( [ ] * compilerspec . LuetCompilationSpec , error ) {
compilerSpecs := compilerspec . NewLuetCompilationspecs ( )
2020-06-06 06:58:18 +00:00
w := db . World ( )
for _ , p := range w {
spec , err := cs . FromPackage ( p )
if err != nil {
return nil , err
}
if dst != "" {
spec . SetOutputPath ( dst )
}
compilerSpecs . Add ( spec )
}
switch minimum {
case true :
return cs . ComputeMinimumCompilableSet ( compilerSpecs . Unique ( ) . All ( ) ... )
default :
return compilerSpecs . Unique ( ) . All ( ) , nil
}
}
// ComputeMinimumCompilableSet strips specs that are eventually compiled by leafs
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) ComputeMinimumCompilableSet ( p ... * compilerspec . LuetCompilationSpec ) ( [ ] * compilerspec . LuetCompilationSpec , error ) {
2020-06-06 06:58:18 +00:00
// Generate a set with all the deps of the provided specs
// we will use that set to remove the deps from the list of provided compilation specs
allDependencies := solver . PackagesAssertions { } // Get all packages that will be in deps
2021-04-12 17:00:36 +00:00
result := [ ] * compilerspec . LuetCompilationSpec { }
2020-06-06 06:58:18 +00:00
for _ , spec := range p {
ass , err := cs . ComputeDepTree ( spec )
if err != nil {
return result , errors . Wrap ( err , "computin specs deptree" )
}
allDependencies = append ( allDependencies , ass . Drop ( spec . GetPackage ( ) ) ... )
}
for _ , spec := range p {
if found := allDependencies . Search ( spec . GetPackage ( ) . GetFingerPrint ( ) ) ; found == nil {
result = append ( result , spec )
}
}
return result , nil
}
2021-04-07 13:54:38 +00:00
// ComputeDepTree computes the dependency tree of a compilation spec and returns solver assertions
// in order to be able to compile the spec.
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) ComputeDepTree ( p * compilerspec . LuetCompilationSpec ) ( solver . PackagesAssertions , error ) {
2019-11-11 23:13:03 +00:00
2021-04-12 17:00:36 +00:00
s := solver . NewResolver ( cs . Options . SolverOptions . Options , pkg . NewInMemoryDatabase ( false ) , cs . Database , pkg . NewInMemoryDatabase ( false ) , cs . Options . SolverOptions . Resolver ( ) )
2019-11-29 18:01:49 +00:00
2020-04-04 12:29:08 +00:00
solution , err := s . Install ( pkg . Packages { p . GetPackage ( ) } )
2019-11-15 23:38:07 +00:00
if err != nil {
2020-02-18 17:22:18 +00:00
return nil , errors . Wrap ( err , "While computing a solution for " + p . GetPackage ( ) . HumanReadableString ( ) )
2019-11-15 23:38:07 +00:00
}
2019-11-29 18:01:52 +00:00
2020-04-09 15:07:57 +00:00
dependencies , err := solution . Order ( cs . Database , p . GetPackage ( ) . GetFingerPrint ( ) )
if err != nil {
return nil , errors . Wrap ( err , "While order a solution for " + p . GetPackage ( ) . HumanReadableString ( ) )
}
2019-12-17 18:32:31 +00:00
2019-11-15 23:38:07 +00:00
assertions := solver . PackagesAssertions { }
for _ , assertion := range dependencies { //highly dependent on the order
2019-11-29 18:01:49 +00:00
if assertion . Value {
2019-12-14 14:00:16 +00:00
nthsolution := dependencies . Cut ( assertion . Package )
2019-11-15 23:38:07 +00:00
assertion . Hash = solver . PackageHash {
2020-05-18 17:10:38 +00:00
BuildHash : nthsolution . HashFrom ( assertion . Package ) ,
2019-12-14 14:00:16 +00:00
PackageHash : nthsolution . AssertionHash ( ) ,
2019-11-15 23:38:07 +00:00
}
assertions = append ( assertions , assertion )
}
}
p . SetSourceAssertion ( assertions )
return assertions , nil
}
2021-04-07 13:54:38 +00:00
// Compile is a non-parallel version of CompileParallel. It builds the compilation specs and generates
// an artifact
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) Compile ( keepPermissions bool , p * compilerspec . LuetCompilationSpec ) ( * artifact . PackageArtifact , error ) {
2019-11-15 23:38:07 +00:00
asserts , err := cs . ComputeDepTree ( p )
if err != nil {
panic ( err )
}
p . SetSourceAssertion ( asserts )
2021-04-12 17:00:36 +00:00
return cs . compile ( cs . Options . Concurrency , keepPermissions , p )
2019-11-15 23:38:07 +00:00
}
2021-04-07 13:54:38 +00:00
func genImageList ( refs [ ] string , hash string ) [ ] string {
var res [ ] string
for _ , r := range refs {
res = append ( res , fmt . Sprintf ( "%s:%s" , r , hash ) )
}
return res
}
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) compile ( concurrency int , keepPermissions bool , p * compilerspec . LuetCompilationSpec ) ( * artifact . PackageArtifact , error ) {
2021-04-14 14:49:43 +00:00
if len ( p . BuildOptions . PullImageRepository ) != 0 {
orig := cs . Options . PullImageRepository
cs . Options . PullImageRepository = append ( orig , p . BuildOptions . PullImageRepository ... )
defer func ( ) { cs . Options . PullImageRepository = orig } ( )
}
2020-02-18 17:22:18 +00:00
Info ( ":package: Compiling" , p . GetPackage ( ) . HumanReadableString ( ) , ".... :coffee:" )
2019-11-12 07:48:07 +00:00
2021-01-03 19:08:04 +00:00
Debug ( fmt . Sprintf ( "%s: has images %t, empty package: %t" , p . GetPackage ( ) . HumanReadableString ( ) , p . HasImageSource ( ) , p . EmptyPackage ( ) ) )
if ! p . HasImageSource ( ) && ! p . EmptyPackage ( ) {
return nil ,
fmt . Errorf (
"%s is invalid: package has no dependencies and no seed image supplied while it has steps defined" ,
p . GetPackage ( ) . GetFingerPrint ( ) ,
)
2019-11-11 23:13:03 +00:00
}
2019-11-10 09:48:07 +00:00
2020-03-19 21:37:32 +00:00
targetAssertion := p . GetSourceAssertion ( ) . Search ( p . GetPackage ( ) . GetFingerPrint ( ) )
2020-11-13 17:25:44 +00:00
bus . Manager . Publish ( bus . EventPackagePreBuild , struct {
2021-04-12 17:00:36 +00:00
CompileSpec * compilerspec . LuetCompilationSpec
2020-11-13 17:25:44 +00:00
Assert solver . PackageAssert
} {
CompileSpec : p ,
Assert : * targetAssertion ,
} )
2021-04-12 17:00:36 +00:00
// Update compilespec build options
p . SetBuildOptions ( cs . Options )
2019-11-11 18:19:13 +00:00
// - If image is set we just generate a plain dockerfile
// Treat last case (easier) first. The image is provided and we just compute a plain dockerfile with the images listed as above
if p . GetImage ( ) != "" {
2021-04-12 17:00:36 +00:00
return cs . compileWithImage ( p . GetImage ( ) , "" , targetAssertion . Hash . PackageHash , concurrency , keepPermissions , cs . Options . KeepImg , p , true )
2019-11-11 18:19:13 +00:00
}
2019-11-12 16:31:50 +00:00
// - If image is not set, we read a base_image. Then we will build one image from it to kick-off our build based
// on how we compute the resolvable tree.
// This means to recursively build all the build-images needed to reach that tree part.
// - We later on compute an hash used to identify the image, so each similar deptree keeps the same build image.
2019-11-15 23:38:07 +00:00
dependencies := p . GetSourceAssertion ( ) . Drop ( p . GetPackage ( ) ) // at this point we should have a flattened list of deps to build, including all of them (with all constraints propagated already)
2021-04-12 17:00:36 +00:00
departifacts := [ ] * artifact . PackageArtifact { } // TODO: Return this somehow
2019-11-11 18:19:13 +00:00
var lastHash string
2019-11-14 16:45:21 +00:00
depsN := 0
currentN := 0
2020-10-06 15:57:57 +00:00
packageDeps := ! cs . Options . PackageTargetOnly
2020-02-18 17:22:18 +00:00
if ! cs . Options . NoDeps {
Info ( ":deciduous_tree: Build dependencies for " + p . GetPackage ( ) . HumanReadableString ( ) )
for _ , assertion := range dependencies { //highly dependent on the order
depsN ++
Info ( " :arrow_right_hook:" , assertion . Package . HumanReadableString ( ) , ":leaves:" )
2019-11-15 23:38:07 +00:00
}
2020-02-18 17:22:18 +00:00
for _ , assertion := range dependencies { //highly dependent on the order
currentN ++
2020-10-30 18:15:04 +00:00
pkgTag := fmt . Sprintf ( ":package: %d/%d %s ⤑ :hammer: build %s" , currentN , depsN , p . GetPackage ( ) . HumanReadableString ( ) , assertion . Package . HumanReadableString ( ) )
Info ( pkgTag , " starts" )
2020-02-18 17:22:18 +00:00
compileSpec , err := cs . FromPackage ( assertion . Package )
if err != nil {
return nil , errors . Wrap ( err , "Error while generating compilespec for " + assertion . Package . GetName ( ) )
}
compileSpec . SetOutputPath ( p . GetOutputPath ( ) )
2021-04-07 13:54:38 +00:00
Debug ( pkgTag , " :arrow_right_hook: :whale: Builder image from hash" , assertion . Hash . BuildHash )
Debug ( pkgTag , " :arrow_right_hook: :whale: Package image from hash" , assertion . Hash . PackageHash )
2020-02-18 17:22:18 +00:00
2020-11-13 17:25:44 +00:00
bus . Manager . Publish ( bus . EventPackagePreBuild , struct {
2021-04-12 17:00:36 +00:00
CompileSpec * compilerspec . LuetCompilationSpec
2020-11-13 17:25:44 +00:00
Assert solver . PackageAssert
} {
CompileSpec : compileSpec ,
Assert : assertion ,
} )
2021-04-07 13:54:38 +00:00
lastHash = assertion . Hash . PackageHash
// for the source instead, pick an image and a buildertaggedImage from hashes if they exists.
// otherways fallback to the pushed repo
// Resolve images from the hashtree
resolvedBuildImage := cs . resolveExistingImageHash ( assertion . Hash . BuildHash )
2020-02-18 17:22:18 +00:00
if compileSpec . GetImage ( ) != "" {
Debug ( pkgTag , " :wrench: Compiling " + compileSpec . GetPackage ( ) . HumanReadableString ( ) + " from image" )
2021-04-07 13:54:38 +00:00
2021-04-12 17:00:36 +00:00
a , err := cs . compileWithImage ( compileSpec . GetImage ( ) , resolvedBuildImage , assertion . Hash . PackageHash , concurrency , keepPermissions , cs . Options . KeepImg , compileSpec , packageDeps )
2019-11-11 18:19:13 +00:00
if err != nil {
2020-02-18 17:22:18 +00:00
return nil , errors . Wrap ( err , "Failed compiling " + compileSpec . GetPackage ( ) . HumanReadableString ( ) )
2019-11-11 18:19:13 +00:00
}
2021-04-12 17:00:36 +00:00
departifacts = append ( departifacts , a )
2020-02-18 17:22:18 +00:00
Info ( pkgTag , ":white_check_mark: Done" )
2019-11-11 18:19:13 +00:00
continue
}
2020-02-18 17:22:18 +00:00
Debug ( pkgTag , " :wrench: Compiling " + compileSpec . GetPackage ( ) . HumanReadableString ( ) + " from tree" )
2021-04-12 17:00:36 +00:00
a , err := cs . compileWithImage ( resolvedBuildImage , "" , assertion . Hash . PackageHash , concurrency , keepPermissions , cs . Options . KeepImg , compileSpec , packageDeps )
2019-11-11 18:19:13 +00:00
if err != nil {
2020-02-18 17:22:18 +00:00
return nil , errors . Wrap ( err , "Failed compiling " + compileSpec . GetPackage ( ) . HumanReadableString ( ) )
2019-11-11 18:19:13 +00:00
}
2020-11-13 17:25:44 +00:00
bus . Manager . Publish ( bus . EventPackagePostBuild , struct {
2021-04-12 17:00:36 +00:00
CompileSpec * compilerspec . LuetCompilationSpec
Artifact * artifact . PackageArtifact
2020-11-13 17:25:44 +00:00
} {
CompileSpec : compileSpec ,
2021-04-12 17:00:36 +00:00
Artifact : a ,
2020-11-13 17:25:44 +00:00
} )
2021-04-12 17:00:36 +00:00
departifacts = append ( departifacts , a )
2020-10-30 18:15:04 +00:00
Info ( pkgTag , ":white_check_mark: Done" )
2019-11-15 23:38:07 +00:00
}
2020-02-18 17:22:18 +00:00
} else if len ( dependencies ) > 0 {
2021-04-07 13:54:38 +00:00
lastHash = dependencies [ len ( dependencies ) - 1 ] . Hash . PackageHash
2020-02-18 17:22:18 +00:00
}
if ! cs . Options . OnlyDeps {
2021-04-07 13:54:38 +00:00
resolvedBuildImage := cs . resolveExistingImageHash ( lastHash )
2020-10-30 18:15:04 +00:00
Info ( ":rocket: All dependencies are satisfied, building package requested by the user" , p . GetPackage ( ) . HumanReadableString ( ) )
2021-04-07 13:54:38 +00:00
Info ( ":package:" , p . GetPackage ( ) . HumanReadableString ( ) , " Using image: " , resolvedBuildImage )
2021-04-12 17:00:36 +00:00
a , err := cs . compileWithImage ( resolvedBuildImage , "" , targetAssertion . Hash . PackageHash , concurrency , keepPermissions , cs . Options . KeepImg , p , true )
2019-11-15 23:38:07 +00:00
if err != nil {
2021-04-12 17:00:36 +00:00
return a , err
2019-11-11 18:19:13 +00:00
}
2021-04-12 17:00:36 +00:00
a . Dependencies = departifacts
a . SourceAssertion = p . GetSourceAssertion ( )
2019-11-26 19:11:27 +00:00
2020-11-13 17:25:44 +00:00
bus . Manager . Publish ( bus . EventPackagePostBuild , struct {
2021-04-12 17:00:36 +00:00
CompileSpec * compilerspec . LuetCompilationSpec
Artifact * artifact . PackageArtifact
2020-11-13 17:25:44 +00:00
} {
CompileSpec : p ,
2021-04-12 17:00:36 +00:00
Artifact : a ,
2020-11-13 17:25:44 +00:00
} )
2021-04-12 17:00:36 +00:00
return a , err
2020-02-18 17:22:18 +00:00
} else {
return departifacts [ len ( departifacts ) - 1 ] , nil
2019-11-15 17:11:26 +00:00
}
2019-11-04 16:16:13 +00:00
}
2020-10-04 17:16:01 +00:00
type templatedata map [ string ] interface { }
2021-04-14 14:49:43 +00:00
func ( cs * LuetCompiler ) templatePackage ( vals [ ] map [ string ] interface { } , pack pkg . Package ) ( [ ] byte , error ) {
2019-11-10 09:48:07 +00:00
2020-11-14 23:13:46 +00:00
var dataresult [ ] byte
val := pack . Rel ( DefinitionFile )
2021-04-12 17:00:36 +00:00
// Update processed build values
dst , err := helpers . UnMarshalValues ( cs . Options . BuildValuesFile )
if err != nil {
return nil , errors . Wrap ( err , "unmarshalling values" )
}
2021-04-14 14:49:43 +00:00
cs . Options . BuildValues = append ( vals , ( map [ string ] interface { } ) ( dst ) )
2021-04-12 17:00:36 +00:00
2020-11-14 23:13:46 +00:00
if _ , err := os . Stat ( pack . Rel ( CollectionFile ) ) ; err == nil {
val = pack . Rel ( CollectionFile )
data , err := ioutil . ReadFile ( val )
if err != nil {
return nil , errors . Wrap ( err , "rendering file " + val )
}
dataBuild , err := ioutil . ReadFile ( pack . Rel ( BuildFile ) )
if err != nil {
return nil , errors . Wrap ( err , "rendering file " + val )
}
2021-04-12 10:04:04 +00:00
2020-11-14 23:13:46 +00:00
packsRaw , err := pkg . GetRawPackages ( data )
2021-04-12 10:04:04 +00:00
if err != nil {
return nil , errors . Wrap ( err , "getting raw packages" )
}
2020-11-14 23:13:46 +00:00
2020-11-15 10:47:32 +00:00
raw := packsRaw . Find ( pack . GetName ( ) , pack . GetCategory ( ) , pack . GetVersion ( ) )
2021-04-14 14:49:43 +00:00
td := templatedata { }
if len ( vals ) > 0 {
for _ , bv := range vals {
current := templatedata ( bv )
if err := mergo . Merge ( & td , current ) ; err != nil {
return nil , errors . Wrap ( err , "merging values maps" )
}
}
}
if err := mergo . Merge ( & td , raw ) ; err != nil {
return nil , errors . Wrap ( err , "merging values maps" )
}
2020-11-14 23:13:46 +00:00
2021-04-14 14:49:43 +00:00
dat , err := helpers . RenderHelm ( string ( dataBuild ) , td , dst )
2020-11-14 23:13:46 +00:00
if err != nil {
return nil , errors . Wrap ( err , "rendering file " + pack . Rel ( BuildFile ) )
}
dataresult = [ ] byte ( dat )
} else {
2021-04-14 14:49:43 +00:00
bv := cs . Options . BuildValuesFile
if len ( vals ) > 0 {
valuesdir , err := ioutil . TempDir ( "" , "genvalues" )
if err != nil {
return nil , errors . Wrap ( err , "Could not create tempdir" )
}
defer os . RemoveAll ( valuesdir ) // clean up
for _ , b := range vals {
out , err := yaml . Marshal ( b )
if err != nil {
return nil , errors . Wrap ( err , "while marshalling values file" )
}
f := filepath . Join ( valuesdir , helpers . RandStringRunes ( 20 ) )
if err := ioutil . WriteFile ( f , out , os . ModePerm ) ; err != nil {
return nil , errors . Wrap ( err , "while writing temporary values file" )
}
bv = append ( [ ] string { f } , bv ... )
}
}
out , err := helpers . RenderFiles ( pack . Rel ( BuildFile ) , val , bv ... )
2020-11-14 23:13:46 +00:00
if err != nil {
return nil , errors . Wrap ( err , "rendering file " + pack . Rel ( BuildFile ) )
}
dataresult = [ ] byte ( out )
2020-10-04 17:16:01 +00:00
}
2021-04-12 10:04:04 +00:00
2021-04-12 17:00:36 +00:00
return dataresult , nil
2021-04-12 10:04:04 +00:00
}
// FromPackage returns a compilation spec from a package definition
2021-04-12 17:00:36 +00:00
func ( cs * LuetCompiler ) FromPackage ( p pkg . Package ) ( * compilerspec . LuetCompilationSpec , error ) {
2021-04-12 10:04:04 +00:00
pack , err := cs . Database . FindPackageCandidate ( p )
if err != nil {
return nil , err
}
2021-04-14 14:49:43 +00:00
opts := options . Compiler { }
artifactMetadataFile := filepath . Join ( p . GetTreeDir ( ) , p . GetMetadataFilePath ( ) )
if fi , err := os . Stat ( artifactMetadataFile ) ; err == nil {
f , err := os . Open ( fi . Name ( ) )
if err != nil {
return nil , errors . Wrapf ( err , "could not open %s" , fi . Name ( ) )
}
dat , err := ioutil . ReadAll ( f )
if err != nil {
return nil , err
}
art , err := artifact . NewPackageArtifactFromYaml ( dat )
if err != nil {
return nil , errors . Wrap ( err , "could not decode package from yaml" )
}
opts = art . CompileSpec . BuildOptions
opts . PushImageRepository = ""
} else if ! os . IsNotExist ( err ) {
Debug ( "error reading already existing artifact metadata file: " , err . Error ( ) )
}
bytes , err := cs . templatePackage ( opts . BuildValues , pack )
2021-04-12 10:04:04 +00:00
if err != nil {
return nil , errors . Wrap ( err , "while rendering package template" )
}
2019-11-04 16:16:13 +00:00
2021-04-14 14:49:43 +00:00
newSpec , err := compilerspec . NewLuetCompilationSpec ( bytes , pack )
if err != nil {
return nil , err
}
newSpec . BuildOptions = opts
return newSpec , err
2019-11-04 16:16:13 +00:00
}
2019-11-04 16:20:00 +00:00
2021-04-07 13:54:38 +00:00
// GetBackend returns the current compilation backend
2019-11-04 16:20:00 +00:00
func ( cs * LuetCompiler ) GetBackend ( ) CompilerBackend {
return cs . Backend
}
2021-04-07 13:54:38 +00:00
// SetBackend sets the compilation backend
2019-11-04 16:20:00 +00:00
func ( cs * LuetCompiler ) SetBackend ( b CompilerBackend ) {
cs . Backend = b
}