mirror of
https://github.com/mudler/luet.git
synced 2025-09-02 15:54:39 +00:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
47f0049efa | ||
|
0cc2b72831 | ||
|
f2df3faee5 | ||
|
287098f101 | ||
|
f9a7113ab9 | ||
|
c3559d952c | ||
|
fc863fc8e5 | ||
|
ac149e9336 | ||
|
b9c8e50e42 | ||
|
cf7df00a65 | ||
|
83f924da35 | ||
|
c82d23f9f2 | ||
|
0e46e763d5 | ||
|
a793b44e83 | ||
|
19e6054574 | ||
|
a8624fe451 | ||
|
14c1d6ef24 | ||
|
36c58307e2 | ||
|
665261e526 | ||
|
794c5984a2 | ||
|
a765147c1d | ||
|
088adf6f3a | ||
|
cead09fb9f | ||
|
9a1787ddaf | ||
|
b1316b50b4 | ||
|
d92ee9e1d9 | ||
|
e7b58eec41 | ||
|
6a1b64acea | ||
|
df14fe60fc | ||
|
459eb01a59 | ||
|
e6c597c7d3 | ||
|
e70cdbaaf7 | ||
|
eea9dad2c6 | ||
|
513f441bb3 | ||
|
ebe7466fdc |
101
cmd/convert.go
101
cmd/convert.go
@@ -1,101 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
. "github.com/mudler/luet/pkg/config"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
tree "github.com/mudler/luet/pkg/tree"
|
||||
|
||||
"github.com/mudler/luet/pkg/tree/builder/gentoo"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var convertCmd = &cobra.Command{
|
||||
Use: "convert [portage-tree] [luet-tree]",
|
||||
Short: "convert other package manager tree into luet",
|
||||
Long: `Parses external PM and produces a luet parsable tree`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("type", cmd.Flags().Lookup("type"))
|
||||
viper.BindPFlag("database", cmd.Flags().Lookup("database"))
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
t := viper.GetString("type")
|
||||
databaseType := viper.GetString("database")
|
||||
var db pkg.PackageDatabase
|
||||
|
||||
if len(args) != 2 {
|
||||
Fatal("Incorrect number of arguments")
|
||||
}
|
||||
|
||||
input := args[0]
|
||||
output := args[1]
|
||||
Info("Converting trees from " + input + " [" + t + "]")
|
||||
|
||||
var builder tree.Parser
|
||||
switch t {
|
||||
case "gentoo":
|
||||
builder = gentoo.NewGentooBuilder(
|
||||
&gentoo.SimpleEbuildParser{},
|
||||
LuetCfg.GetGeneral().Concurrency,
|
||||
gentoo.InMemory)
|
||||
default: // dup
|
||||
builder = gentoo.NewGentooBuilder(
|
||||
&gentoo.SimpleEbuildParser{},
|
||||
LuetCfg.GetGeneral().Concurrency,
|
||||
gentoo.InMemory)
|
||||
}
|
||||
|
||||
switch databaseType {
|
||||
case "memory":
|
||||
db = pkg.NewInMemoryDatabase(false)
|
||||
case "boltdb":
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
if err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
db = pkg.NewBoltDatabase(tmpdir)
|
||||
}
|
||||
defer db.Clean()
|
||||
|
||||
packageTree, err := builder.Generate(input)
|
||||
if err != nil {
|
||||
Fatal("Error: " + err.Error())
|
||||
}
|
||||
|
||||
defer packageTree.Clean()
|
||||
Info("Tree generated")
|
||||
|
||||
generalRecipe := tree.NewGeneralRecipe(packageTree)
|
||||
Info("Saving generated tree to " + output)
|
||||
|
||||
err = generalRecipe.Save(output)
|
||||
if err != nil {
|
||||
Fatal("Error: " + err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
convertCmd.Flags().String("type", "gentoo", "source type")
|
||||
convertCmd.Flags().String("database", "memory", "database used for solving (memory,boltdb)")
|
||||
|
||||
RootCmd.AddCommand(convertCmd)
|
||||
}
|
89
cmd/pack.go
Normal file
89
cmd/pack.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
helpers "github.com/mudler/luet/cmd/helpers"
|
||||
"github.com/mudler/luet/pkg/compiler"
|
||||
. "github.com/mudler/luet/pkg/config"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var packCmd = &cobra.Command{
|
||||
Use: "pack <package name>",
|
||||
Short: "pack a custom package",
|
||||
Long: `pack and creates metadata directly from a source path`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("destination", cmd.Flags().Lookup("destination"))
|
||||
viper.BindPFlag("compression", cmd.Flags().Lookup("compression"))
|
||||
viper.BindPFlag("source", cmd.Flags().Lookup("source"))
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sourcePath := viper.GetString("source")
|
||||
|
||||
dst := viper.GetString("destination")
|
||||
compressionType := viper.GetString("compression")
|
||||
concurrency := LuetCfg.GetGeneral().Concurrency
|
||||
|
||||
if len(args) != 1 {
|
||||
Fatal("You must specify a package name")
|
||||
}
|
||||
|
||||
packageName := args[0]
|
||||
|
||||
p, err := helpers.ParsePackageStr(packageName)
|
||||
if err != nil {
|
||||
Fatal("Invalid package string ", packageName, ": ", err.Error())
|
||||
}
|
||||
|
||||
spec := &compiler.LuetCompilationSpec{Package: p}
|
||||
artifact := compiler.NewPackageArtifact(filepath.Join(dst, p.GetFingerPrint()+".package.tar"))
|
||||
artifact.SetCompressionType(compiler.CompressionImplementation(compressionType))
|
||||
err = artifact.Compress(sourcePath, concurrency)
|
||||
if err != nil {
|
||||
Fatal("failed compressing ", packageName, ": ", err.Error())
|
||||
}
|
||||
artifact.SetCompileSpec(spec)
|
||||
filelist, err := artifact.FileList()
|
||||
if err != nil {
|
||||
Fatal("failed generating file list for ", packageName, ": ", err.Error())
|
||||
}
|
||||
artifact.SetFiles(filelist)
|
||||
artifact.GetCompileSpec().GetPackage().SetBuildTimestamp(time.Now().String())
|
||||
err = artifact.WriteYaml(dst)
|
||||
if err != nil {
|
||||
Fatal("failed writing metadata yaml file for ", packageName, ": ", err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
packCmd.Flags().String("source", path, "Source folder")
|
||||
packCmd.Flags().String("destination", path, "Destination folder")
|
||||
packCmd.Flags().String("compression", "gzip", "Compression alg: none, gzip")
|
||||
|
||||
RootCmd.AddCommand(packCmd)
|
||||
}
|
19
cmd/root.go
19
cmd/root.go
@@ -24,6 +24,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/marcsauter/single"
|
||||
bus "github.com/mudler/luet/pkg/bus"
|
||||
|
||||
extensions "github.com/mudler/cobra-extensions"
|
||||
config "github.com/mudler/luet/pkg/config"
|
||||
helpers "github.com/mudler/luet/pkg/helpers"
|
||||
@@ -38,7 +40,7 @@ var Verbose bool
|
||||
var LockedCommands = []string{"install", "uninstall", "upgrade"}
|
||||
|
||||
const (
|
||||
LuetCLIVersion = "0.8.14"
|
||||
LuetCLIVersion = "0.9.4"
|
||||
LuetEnvPrefix = "LUET"
|
||||
)
|
||||
|
||||
@@ -68,6 +70,18 @@ var RootCmd = &cobra.Command{
|
||||
if err != nil {
|
||||
Fatal("failed on init tmp basedir:", err.Error())
|
||||
}
|
||||
|
||||
viper.BindPFlag("plugin", cmd.Flags().Lookup("plugin"))
|
||||
|
||||
plugin := viper.GetStringSlice("plugin")
|
||||
|
||||
bus.Manager.Load(plugin...).Register()
|
||||
if len(bus.Manager.Plugins) != 0 {
|
||||
Info(":lollipop:Enabled plugins:")
|
||||
for _, p := range bus.Manager.Plugins {
|
||||
Info("\t:arrow_right:", p.Name)
|
||||
}
|
||||
}
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
// Cleanup all tmp directories used by luet
|
||||
@@ -156,6 +170,7 @@ func init() {
|
||||
"Disable config protect analysis.")
|
||||
pflags.StringP("logfile", "l", config.LuetCfg.GetLogging().Path,
|
||||
"Logfile path. Empty value disable log to file.")
|
||||
pflags.StringSlice("plugin", []string{}, "A list of runtime plugins to load")
|
||||
|
||||
// os/user doesn't work in from scratch environments.
|
||||
// Check if i can retrieve user informations.
|
||||
@@ -175,6 +190,8 @@ func init() {
|
||||
config.LuetCfg.Viper.BindPFlag("general.debug", pflags.Lookup("debug"))
|
||||
config.LuetCfg.Viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal"))
|
||||
config.LuetCfg.Viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner"))
|
||||
config.LuetCfg.Viper.BindPFlag("plugin", pflags.Lookup("plugin"))
|
||||
|
||||
// Currently I maintain this only from cli.
|
||||
config.LuetCfg.Viper.BindPFlag("no_spinner", pflags.Lookup("no-spinner"))
|
||||
config.LuetCfg.Viper.BindPFlag("config_protect_skip", pflags.Lookup("skip-config-protect"))
|
||||
|
@@ -34,5 +34,6 @@ func init() {
|
||||
NewTreePkglistCommand(),
|
||||
NewTreeValidateCommand(),
|
||||
NewTreeBumpCommand(),
|
||||
NewTreeImageCommand(),
|
||||
)
|
||||
}
|
||||
|
136
cmd/tree/images.go
Normal file
136
cmd/tree/images.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
// Daniele Rondina <geaaru@sabayonlinux.org>
|
||||
//
|
||||
// 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 cmd_tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
//. "github.com/mudler/luet/pkg/config"
|
||||
"github.com/ghodss/yaml"
|
||||
helpers "github.com/mudler/luet/cmd/helpers"
|
||||
"github.com/mudler/luet/pkg/compiler"
|
||||
"github.com/mudler/luet/pkg/compiler/backend"
|
||||
. "github.com/mudler/luet/pkg/config"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"github.com/mudler/luet/pkg/solver"
|
||||
tree "github.com/mudler/luet/pkg/tree"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func NewTreeImageCommand() *cobra.Command {
|
||||
|
||||
var ans = &cobra.Command{
|
||||
Use: "images [OPTIONS]",
|
||||
Short: "List of the images of a package",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
t, _ := cmd.Flags().GetStringArray("tree")
|
||||
if len(t) == 0 {
|
||||
Fatal("Mandatory tree param missing.")
|
||||
}
|
||||
|
||||
if len(args) != 1 {
|
||||
Fatal("Expects one package as parameter")
|
||||
}
|
||||
viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository"))
|
||||
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var results TreeResults
|
||||
|
||||
treePath, _ := cmd.Flags().GetStringArray("tree")
|
||||
imageRepository := viper.GetString("image-repository")
|
||||
|
||||
out, _ := cmd.Flags().GetString("output")
|
||||
if out != "terminal" {
|
||||
LuetCfg.GetLogging().SetLogLevel("error")
|
||||
}
|
||||
|
||||
reciper := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
|
||||
for _, t := range treePath {
|
||||
err := reciper.Load(t)
|
||||
if err != nil {
|
||||
Fatal("Error on load tree ", err)
|
||||
}
|
||||
}
|
||||
compilerBackend := backend.NewSimpleDockerBackend()
|
||||
|
||||
opts := compiler.NewDefaultCompilerOptions()
|
||||
opts.SolverOptions = *LuetCfg.GetSolverOptions()
|
||||
opts.ImageRepository = imageRepository
|
||||
|
||||
solverOpts := solver.Options{Type: solver.SingleCoreSimple, Concurrency: 1}
|
||||
luetCompiler := compiler.NewLuetCompiler(compilerBackend, reciper.GetDatabase(), opts, solverOpts)
|
||||
|
||||
a := args[0]
|
||||
|
||||
pack, err := helpers.ParsePackageStr(a)
|
||||
if err != nil {
|
||||
Fatal("Invalid package string ", a, ": ", err.Error())
|
||||
}
|
||||
|
||||
spec, err := luetCompiler.FromPackage(pack)
|
||||
if err != nil {
|
||||
Fatal("Error: " + err.Error())
|
||||
}
|
||||
asserts, err := luetCompiler.ComputeDepTree(spec)
|
||||
|
||||
for _, assertion := range asserts { //highly dependent on the order
|
||||
|
||||
//buildImageHash := imageRepository + ":" + assertion.Hash.BuildHash
|
||||
currentPackageImageHash := imageRepository + ":" + assertion.Hash.PackageHash
|
||||
|
||||
results.Packages = append(results.Packages, TreePackageResult{
|
||||
Name: assertion.Package.GetName(),
|
||||
Version: assertion.Package.GetVersion(),
|
||||
Category: assertion.Package.GetCategory(),
|
||||
Image: currentPackageImageHash,
|
||||
})
|
||||
}
|
||||
|
||||
y, err := yaml.Marshal(results)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
switch out {
|
||||
case "yaml":
|
||||
fmt.Println(string(y))
|
||||
case "json":
|
||||
j2, err := yaml.YAMLToJSON(y)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(j2))
|
||||
default:
|
||||
for _, p := range results.Packages {
|
||||
fmt.Println(fmt.Sprintf("%s/%s-%s: %s", p.Category, p.Name, p.Version, p.Image))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
ans.Flags().StringP("output", "o", "terminal", "Output format ( Defaults: terminal, available: json,yaml )")
|
||||
ans.Flags().StringArrayP("tree", "t", []string{}, "Path of the tree to use.")
|
||||
ans.Flags().String("image-repository", "luet/cache", "Default base image string for generated image")
|
||||
|
||||
return ans
|
||||
}
|
@@ -37,6 +37,7 @@ type TreePackageResult struct {
|
||||
Category string `json:"category"`
|
||||
Version string `json:"version"`
|
||||
Path string `json:"path"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
type TreeResults struct {
|
||||
|
@@ -4,14 +4,17 @@ export LUET_NOLOCK=true
|
||||
|
||||
LUET_VERSION=0.8.6
|
||||
LUET_ROOTFS=${LUET_ROOTFS:-/}
|
||||
LUET_DATABASE_PATH=${LUET_DATABASE_PATH:-/}
|
||||
LUET_DATABASE_PATH=${LUET_DATABASE_PATH:-/var/luet/db}
|
||||
LUET_DATABASE_ENGINE=${LUET_DATABASE_ENGINE:-boltdb}
|
||||
LUET_CONFIG_PROTECT=${LUET_CONFIG_PROTECT:-0}
|
||||
LUET_CONFIG_PROTECT=${LUET_CONFIG_PROTECT:-1}
|
||||
|
||||
wget -q https://github.com/mudler/luet/releases/download/0.8.6/luet-0.8.6-linux-amd64 -O luet
|
||||
chmod +x luet
|
||||
|
||||
mkdir -p /etc/luet/repos.conf.d || true
|
||||
mkdir -p $LUET_DATABASE_PATH || true
|
||||
mkdir -p /var/tmp/luet || true
|
||||
|
||||
if [ "${LUET_CONFIG_PROTECT}" = "1" ] ; then
|
||||
mkdir -p /etc/luet/config.protect.d || true
|
||||
wget -q https://raw.githubusercontent.com/mudler/luet/master/contrib/config/config.protect.d/01_etc.yml.example -O /etc/luet/config.protect.d/01_etc.yml
|
||||
@@ -25,6 +28,7 @@ system:
|
||||
rootfs: ${LUET_ROOTFS}
|
||||
database_path: "${LUET_DATABASE_PATH}"
|
||||
database_engine: "${LUET_DATABASE_ENGINE}"
|
||||
tmpdir_base: "/var/tmp/luet"
|
||||
EOF
|
||||
|
||||
./luet install repository/luet repository/mocaccino-repository-index
|
||||
|
@@ -69,6 +69,7 @@
|
||||
# Default $TMPDIR/tmpluet
|
||||
# tmpdir_base: "/tmp/tmpluet"
|
||||
#
|
||||
#
|
||||
# ---------------------------------------------
|
||||
# Repositories configurations directories.
|
||||
# ---------------------------------------------
|
||||
@@ -93,6 +94,11 @@
|
||||
# annotation.
|
||||
# config_protect_skip: false
|
||||
#
|
||||
# The paths used for load repositories and config
|
||||
# protects are based on host rootfs.
|
||||
# If set to false rootfs path is used as prefix.
|
||||
# config_from_host: true
|
||||
#
|
||||
# System repositories
|
||||
# ---------------------------------------------
|
||||
# In alternative to define repositories files
|
||||
|
11
go.mod
11
go.mod
@@ -7,7 +7,7 @@ require (
|
||||
github.com/Sabayon/pkgs-checker v0.7.2
|
||||
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
|
||||
github.com/briandowns/spinner v1.7.0
|
||||
github.com/cavaliercoder/grab v2.0.0+incompatible
|
||||
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
||||
github.com/crillab/gophersat v1.3.2-0.20201023142334-3fc2ac466765
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200417035958-130b0bc6032c+incompatible
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
@@ -25,9 +25,10 @@ require (
|
||||
github.com/moby/sys/mount v0.1.1-0.20200320164225-6154f11e6840 // indirect
|
||||
github.com/mudler/cobra-extensions v0.0.0-20200612154940-31a47105fe3d
|
||||
github.com/mudler/docker-companion v0.4.6-0.20200418093252-41846f112d87
|
||||
github.com/mudler/go-pluggable v0.0.0-20201113184918-d36448fc8f82
|
||||
github.com/mudler/topsort v0.0.0-20201103161459-db5c7901c290
|
||||
github.com/onsi/ginkgo v1.12.1
|
||||
github.com/onsi/gomega v1.10.0
|
||||
github.com/onsi/ginkgo v1.14.2
|
||||
github.com/onsi/gomega v1.10.3
|
||||
github.com/otiai10/copy v1.2.1-0.20200916181228-26f84a0b1578
|
||||
github.com/pelletier/go-toml v1.6.0 // indirect
|
||||
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
|
||||
@@ -39,11 +40,9 @@ require (
|
||||
go.uber.org/multierr v1.4.0 // indirect
|
||||
go.uber.org/zap v1.13.0
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gotest.tools/v3 v3.0.2 // indirect
|
||||
helm.sh/helm/v3 v3.3.4
|
||||
mvdan.cc/sh/v3 v3.0.0-beta1
|
||||
)
|
||||
|
||||
replace github.com/docker/docker => github.com/Luet-lab/moby v17.12.0-ce-rc1.0.20200605210607-749178b8f80d+incompatible
|
||||
|
51
go.sum
51
go.sum
@@ -97,17 +97,21 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cavaliercoder/grab v2.0.0+incompatible h1:wZHbBQx56+Yxjx2TCGDcenhh3cJn7cCLMfkEPmySTSE=
|
||||
github.com/cavaliercoder/grab v2.0.0+incompatible/go.mod h1:tTBkfNqSBfuMmMBFaO2phgyhdYhiZQ/+iXCZDzcDsMI=
|
||||
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec h1:4XvMn0XuV7qxCH22gbnR79r+xTUaLOSA0GW/egpO3SQ=
|
||||
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec/go.mod h1:NbXoa59CCAGqtRm7kRrcZIk2dTCJMRVF8QI3BOD7isY=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
@@ -201,6 +205,8 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/go-dockerclient v1.6.4 h1:B+L+1lz1LUrNgEUUh8PSG76s70EYC49ssv2xvTefTMM=
|
||||
github.com/fsouza/go-dockerclient v1.6.4/go.mod h1:GOdftxWLWIbIWKbIMDroKFJzPdg6Iw7r+jX1DDZdVsA=
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
@@ -294,6 +300,13 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
|
||||
@@ -504,6 +517,8 @@ github.com/mudler/cobra-extensions v0.0.0-20200612154940-31a47105fe3d h1:fKh+rvw
|
||||
github.com/mudler/cobra-extensions v0.0.0-20200612154940-31a47105fe3d/go.mod h1:puRUWSwyecW2V355tKncwPVPRAjQBduPsFjG0mrV/Nw=
|
||||
github.com/mudler/docker-companion v0.4.6-0.20200418093252-41846f112d87 h1:mGz7T8KvmHH0gLWPI5tQne8xl2cO3T8wrrb6Aa16Jxo=
|
||||
github.com/mudler/docker-companion v0.4.6-0.20200418093252-41846f112d87/go.mod h1:1w4zI1LYXDeiUXqedPcrT5eQJnmKR6dbg5iJMgSIP/Y=
|
||||
github.com/mudler/go-pluggable v0.0.0-20201113184918-d36448fc8f82 h1:Hkefw2tzoKATVUTFsCtDlUnY180+OE851qGbq45ATxk=
|
||||
github.com/mudler/go-pluggable v0.0.0-20201113184918-d36448fc8f82/go.mod h1:4P/ULate+2QxoAQtojaRjyO5VGMhV0KLnSdAS8nuBbo=
|
||||
github.com/mudler/topsort v0.0.0-20201103161459-db5c7901c290 h1:426hFyXMpXeqIeGJn2cGAW9ogvM2Jf+Jv23gtVPvBLM=
|
||||
github.com/mudler/topsort v0.0.0-20201103161459-db5c7901c290/go.mod h1:uP5BBgFxq2wNWo7n1vnY5SSbgL0WDshVJrOO12tZ/lA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
@@ -534,6 +549,8 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
@@ -542,6 +559,9 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.0 h1:Gwkk+PTu/nfOwNMtUB/mRUv0X7ewW5dO4AERT1ThVKo=
|
||||
github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/openSUSE/umoci v0.1.1-0.20191030112807-c0dd46ae078f h1:G9hyzNrFbTgp9KEoGRcNYxAT41lo7hDy9oxXT1Y7WHI=
|
||||
github.com/openSUSE/umoci v0.1.1-0.20191030112807-c0dd46ae078f/go.mod h1:3p4KA5nwyY65lVmQZxv7tm0YEylJ+t1fY91ORsVXv58=
|
||||
@@ -592,7 +612,6 @@ github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f h1:WyCn68lTiy
|
||||
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f/go.mod h1:/iRjX3DdSK956SzsUdV55J+wIsQ+2IBWmBrB4RvZfk4=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/diff v0.0.0-20190930165518-531926345625/go.mod h1:kFj35MyHn14a6pIgWhm46KVjJr5CHys3eEYxkuKD1EI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
@@ -641,7 +660,6 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rootless-containers/proto v0.1.0 h1:gS1JOMEtk1YDYHCzBAf/url+olMJbac7MTrgSeP6zh4=
|
||||
github.com/rootless-containers/proto v0.1.0/go.mod h1:vgkUFZbQd0gcE/K/ZwtE4MYjZPu0UNHLXIQxhyqAFh8=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg=
|
||||
@@ -794,7 +812,6 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
@@ -838,8 +855,9 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2 h1:4dVFTC832rPn4pomLSz1vA+are2+dU19w1H8OngV7nc=
|
||||
golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
@@ -878,6 +896,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -886,11 +905,17 @@ golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -959,6 +984,13 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -991,6 +1023,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
@@ -1029,9 +1063,6 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/metrics v0.18.8/go.mod h1:j7JzZdiyhLP2BsJm/Fzjs+j5Lb1Y7TySjhPWqBPwRXA=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
mvdan.cc/editorconfig v0.1.1-0.20191109213504-890940e3f00e/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
|
||||
mvdan.cc/sh/v3 v3.0.0-beta1 h1:UqiwBEXEPzelaGxuvixaOtzc7WzKtrElePJ8HqvW7K8=
|
||||
mvdan.cc/sh/v3 v3.0.0-beta1/go.mod h1:rBIndNJFYPp8xSppiZcGIk6B5d1g3OEARxEaXjPxwVI=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
|
46
pkg/bus/events.go
Normal file
46
pkg/bus/events.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
var (
|
||||
// Package events
|
||||
|
||||
// EventPackageInstall is the event fired when a new package is being installed
|
||||
EventPackageInstall pluggable.EventType = "package.install"
|
||||
// EventPackageUnInstall is the event fired when a new package is being uninstalled
|
||||
EventPackageUnInstall pluggable.EventType = "package.uninstall"
|
||||
|
||||
// Package build
|
||||
|
||||
// EventPackagePreBuild is the event fired before a package is being built
|
||||
EventPackagePreBuild pluggable.EventType = "package.pre.build"
|
||||
// EventPackagePreBuildArtifact is the event fired before a package artifact is being built
|
||||
EventPackagePreBuildArtifact pluggable.EventType = "package.pre.build_artifact"
|
||||
// EventPackagePostBuildArtifact is the event fired after a package artifact was built
|
||||
EventPackagePostBuildArtifact pluggable.EventType = "package.post.build_artifact"
|
||||
// EventPackagePostBuild is the event fired after a package was built
|
||||
EventPackagePostBuild pluggable.EventType = "package.post.build"
|
||||
|
||||
// Repository events
|
||||
|
||||
// EventRepositoryPreBuild is the event fired before a repository is being built
|
||||
EventRepositoryPreBuild pluggable.EventType = "repository.pre.build"
|
||||
// EventRepositoryPostBuild is the event fired after a repository was built
|
||||
EventRepositoryPostBuild pluggable.EventType = "repository.post.build"
|
||||
)
|
||||
|
||||
// Manager is the bus instance manager, which subscribes plugins to events emitted by Luet
|
||||
var Manager *pluggable.Manager = pluggable.NewManager(
|
||||
[]pluggable.EventType{
|
||||
EventPackageInstall,
|
||||
EventPackageUnInstall,
|
||||
EventPackagePreBuild,
|
||||
EventPackagePreBuildArtifact,
|
||||
EventPackagePostBuildArtifact,
|
||||
EventPackagePostBuild,
|
||||
EventRepositoryPreBuild,
|
||||
EventRepositoryPostBuild,
|
||||
},
|
||||
)
|
@@ -34,6 +34,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
bus "github.com/mudler/luet/pkg/bus"
|
||||
. "github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
@@ -170,6 +171,8 @@ func (a *PackageArtifact) WriteYaml(dst string) error {
|
||||
return errors.Wrap(err, "While marshalling for PackageArtifact YAML")
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuildArtifact, a)
|
||||
|
||||
mangle, err := NewPackageArtifactFromYaml(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Generated invalid artifact")
|
||||
@@ -191,6 +194,7 @@ func (a *PackageArtifact) WriteYaml(dst string) error {
|
||||
return errors.Wrap(err, "While writing PackageArtifact YAML")
|
||||
}
|
||||
//a.CompileSpec.GetPackage().SetPath(p)
|
||||
bus.Manager.Publish(bus.EventPackagePostBuildArtifact, a)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -516,7 +520,7 @@ func worker(i int, wg *sync.WaitGroup, s <-chan CopyJob) {
|
||||
}
|
||||
|
||||
// ExtractArtifactFromDelta extracts deltas from ArtifactLayer from an image in tar format
|
||||
func ExtractArtifactFromDelta(src, dst string, layers []ArtifactLayer, concurrency int, keepPerms bool, includes []string, t CompressionImplementation) (Artifact, error) {
|
||||
func ExtractArtifactFromDelta(src, dst string, layers []ArtifactLayer, concurrency int, keepPerms bool, includes []string, excludes []string, t CompressionImplementation) (Artifact, error) {
|
||||
|
||||
archive, err := LuetCfg.GetSystem().TempDir("archive")
|
||||
if err != nil {
|
||||
@@ -546,7 +550,8 @@ func ExtractArtifactFromDelta(src, dst string, layers []ArtifactLayer, concurren
|
||||
}
|
||||
|
||||
// Handle includes in spec. If specified they filter what gets in the package
|
||||
if len(includes) > 0 {
|
||||
|
||||
if len(includes) > 0 && len(excludes) == 0 {
|
||||
var includeRegexp []*regexp.Regexp
|
||||
for _, i := range includes {
|
||||
r, e := regexp.Compile(i)
|
||||
@@ -574,6 +579,81 @@ func ExtractArtifactFromDelta(src, dst string, layers []ArtifactLayer, concurren
|
||||
Debug("File ", a.Name, " deleted")
|
||||
}
|
||||
}
|
||||
|
||||
} else if len(includes) == 0 && len(excludes) != 0 {
|
||||
var excludeRegexp []*regexp.Regexp
|
||||
for _, i := range excludes {
|
||||
r, e := regexp.Compile(i)
|
||||
if e != nil {
|
||||
Warning("Failed compiling regex:", e)
|
||||
continue
|
||||
}
|
||||
excludeRegexp = append(excludeRegexp, r)
|
||||
}
|
||||
for _, l := range layers {
|
||||
// Consider d.Additions (and d.Changes? - warn at least) only
|
||||
ADD:
|
||||
for _, a := range l.Diffs.Additions {
|
||||
for _, i := range excludeRegexp {
|
||||
if i.MatchString(a.Name) {
|
||||
continue ADD
|
||||
}
|
||||
}
|
||||
toCopy <- CopyJob{Src: filepath.Join(src, a.Name), Dst: filepath.Join(archive, a.Name), Artifact: a.Name}
|
||||
}
|
||||
for _, a := range l.Diffs.Changes {
|
||||
Debug("File ", a.Name, " changed")
|
||||
}
|
||||
for _, a := range l.Diffs.Deletions {
|
||||
Debug("File ", a.Name, " deleted")
|
||||
}
|
||||
}
|
||||
|
||||
} else if len(includes) != 0 && len(excludes) != 0 {
|
||||
|
||||
var includeRegexp []*regexp.Regexp
|
||||
for _, i := range includes {
|
||||
r, e := regexp.Compile(i)
|
||||
if e != nil {
|
||||
Warning("Failed compiling regex:", e)
|
||||
continue
|
||||
}
|
||||
includeRegexp = append(includeRegexp, r)
|
||||
}
|
||||
var excludeRegexp []*regexp.Regexp
|
||||
for _, i := range excludes {
|
||||
r, e := regexp.Compile(i)
|
||||
if e != nil {
|
||||
Warning("Failed compiling regex:", e)
|
||||
continue
|
||||
}
|
||||
excludeRegexp = append(excludeRegexp, r)
|
||||
}
|
||||
|
||||
for _, l := range layers {
|
||||
// Consider d.Additions (and d.Changes? - warn at least) only
|
||||
EXCLUDES:
|
||||
for _, a := range l.Diffs.Additions {
|
||||
for _, i := range includeRegexp {
|
||||
if i.MatchString(a.Name) {
|
||||
for _, e := range excludeRegexp {
|
||||
if e.MatchString(a.Name) {
|
||||
continue EXCLUDES
|
||||
}
|
||||
}
|
||||
toCopy <- CopyJob{Src: filepath.Join(src, a.Name), Dst: filepath.Join(archive, a.Name), Artifact: a.Name}
|
||||
continue EXCLUDES
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, a := range l.Diffs.Changes {
|
||||
Debug("File ", a.Name, " changed")
|
||||
}
|
||||
for _, a := range l.Diffs.Deletions {
|
||||
Debug("File ", a.Name, " deleted")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Otherwise just grab all
|
||||
for _, l := range layers {
|
||||
|
@@ -130,7 +130,7 @@ RUN echo bar > /test2`))
|
||||
err = b.ExtractRootfs(CompilerBackendOptions{SourcePath: filepath.Join(tmpdir, "output2.tar"), Destination: rootfs}, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
artifact, err := ExtractArtifactFromDelta(rootfs, filepath.Join(tmpdir, "package.tar"), diffs, 2, false, []string{}, None)
|
||||
artifact, err := ExtractArtifactFromDelta(rootfs, filepath.Join(tmpdir, "package.tar"), diffs, 2, false, []string{}, []string{}, None)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(helpers.Exists(filepath.Join(tmpdir, "package.tar"))).To(BeTrue())
|
||||
err = helpers.Untar(artifact.GetPath(), unpacked, false)
|
||||
|
@@ -95,7 +95,7 @@ func (*SimpleDocker) ImageExists(imagename string) bool {
|
||||
cmd := exec.Command("docker", buildarg...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
Warning("Image not present")
|
||||
Debug("Image not present")
|
||||
Debug(string(out))
|
||||
return false
|
||||
}
|
||||
|
@@ -21,13 +21,13 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
bus "github.com/mudler/luet/pkg/bus"
|
||||
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
|
||||
const BuildFile = "build.yaml"
|
||||
const DefinitionFile = "definition.yaml"
|
||||
const CollectionFile = "collection.yaml"
|
||||
|
||||
type LuetCompiler struct {
|
||||
*tree.CompilerRecipe
|
||||
@@ -181,7 +182,7 @@ func (cs *LuetCompiler) CompileParallel(keepPermissions bool, ps CompilationSpec
|
||||
return artifacts, allErrors
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) stripIncludesFromRootfs(includes []string, rootfs string) error {
|
||||
func (cs *LuetCompiler) stripFromRootfs(includes []string, rootfs string, include bool) error {
|
||||
var includeRegexp []*regexp.Regexp
|
||||
for _, i := range includes {
|
||||
r, e := regexp.Compile(i)
|
||||
@@ -213,7 +214,7 @@ func (cs *LuetCompiler) stripIncludesFromRootfs(includes []string, rootfs string
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
if include && !match || !include && match {
|
||||
toRemove = append(toRemove, currentpath)
|
||||
}
|
||||
|
||||
@@ -235,7 +236,58 @@ func (cs *LuetCompiler) stripIncludesFromRootfs(includes []string, rootfs string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage string, concurrency int, keepPermissions, keepImg bool, p CompilationSpec, generateArtifact bool) (Artifact, error) {
|
||||
func (cs *LuetCompiler) unpackFs(rootfs string, concurrency int, p CompilationSpec) (Artifact, error) {
|
||||
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 {
|
||||
// strip from includes
|
||||
cs.stripFromRootfs(p.GetExcludes(), rootfs, false)
|
||||
}
|
||||
artifact := NewPackageArtifact(p.Rel(p.GetPackage().GetFingerPrint() + ".package.tar"))
|
||||
artifact.SetCompressionType(cs.CompressionType)
|
||||
|
||||
if err := artifact.Compress(rootfs, concurrency); err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while creating package archive")
|
||||
}
|
||||
|
||||
artifact.SetCompileSpec(p)
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) unpackDelta(rootfs string, concurrency int, keepPermissions bool, p CompilationSpec, builderOpts, runnerOpts CompilerBackendOptions) (Artifact, error) {
|
||||
pkgTag := ":package: " + p.GetPackage().HumanReadableString()
|
||||
if err := cs.Backend.ExportImage(builderOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Could not export image")
|
||||
}
|
||||
if !cs.Options.KeepImageExport {
|
||||
defer os.Remove(builderOpts.Destination)
|
||||
}
|
||||
Info(pkgTag, ":hammer: Generating delta")
|
||||
diffs, err := cs.Backend.Changes(builderOpts.Destination, runnerOpts.Destination)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not generate changes from layers")
|
||||
}
|
||||
artifact, err := ExtractArtifactFromDelta(rootfs, p.Rel(p.GetPackage().GetFingerPrint()+".package.tar"), diffs, concurrency, keepPermissions, p.GetIncludes(), p.GetExcludes(), cs.CompressionType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not generate deltas")
|
||||
}
|
||||
|
||||
artifact.SetCompileSpec(p)
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) buildPackageImage(image, buildertaggedImage, packageImage string,
|
||||
concurrency int, keepPermissions bool,
|
||||
p CompilationSpec) (CompilerBackendOptions, CompilerBackendOptions, error) {
|
||||
|
||||
var runnerOpts, builderOpts CompilerBackendOptions
|
||||
|
||||
pkgTag := ":package: " + p.GetPackage().HumanReadableString()
|
||||
|
||||
@@ -260,32 +312,23 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
packageImage = cs.ImageRepository + "-" + fp
|
||||
}
|
||||
|
||||
if !cs.Clean {
|
||||
exists := cs.Backend.ImageExists(buildertaggedImage) && cs.Backend.ImageExists(packageImage)
|
||||
if art, err := LoadArtifactFromYaml(p); err == nil && (cs.Options.SkipIfMetadataExists || exists) {
|
||||
Debug("Artifact reloaded. Skipping build")
|
||||
return art, err
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
err := os.MkdirAll(p.Rel("build"), os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while creating tempdir for building")
|
||||
return builderOpts, runnerOpts, errors.Wrap(err, "Error met while creating tempdir for building")
|
||||
}
|
||||
buildDir, err := ioutil.TempDir(p.Rel("build"), "pack")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while creating tempdir for building")
|
||||
return builderOpts, runnerOpts, errors.Wrap(err, "Error met while creating tempdir for building")
|
||||
}
|
||||
defer os.RemoveAll(buildDir) // clean up
|
||||
|
||||
// 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)
|
||||
err = helpers.CopyDir(p.GetPackage().GetPath(), buildDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not copy package sources")
|
||||
|
||||
return builderOpts, runnerOpts, errors.Wrap(err, "Could not copy package sources")
|
||||
}
|
||||
|
||||
// Copy file into the build context, the compilespec might have requested to do so.
|
||||
@@ -299,63 +342,75 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
Info(pkgTag, ":whale: Generating 'builder' image definition from", image)
|
||||
|
||||
// First we create the builder image
|
||||
p.WriteBuildImageDefinition(filepath.Join(buildDir, p.GetPackage().GetFingerPrint()+"-builder.dockerfile"))
|
||||
builderOpts := CompilerBackendOptions{
|
||||
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")
|
||||
}
|
||||
|
||||
// Then we write the step image, which uses the builder one
|
||||
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")
|
||||
}
|
||||
|
||||
builderOpts = CompilerBackendOptions{
|
||||
ImageName: buildertaggedImage,
|
||||
SourcePath: buildDir,
|
||||
DockerFileName: p.GetPackage().GetFingerPrint() + "-builder.dockerfile",
|
||||
Destination: p.Rel(p.GetPackage().GetFingerPrint() + "-builder.image.tar"),
|
||||
}
|
||||
|
||||
buildBuilderImage := true
|
||||
if cs.Options.PullFirst {
|
||||
if err := cs.Backend.DownloadImage(builderOpts); err == nil {
|
||||
buildBuilderImage = false
|
||||
}
|
||||
}
|
||||
|
||||
if buildBuilderImage {
|
||||
if err = cs.Backend.BuildImage(builderOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Could not build image: "+image+" "+builderOpts.DockerFileName)
|
||||
}
|
||||
}
|
||||
|
||||
if err = cs.Backend.ExportImage(builderOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Could not export image")
|
||||
}
|
||||
|
||||
if !cs.Options.KeepImageExport {
|
||||
defer os.Remove(builderOpts.Destination)
|
||||
}
|
||||
|
||||
if cs.Options.Push && buildBuilderImage {
|
||||
if err = cs.Backend.Push(builderOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
|
||||
}
|
||||
}
|
||||
// Then we write the step image, which uses the builder one
|
||||
p.WriteStepImageDefinition(buildertaggedImage, filepath.Join(buildDir, p.GetPackage().GetFingerPrint()+".dockerfile"))
|
||||
runnerOpts := CompilerBackendOptions{
|
||||
runnerOpts = CompilerBackendOptions{
|
||||
ImageName: packageImage,
|
||||
SourcePath: buildDir,
|
||||
DockerFileName: p.GetPackage().GetFingerPrint() + ".dockerfile",
|
||||
Destination: p.Rel(p.GetPackage().GetFingerPrint() + ".image.tar"),
|
||||
}
|
||||
|
||||
buildPackageImage := true
|
||||
if cs.Options.PullFirst {
|
||||
//Best effort pull
|
||||
if err := cs.Backend.DownloadImage(runnerOpts); err == nil {
|
||||
buildPackageImage = false
|
||||
buildAndPush := func(opts CompilerBackendOptions) error {
|
||||
buildImage := true
|
||||
if cs.Options.PullFirst {
|
||||
if err := cs.Backend.DownloadImage(opts); err == nil {
|
||||
buildImage = false
|
||||
}
|
||||
}
|
||||
if buildImage {
|
||||
if err := cs.Backend.BuildImage(opts); err != nil {
|
||||
return errors.Wrap(err, "Could not build image: "+image+" "+opts.DockerFileName)
|
||||
}
|
||||
if cs.Options.Push {
|
||||
if err = cs.Backend.Push(opts); err != nil {
|
||||
return errors.Wrap(err, "Could not push image: "+image+" "+opts.DockerFileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if buildPackageImage {
|
||||
if err := cs.Backend.BuildImage(runnerOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Failed building image for "+runnerOpts.ImageName+" "+runnerOpts.DockerFileName)
|
||||
}
|
||||
if err := buildAndPush(builderOpts); err != nil {
|
||||
return builderOpts, runnerOpts, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
|
||||
}
|
||||
|
||||
if err := buildAndPush(runnerOpts); err != nil {
|
||||
return builderOpts, runnerOpts, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
|
||||
}
|
||||
|
||||
return builderOpts, runnerOpts, nil
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) genArtifact(p CompilationSpec, builderOpts, runnerOpts CompilerBackendOptions, concurrency int, keepPermissions bool) (Artifact, error) {
|
||||
|
||||
// generate Artifact
|
||||
var artifact Artifact
|
||||
var rootfs string
|
||||
var err error
|
||||
unpack := p.ImageUnpack()
|
||||
pkgTag := ":package: " + p.GetPackage().HumanReadableString()
|
||||
|
||||
// If package_dir was specified in the spec, we want to treat the content of the directory
|
||||
// as the root of our archive. ImageUnpack is implied to be true. override it
|
||||
if p.GetPackageDir() != "" {
|
||||
unpack = true
|
||||
}
|
||||
|
||||
// prepare folder content of the image with the package compiled inside
|
||||
if err := cs.Backend.ExportImage(runnerOpts); err != nil {
|
||||
return nil, errors.Wrap(err, "Failed exporting image")
|
||||
}
|
||||
@@ -364,23 +419,7 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
defer os.Remove(runnerOpts.Destination)
|
||||
}
|
||||
|
||||
if cs.Options.Push && buildPackageImage {
|
||||
err = cs.Backend.Push(runnerOpts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not push image: "+image+" "+builderOpts.DockerFileName)
|
||||
}
|
||||
}
|
||||
|
||||
var artifact Artifact
|
||||
unpack := p.ImageUnpack()
|
||||
|
||||
// If package_dir was specified in the spec, we want to treat the content of the directory
|
||||
// as the root of our archive. ImageUnpack is implied to be true. override it
|
||||
if p.GetPackageDir() != "" {
|
||||
unpack = true
|
||||
}
|
||||
|
||||
rootfs, err := ioutil.TempDir(p.GetOutputPath(), "rootfs")
|
||||
rootfs, err = ioutil.TempDir(p.GetOutputPath(), "rootfs")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not create tempdir")
|
||||
}
|
||||
@@ -388,62 +427,24 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
|
||||
// TODO: Compression and such
|
||||
err = cs.Backend.ExtractRootfs(CompilerBackendOptions{
|
||||
ImageName: packageImage,
|
||||
ImageName: runnerOpts.ImageName,
|
||||
SourcePath: runnerOpts.Destination, Destination: rootfs}, keepPermissions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not extract rootfs")
|
||||
}
|
||||
|
||||
if !keepImg {
|
||||
// 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
|
||||
// TODO: Handle caching and optionally do not remove things
|
||||
err = cs.Backend.RemoveImage(builderOpts)
|
||||
if err != nil {
|
||||
Warning("Could not remove image ", builderOpts.ImageName)
|
||||
// return nil, errors.Wrap(err, "Could not remove image")
|
||||
}
|
||||
err = cs.Backend.RemoveImage(runnerOpts)
|
||||
if err != nil {
|
||||
Warning("Could not remove image ", builderOpts.ImageName)
|
||||
// return nil, errors.Wrap(err, "Could not remove image")
|
||||
}
|
||||
}
|
||||
|
||||
if !generateArtifact {
|
||||
return &PackageArtifact{}, nil
|
||||
}
|
||||
|
||||
if unpack {
|
||||
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.stripIncludesFromRootfs(p.GetIncludes(), rootfs)
|
||||
}
|
||||
artifact = NewPackageArtifact(p.Rel(p.GetPackage().GetFingerPrint() + ".package.tar"))
|
||||
artifact.SetCompressionType(cs.CompressionType)
|
||||
|
||||
err = artifact.Compress(rootfs, concurrency)
|
||||
// Take content of container as a base for our package files
|
||||
artifact, err = cs.unpackFs(rootfs, concurrency, p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while creating package archive")
|
||||
}
|
||||
|
||||
artifact.SetCompileSpec(p)
|
||||
} else {
|
||||
Info(pkgTag, ":hammer: Generating delta")
|
||||
diffs, err := cs.Backend.Changes(p.Rel(p.GetPackage().GetFingerPrint()+"-builder.image.tar"), p.Rel(p.GetPackage().GetFingerPrint()+".image.tar"))
|
||||
// Generate delta between the two images
|
||||
artifact, err = cs.unpackDelta(rootfs, concurrency, keepPermissions, p, builderOpts, runnerOpts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not generate changes from layers")
|
||||
return nil, errors.Wrap(err, "Error met while creating package archive")
|
||||
}
|
||||
artifact, err = ExtractArtifactFromDelta(rootfs, p.Rel(p.GetPackage().GetFingerPrint()+".package.tar"), diffs, concurrency, keepPermissions, p.GetIncludes(), cs.CompressionType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not generate deltas")
|
||||
}
|
||||
|
||||
artifact.SetCompileSpec(p)
|
||||
}
|
||||
|
||||
filelist, err := artifact.FileList()
|
||||
@@ -452,7 +453,6 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
}
|
||||
|
||||
artifact.SetFiles(filelist)
|
||||
|
||||
artifact.GetCompileSpec().GetPackage().SetBuildTimestamp(time.Now().String())
|
||||
|
||||
err = artifact.WriteYaml(p.GetOutputPath())
|
||||
@@ -464,6 +464,43 @@ func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) compileWithImage(image, buildertaggedImage, packageImage string,
|
||||
concurrency int,
|
||||
keepPermissions, keepImg bool,
|
||||
p CompilationSpec, generateArtifact bool) (Artifact, error) {
|
||||
|
||||
if !cs.Clean {
|
||||
exists := cs.Backend.ImageExists(buildertaggedImage) && cs.Backend.ImageExists(packageImage)
|
||||
if art, err := LoadArtifactFromYaml(p); err == nil && (cs.Options.SkipIfMetadataExists || exists) {
|
||||
Debug("Artifact reloaded. Skipping build")
|
||||
return art, err
|
||||
}
|
||||
}
|
||||
|
||||
builderOpts, runnerOpts, err := cs.buildPackageImage(image, buildertaggedImage, packageImage, concurrency, keepPermissions, p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed building package image")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if !generateArtifact {
|
||||
return &PackageArtifact{}, nil
|
||||
}
|
||||
|
||||
return cs.genArtifact(p, builderOpts, runnerOpts, concurrency, keepPermissions)
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) FromDatabase(db pkg.PackageDatabase, minimum bool, dst string) ([]CompilationSpec, error) {
|
||||
compilerSpecs := NewLuetCompilationspecs()
|
||||
|
||||
@@ -562,6 +599,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
targetAssertion := p.GetSourceAssertion().Search(p.GetPackage().GetFingerPrint())
|
||||
targetPackageHash := cs.ImageRepository + ":" + targetAssertion.Hash.PackageHash
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Assert solver.PackageAssert
|
||||
}{
|
||||
CompileSpec: p,
|
||||
Assert: *targetAssertion,
|
||||
})
|
||||
|
||||
// - 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() != "" {
|
||||
@@ -602,6 +647,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
Debug(pkgTag, " :arrow_right_hook: :whale: Builder image from", buildImageHash)
|
||||
Debug(pkgTag, " :arrow_right_hook: :whale: Package image name", currentPackageImageHash)
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Assert solver.PackageAssert
|
||||
}{
|
||||
CompileSpec: compileSpec,
|
||||
Assert: assertion,
|
||||
})
|
||||
|
||||
lastHash = currentPackageImageHash
|
||||
if compileSpec.GetImage() != "" {
|
||||
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().HumanReadableString()+" from image")
|
||||
@@ -621,6 +674,15 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
// deperrs = append(deperrs, err)
|
||||
// break // stop at first error
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePostBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Artifact Artifact
|
||||
}{
|
||||
CompileSpec: compileSpec,
|
||||
Artifact: artifact,
|
||||
})
|
||||
|
||||
departifacts = append(departifacts, artifact)
|
||||
Info(pkgTag, ":white_check_mark: Done")
|
||||
}
|
||||
@@ -639,6 +701,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
artifact.SetDependencies(departifacts)
|
||||
artifact.SetSourceAssertion(p.GetSourceAssertion())
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePostBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Artifact Artifact
|
||||
}{
|
||||
CompileSpec: p,
|
||||
Artifact: artifact,
|
||||
})
|
||||
|
||||
return artifact, err
|
||||
} else {
|
||||
return departifacts[len(departifacts)-1], nil
|
||||
@@ -654,32 +724,39 @@ func (cs *LuetCompiler) FromPackage(p pkg.Package) (CompilationSpec, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buildFile := pack.Rel(BuildFile)
|
||||
if !helpers.Exists(buildFile) {
|
||||
return nil, errors.New("No build file present for " + p.GetFingerPrint())
|
||||
}
|
||||
defFile := pack.Rel(DefinitionFile)
|
||||
if !helpers.Exists(defFile) {
|
||||
return nil, errors.New("No build file present for " + p.GetFingerPrint())
|
||||
}
|
||||
def, err := ioutil.ReadFile(defFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var dataresult []byte
|
||||
|
||||
val := pack.Rel(DefinitionFile)
|
||||
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)
|
||||
}
|
||||
packsRaw, err := pkg.GetRawPackages(data)
|
||||
|
||||
raw := packsRaw.Find(pack.GetName(), pack.GetCategory(), pack.GetVersion())
|
||||
|
||||
dat, err := helpers.RenderHelm(string(dataBuild), raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rendering file "+pack.Rel(BuildFile))
|
||||
}
|
||||
dataresult = []byte(dat)
|
||||
} else {
|
||||
out, err := helpers.RenderFiles(pack.Rel(BuildFile), val)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rendering file "+pack.Rel(BuildFile))
|
||||
}
|
||||
dataresult = []byte(out)
|
||||
}
|
||||
|
||||
build, err := ioutil.ReadFile(buildFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var values templatedata
|
||||
if err = yaml.Unmarshal(def, &values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := helpers.RenderHelm(string(build), values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewLuetCompilationSpec([]byte(out), pack)
|
||||
return NewLuetCompilationSpec(dataresult, pack)
|
||||
}
|
||||
|
||||
func (cs *LuetCompiler) GetBackend() CompilerBackend {
|
||||
|
@@ -265,6 +265,146 @@ var _ = Describe("Compiler", func() {
|
||||
Expect(helpers.Exists(spec.Rel("test6"))).ToNot(BeTrue())
|
||||
})
|
||||
|
||||
It("Compiles and excludes files", func() {
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
err = generalRecipe.Load("../../tests/fixtures/excludes")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(1))
|
||||
|
||||
compiler := NewLuetCompiler(sd.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), NewDefaultCompilerOptions(), solver.Options{Type: solver.SingleCoreSimple})
|
||||
|
||||
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// err = generalRecipe.Tree().ResolveDeps(3)
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
spec.SetOutputPath(tmpdir)
|
||||
compiler.SetConcurrency(1)
|
||||
|
||||
artifacts, errs := compiler.CompileParallel(false, NewLuetCompilationspecs(spec))
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(len(artifacts)).To(Equal(1))
|
||||
|
||||
for _, artifact := range artifacts {
|
||||
Expect(helpers.Exists(artifact.GetPath())).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact.GetPath(), tmpdir, false)).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(helpers.Exists(spec.Rel("test5"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("marvin"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("marvot"))).ToNot(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test6"))).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Compiles includes and excludes files", func() {
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
err = generalRecipe.Load("../../tests/fixtures/excludesincludes")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(1))
|
||||
|
||||
compiler := NewLuetCompiler(sd.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), NewDefaultCompilerOptions(), solver.Options{Type: solver.SingleCoreSimple})
|
||||
|
||||
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// err = generalRecipe.Tree().ResolveDeps(3)
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
spec.SetOutputPath(tmpdir)
|
||||
compiler.SetConcurrency(1)
|
||||
|
||||
artifacts, errs := compiler.CompileParallel(false, NewLuetCompilationspecs(spec))
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(len(artifacts)).To(Equal(1))
|
||||
|
||||
for _, artifact := range artifacts {
|
||||
Expect(helpers.Exists(artifact.GetPath())).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact.GetPath(), tmpdir, false)).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(helpers.Exists(spec.Rel("test5"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("marvin"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("marvot"))).ToNot(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test6"))).ToNot(BeTrue())
|
||||
})
|
||||
|
||||
It("Compiles and excludes ony wanted files also from unpacked packages", func() {
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
err = generalRecipe.Load("../../tests/fixtures/excludeimage")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(2))
|
||||
|
||||
compiler := NewLuetCompiler(sd.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), NewDefaultCompilerOptions(), solver.Options{Type: solver.SingleCoreSimple})
|
||||
|
||||
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// err = generalRecipe.Tree().ResolveDeps(3)
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
spec.SetOutputPath(tmpdir)
|
||||
compiler.SetConcurrency(1)
|
||||
artifacts, errs := compiler.CompileParallel(false, NewLuetCompilationspecs(spec))
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(len(artifacts)).To(Equal(1))
|
||||
|
||||
for _, artifact := range artifacts {
|
||||
Expect(helpers.Exists(artifact.GetPath())).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact.GetPath(), tmpdir, false)).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(helpers.Exists(spec.Rel("marvin"))).ToNot(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test5"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test6"))).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Compiles includes and excludes ony wanted files also from unpacked packages", func() {
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
err = generalRecipe.Load("../../tests/fixtures/excludeincludeimage")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(2))
|
||||
|
||||
compiler := NewLuetCompiler(sd.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), NewDefaultCompilerOptions(), solver.Options{Type: solver.SingleCoreSimple})
|
||||
|
||||
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// err = generalRecipe.Tree().ResolveDeps(3)
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
spec.SetOutputPath(tmpdir)
|
||||
compiler.SetConcurrency(1)
|
||||
artifacts, errs := compiler.CompileParallel(false, NewLuetCompilationspecs(spec))
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(len(artifacts)).To(Equal(1))
|
||||
|
||||
for _, artifact := range artifacts {
|
||||
Expect(helpers.Exists(artifact.GetPath())).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact.GetPath(), tmpdir, false)).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(helpers.Exists(spec.Rel("marvin"))).ToNot(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test5"))).To(BeTrue())
|
||||
Expect(helpers.Exists(spec.Rel("test6"))).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Compiles and includes ony wanted files also from unpacked packages", func() {
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
tmpdir, err := ioutil.TempDir("", "package")
|
||||
|
@@ -147,6 +147,7 @@ type ArtifactLayersSummary struct {
|
||||
type CompilationSpec interface {
|
||||
ImageUnpack() bool // tells if the definition is just an image
|
||||
GetIncludes() []string
|
||||
GetExcludes() []string
|
||||
|
||||
RenderBuildImage() (string, error)
|
||||
WriteBuildImageDefinition(string) error
|
||||
|
@@ -102,6 +102,7 @@ type LuetCompilationSpec struct {
|
||||
OutputPath string `json:"-"` // Where the build processfiles go
|
||||
Unpack bool `json:"unpack"`
|
||||
Includes []string `json:"includes"`
|
||||
Excludes []string `json:"excludes"`
|
||||
}
|
||||
|
||||
func NewLuetCompilationSpec(b []byte, p pkg.Package) (CompilationSpec, error) {
|
||||
@@ -148,6 +149,10 @@ func (cs *LuetCompilationSpec) GetIncludes() []string {
|
||||
return cs.Includes
|
||||
}
|
||||
|
||||
func (cs *LuetCompilationSpec) GetExcludes() []string {
|
||||
return cs.Excludes
|
||||
}
|
||||
|
||||
func (cs *LuetCompilationSpec) GetRetrieve() []string {
|
||||
return cs.Retrieve
|
||||
}
|
||||
|
@@ -97,7 +97,7 @@ type LuetSystemConfig struct {
|
||||
TmpDirBase string `yaml:"tmpdir_base" mapstructure:"tmpdir_base"`
|
||||
}
|
||||
|
||||
func (sc LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
func (sc *LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
dbpath := filepath.Join(sc.Rootfs, sc.DatabasePath)
|
||||
dbpath = filepath.Join(dbpath, "repos/"+name)
|
||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||
@@ -107,7 +107,7 @@ func (sc LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
return dbpath
|
||||
}
|
||||
|
||||
func (sc LuetSystemConfig) GetSystemRepoDatabaseDirPath() string {
|
||||
func (sc *LuetSystemConfig) GetSystemRepoDatabaseDirPath() string {
|
||||
dbpath := filepath.Join(sc.Rootfs,
|
||||
sc.DatabasePath)
|
||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||
@@ -117,7 +117,7 @@ func (sc LuetSystemConfig) GetSystemRepoDatabaseDirPath() string {
|
||||
return dbpath
|
||||
}
|
||||
|
||||
func (sc LuetSystemConfig) GetSystemPkgsCacheDirPath() (ans string) {
|
||||
func (sc *LuetSystemConfig) GetSystemPkgsCacheDirPath() (ans string) {
|
||||
var cachepath string
|
||||
if sc.PkgsCachePath != "" {
|
||||
cachepath = sc.PkgsCachePath
|
||||
@@ -135,6 +135,10 @@ func (sc LuetSystemConfig) GetSystemPkgsCacheDirPath() (ans string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (sc *LuetSystemConfig) GetRootFsAbs() (string, error) {
|
||||
return filepath.Abs(sc.Rootfs)
|
||||
}
|
||||
|
||||
type LuetRepository struct {
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description"`
|
||||
@@ -204,6 +208,7 @@ type LuetConfig struct {
|
||||
RepositoriesConfDir []string `mapstructure:"repos_confdir"`
|
||||
ConfigProtectConfDir []string `mapstructure:"config_protect_confdir"`
|
||||
ConfigProtectSkip bool `mapstructure:"config_protect_skip"`
|
||||
ConfigFromHost bool `mapstructure:"config_from_host"`
|
||||
CacheRepositories []LuetRepository `mapstructure:"repetitors"`
|
||||
SystemRepositories []LuetRepository `mapstructure:"repositories"`
|
||||
|
||||
@@ -251,6 +256,8 @@ func GenDefault(viper *v.Viper) {
|
||||
viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"})
|
||||
viper.SetDefault("config_protect_confdir", []string{"/etc/luet/config.protect.d"})
|
||||
viper.SetDefault("config_protect_skip", false)
|
||||
// TODO: Set default to false when we are ready for migration.
|
||||
viper.SetDefault("config_from_host", true)
|
||||
viper.SetDefault("cache_repositories", []string{})
|
||||
viper.SetDefault("system_repositories", []string{})
|
||||
|
||||
|
@@ -24,6 +24,37 @@ import (
|
||||
copy "github.com/otiai10/copy"
|
||||
)
|
||||
|
||||
func OrderFiles(target string, files []string) ([]string, []string) {
|
||||
|
||||
var newFiles []string
|
||||
var notPresent []string
|
||||
|
||||
for _, f := range files {
|
||||
target := filepath.Join(target, f)
|
||||
fi, err := os.Lstat(target)
|
||||
if err != nil {
|
||||
notPresent = append(notPresent, f)
|
||||
continue
|
||||
}
|
||||
if m := fi.Mode(); !m.IsDir() {
|
||||
newFiles = append(newFiles, f)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
target := filepath.Join(target, f)
|
||||
fi, err := os.Lstat(target)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if m := fi.Mode(); m.IsDir() {
|
||||
newFiles = append(newFiles, f)
|
||||
}
|
||||
}
|
||||
|
||||
return newFiles, notPresent
|
||||
}
|
||||
|
||||
func ListDir(dir string) ([]string, error) {
|
||||
content := []string{}
|
||||
|
||||
|
@@ -16,6 +16,10 @@
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -28,4 +32,33 @@ var _ = Describe("Helpers", func() {
|
||||
Expect(Exists("../../tests/fixtures/buildtree/app-admin/enman/1.4.0/build.yaml.not.exists")).To(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Orders dir and files correctly", func() {
|
||||
It("puts files first and folders at end", func() {
|
||||
testDir, err := ioutil.TempDir(os.TempDir(), "test")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(testDir, "foo"), []byte("test\n"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(testDir, "baz"), []byte("test\n"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.MkdirAll(filepath.Join(testDir, "bar"), 0755)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = ioutil.WriteFile(filepath.Join(testDir, "bar", "foo"), []byte("test\n"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.MkdirAll(filepath.Join(testDir, "baz2"), 0755)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = ioutil.WriteFile(filepath.Join(testDir, "baz2", "foo"), []byte("test\n"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ordered, notExisting := OrderFiles(testDir, []string{"bar", "baz", "bar/foo", "baz2", "foo", "baz2/foo", "notexisting"})
|
||||
|
||||
Expect(ordered).To(Equal([]string{"baz", "bar/foo", "foo", "baz2/foo", "bar", "baz2"}))
|
||||
Expect(notExisting).To(Equal([]string{"notexisting"}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v2"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
@@ -31,3 +34,26 @@ func RenderHelm(template string, values map[string]interface{}) (string, error)
|
||||
|
||||
return out["templates"], nil
|
||||
}
|
||||
|
||||
type templatedata map[string]interface{}
|
||||
|
||||
func RenderFiles(toTemplate, valuesFile string) (string, error) {
|
||||
raw, err := ioutil.ReadFile(toTemplate)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "reading file "+toTemplate)
|
||||
}
|
||||
|
||||
if !Exists(valuesFile) {
|
||||
return "", errors.Wrap(err, "file not existing "+valuesFile)
|
||||
}
|
||||
def, err := ioutil.ReadFile(valuesFile)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "reading file "+valuesFile)
|
||||
}
|
||||
|
||||
var values templatedata
|
||||
if err = yaml.Unmarshal(def, &values); err != nil {
|
||||
return "", errors.Wrap(err, "unmarshalling file "+toTemplate)
|
||||
}
|
||||
return RenderHelm(string(raw), values)
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
|
||||
@@ -101,6 +102,29 @@ func (c *HttpClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Arti
|
||||
}
|
||||
|
||||
resp := client.Do(req)
|
||||
|
||||
// start download loop
|
||||
t := time.NewTicker(500 * time.Millisecond)
|
||||
defer t.Stop()
|
||||
|
||||
download_loop:
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
Info(fmt.Sprintf("[%50s] [%2.2f%%] %.02f / %.02f MB",
|
||||
filepath.Base(resp.Request.HTTPRequest.URL.RequestURI()),
|
||||
100*resp.Progress(),
|
||||
float64(resp.BytesComplete())/(1000*1000),
|
||||
float64(resp.Size())/(1000*1000),
|
||||
))
|
||||
|
||||
case <-resp.Done:
|
||||
// download is complete
|
||||
break download_loop
|
||||
}
|
||||
}
|
||||
|
||||
if err = resp.Err(); err != nil {
|
||||
continue
|
||||
}
|
||||
|
@@ -38,15 +38,26 @@ func NewLocalClient(r RepoData) *LocalClient {
|
||||
func (c *LocalClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Artifact, error) {
|
||||
var err error
|
||||
|
||||
rootfs := ""
|
||||
artifactName := path.Base(artifact.GetPath())
|
||||
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
|
||||
|
||||
if !config.LuetCfg.ConfigFromHost {
|
||||
rootfs, err = config.LuetCfg.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if file is already in cache
|
||||
if helpers.Exists(cacheFile) {
|
||||
Info("Use artifact", artifactName, "from cache.")
|
||||
} else {
|
||||
ok := false
|
||||
for _, uri := range c.RepoData.Urls {
|
||||
|
||||
uri = filepath.Join(rootfs, uri)
|
||||
|
||||
Info("Downloading artifact", artifactName, "from", uri)
|
||||
|
||||
//defer os.Remove(file.Name())
|
||||
@@ -72,8 +83,20 @@ func (c *LocalClient) DownloadFile(name string) (string, error) {
|
||||
var err error
|
||||
var file *os.File = nil
|
||||
|
||||
rootfs := ""
|
||||
|
||||
if !config.LuetCfg.ConfigFromHost {
|
||||
rootfs, err = config.LuetCfg.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
ok := false
|
||||
for _, uri := range c.RepoData.Urls {
|
||||
|
||||
uri = filepath.Join(rootfs, uri)
|
||||
|
||||
Info("Downloading file", name, "from", uri)
|
||||
file, err = config.LuetCfg.GetSystem().TempFile("localclient")
|
||||
if err != nil {
|
||||
|
@@ -19,6 +19,7 @@ package installer
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -29,8 +30,21 @@ import (
|
||||
|
||||
func LoadConfigProtectConfs(c *LuetConfig) error {
|
||||
var regexConfs = regexp.MustCompile(`.yml$`)
|
||||
var err error
|
||||
|
||||
rootfs := ""
|
||||
|
||||
// Respect the rootfs param on read repositories
|
||||
if !c.ConfigFromHost {
|
||||
rootfs, err = c.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cdir := range c.ConfigProtectConfDir {
|
||||
cdir = filepath.Join(rootfs, cdir)
|
||||
|
||||
Debug("Parsing Config Protect Directory", cdir, "...")
|
||||
|
||||
files, err := ioutil.ReadDir(cdir)
|
||||
|
@@ -24,13 +24,13 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mudler/luet/pkg/bus"
|
||||
compiler "github.com/mudler/luet/pkg/compiler"
|
||||
"github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"github.com/mudler/luet/pkg/solver"
|
||||
"github.com/mudler/luet/pkg/tree"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -452,9 +452,9 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp pkg.Packages, s *Sy
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Failed creating package")
|
||||
}
|
||||
bus.Manager.Publish(bus.EventPackageInstall, c)
|
||||
}
|
||||
executedFinalizer := map[string]bool{}
|
||||
|
||||
var toFinalize []pkg.Package
|
||||
if !l.Options.NoDeps {
|
||||
// TODO: Lower those errors as warning
|
||||
for _, w := range p {
|
||||
@@ -466,36 +466,17 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp pkg.Packages, s *Sy
|
||||
ORDER:
|
||||
for _, ass := range ordered {
|
||||
if ass.Value {
|
||||
|
||||
installed, ok := toInstall[ass.Package.GetFingerPrint()]
|
||||
if !ok {
|
||||
// It was a dep already installed in the system, so we can skip it safely
|
||||
continue ORDER
|
||||
}
|
||||
|
||||
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
|
||||
}
|
||||
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
|
||||
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
if _, exists := executedFinalizer[ass.Package.GetFingerPrint()]; !exists {
|
||||
Info("Executing finalizer for " + ass.Package.HumanReadableString())
|
||||
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
err = finalizer.RunInstall(s)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
executedFinalizer[ass.Package.GetFingerPrint()] = true
|
||||
}
|
||||
}
|
||||
|
||||
toFinalize = append(toFinalize, treePackage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,29 +487,11 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp pkg.Packages, s *Sy
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
|
||||
}
|
||||
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
|
||||
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
if _, exists := executedFinalizer[c.Package.GetFingerPrint()]; !exists {
|
||||
Info(":shell: Executing finalizer for " + c.Package.HumanReadableString())
|
||||
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
err = finalizer.RunInstall(s)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
|
||||
}
|
||||
executedFinalizer[c.Package.GetFingerPrint()] = true
|
||||
}
|
||||
}
|
||||
toFinalize = append(toFinalize, treePackage)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
return s.ExecuteFinalizers(toFinalize, l.Options.Force)
|
||||
}
|
||||
|
||||
func (l *LuetInstaller) downloadPackage(a ArtifactMatch) (compiler.Artifact, error) {
|
||||
@@ -631,8 +594,10 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
||||
cp.Map(files)
|
||||
}
|
||||
|
||||
toRemove, notPresent := helpers.OrderFiles(s.Target, files)
|
||||
|
||||
// Remove from target
|
||||
for _, f := range files {
|
||||
for _, f := range toRemove {
|
||||
target := filepath.Join(s.Target, f)
|
||||
|
||||
if !config.LuetCfg.ConfigProtectSkip && cp.Protected(f) {
|
||||
@@ -650,10 +615,7 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
||||
|
||||
fi, err := os.Lstat(target)
|
||||
if err != nil {
|
||||
Warning("File not present in the system target ?", target, err.Error())
|
||||
if err = os.Remove(target); err != nil {
|
||||
Warning("Failed removing file", target, err.Error())
|
||||
}
|
||||
Warning("File not found (it was before?) ", err.Error())
|
||||
continue
|
||||
}
|
||||
switch mode := fi.Mode(); {
|
||||
@@ -663,15 +625,29 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
||||
Warning("Failed reading folder", target, err.Error())
|
||||
}
|
||||
if len(files) != 0 {
|
||||
Warning("Preserving not-empty folder", target, err.Error())
|
||||
Debug("Preserving not-empty folder", target)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err = os.Remove(target); err != nil {
|
||||
Warning("Failed removing file (not present in the system target ?)", target, err.Error())
|
||||
Warning("Failed removing file (maybe not present in the system target anymore ?)", target, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range notPresent {
|
||||
target := filepath.Join(s.Target, f)
|
||||
|
||||
if !config.LuetCfg.ConfigProtectSkip && cp.Protected(f) {
|
||||
Debug("Preserving protected file:", f)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = os.Remove(target); err != nil {
|
||||
Debug("Failed removing file (not present in the system target)", target, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Database.RemovePackageFiles(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed removing package files from database")
|
||||
@@ -681,6 +657,8 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
||||
return errors.Wrap(err, "Failed removing package from database")
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackageUnInstall, p)
|
||||
|
||||
Info(":recycle:", p.GetFingerPrint(), "Removed :heavy_check_mark:")
|
||||
return nil
|
||||
}
|
||||
@@ -722,7 +700,7 @@ func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
|
||||
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
|
||||
}
|
||||
} else {
|
||||
solution, err = solv.Uninstall(p, checkConflicts, full)
|
||||
solution, err = solv.Uninstall(checkConflicts, full, p)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/luet/pkg/bus"
|
||||
"github.com/mudler/luet/pkg/compiler"
|
||||
"github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
@@ -426,6 +427,14 @@ func (r *LuetSystemRepository) Write(dst string, resetRevision bool) error {
|
||||
r.Name, r.Revision, r.LastUpdate,
|
||||
))
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPreBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
}{
|
||||
Repo: *r,
|
||||
Path: dst,
|
||||
})
|
||||
|
||||
// Create tree and repository file
|
||||
archive, err := config.LuetCfg.GetSystem().TempDir("archive")
|
||||
if err != nil {
|
||||
@@ -506,6 +515,14 @@ func (r *LuetSystemRepository) Write(dst string, resetRevision bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPostBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
}{
|
||||
Repo: *r,
|
||||
Path: dst,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,12 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"github.com/mudler/luet/pkg/tree"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type System struct {
|
||||
@@ -12,3 +17,31 @@ type System struct {
|
||||
func (s *System) World() (pkg.Packages, error) {
|
||||
return s.Database.World(), nil
|
||||
}
|
||||
|
||||
type templatedata map[string]interface{}
|
||||
|
||||
func (s *System) ExecuteFinalizers(packs []pkg.Package, force bool) error {
|
||||
executedFinalizer := map[string]bool{}
|
||||
for _, p := range packs {
|
||||
if helpers.Exists(p.Rel(tree.FinalizerFile)) {
|
||||
out, err := helpers.RenderFiles(p.Rel(tree.FinalizerFile), p.Rel(tree.DefinitionFile))
|
||||
if err != nil && !force {
|
||||
return errors.Wrap(err, "reading file "+p.Rel(tree.FinalizerFile))
|
||||
}
|
||||
|
||||
if _, exists := executedFinalizer[p.GetFingerPrint()]; !exists {
|
||||
Info("Executing finalizer for " + p.HumanReadableString())
|
||||
finalizer, err := NewLuetFinalizerFromYaml([]byte(out))
|
||||
if err != nil && !force {
|
||||
return errors.Wrap(err, "Error reading finalizer "+p.Rel(tree.FinalizerFile))
|
||||
}
|
||||
err = finalizer.RunInstall(s)
|
||||
if err != nil && !force {
|
||||
return errors.Wrap(err, "Error executing install finalizer "+p.Rel(tree.FinalizerFile))
|
||||
}
|
||||
executedFinalizer[p.GetFingerPrint()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -147,6 +147,60 @@ func DefaultPackageFromYaml(yml []byte) (DefaultPackage, error) {
|
||||
return unescaped, nil
|
||||
}
|
||||
|
||||
type rawPackages []map[string]interface{}
|
||||
|
||||
func (r rawPackages) Find(name, category, version string) map[string]interface{} {
|
||||
for _, v := range r {
|
||||
if v["name"] == name && v["category"] == category && v["version"] == version {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
func GetRawPackages(yml []byte) (rawPackages, error) {
|
||||
var rawPackages struct {
|
||||
Packages []map[string]interface{} `yaml:"packages"`
|
||||
}
|
||||
source, err := yaml.YAMLToJSON(yml)
|
||||
if err != nil {
|
||||
return []map[string]interface{}{}, err
|
||||
}
|
||||
|
||||
rawIn := json.RawMessage(source)
|
||||
bytes, err := rawIn.MarshalJSON()
|
||||
if err != nil {
|
||||
return []map[string]interface{}{}, err
|
||||
}
|
||||
err = json.Unmarshal(bytes, &rawPackages)
|
||||
if err != nil {
|
||||
return []map[string]interface{}{}, err
|
||||
}
|
||||
return rawPackages.Packages, nil
|
||||
|
||||
}
|
||||
func DefaultPackagesFromYaml(yml []byte) ([]DefaultPackage, error) {
|
||||
|
||||
var unescaped struct {
|
||||
Packages []DefaultPackage `json:"packages"`
|
||||
}
|
||||
source, err := yaml.YAMLToJSON(yml)
|
||||
if err != nil {
|
||||
return []DefaultPackage{}, err
|
||||
}
|
||||
|
||||
rawIn := json.RawMessage(source)
|
||||
bytes, err := rawIn.MarshalJSON()
|
||||
if err != nil {
|
||||
return []DefaultPackage{}, err
|
||||
}
|
||||
err = json.Unmarshal(bytes, &unescaped)
|
||||
if err != nil {
|
||||
return []DefaultPackage{}, err
|
||||
}
|
||||
return unescaped.Packages, nil
|
||||
}
|
||||
|
||||
// Major and minor gets escaped when marshalling in JSON, making compiler fails recognizing selectors for expansion
|
||||
func (t *DefaultPackage) JSON() ([]byte, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
|
@@ -19,6 +19,7 @@ package repository
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -29,8 +30,21 @@ import (
|
||||
|
||||
func LoadRepositories(c *LuetConfig) error {
|
||||
var regexRepo = regexp.MustCompile(`.yml$|.yaml$`)
|
||||
var err error
|
||||
rootfs := ""
|
||||
|
||||
// Respect the rootfs param on read repositories
|
||||
if !c.ConfigFromHost {
|
||||
rootfs, err = c.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rdir := range c.RepositoriesConfDir {
|
||||
|
||||
rdir = filepath.Join(rootfs, rdir)
|
||||
|
||||
Debug("Parsing Repository Directory", rdir, "...")
|
||||
|
||||
files, err := ioutil.ReadDir(rdir)
|
||||
|
@@ -615,23 +615,22 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
|
||||
}
|
||||
|
||||
// Then try to uninstall the versions in the system, and store that tree
|
||||
for _, p := range toUninstall {
|
||||
r, err := s.Uninstall(p, checkconflicts, false)
|
||||
r, err := s.Uninstall(checkconflicts, false, toUninstall...)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall candidates ")
|
||||
}
|
||||
for _, z := range r {
|
||||
err = installedcopy.RemovePackage(z)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
|
||||
}
|
||||
for _, z := range r {
|
||||
err = installedcopy.RemovePackage(z)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
|
||||
}
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
|
||||
}
|
||||
}
|
||||
|
||||
if len(toInstall) == 0 {
|
||||
return toUninstall, PackagesAssertions{}, nil
|
||||
}
|
||||
r, e := s2.Install(toInstall)
|
||||
return toUninstall, r, e
|
||||
assertions, e := s2.Install(toInstall)
|
||||
return toUninstall, assertions, e
|
||||
// To that tree, ask to install the versions that should be upgraded, and try to solve
|
||||
// Return the solution
|
||||
|
||||
@@ -639,21 +638,30 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
|
||||
|
||||
// Uninstall takes a candidate package and return a list of packages that would be removed
|
||||
// in order to purge the candidate. Returns error if unsat.
|
||||
func (s *Parallel) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packages, error) {
|
||||
func (s *Parallel) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg.Packages, error) {
|
||||
if len(packs) == 0 {
|
||||
return pkg.Packages{}, nil
|
||||
}
|
||||
var res pkg.Packages
|
||||
candidate, err := s.InstalledDatabase.FindPackage(c)
|
||||
if err != nil {
|
||||
toRemove := pkg.Packages{}
|
||||
|
||||
// return nil, errors.Wrap(err, "Couldn't find required package in db definition")
|
||||
packages, err := c.Expand(s.InstalledDatabase)
|
||||
// Info("Expanded", packages, err)
|
||||
if err != nil || len(packages) == 0 {
|
||||
candidate = c
|
||||
} else {
|
||||
candidate = packages.Best(nil)
|
||||
for _, c := range packs {
|
||||
candidate, err := s.InstalledDatabase.FindPackage(c)
|
||||
if err != nil {
|
||||
|
||||
// return nil, errors.Wrap(err, "Couldn't find required package in db definition")
|
||||
packages, err := c.Expand(s.InstalledDatabase)
|
||||
// Info("Expanded", packages, err)
|
||||
if err != nil || len(packages) == 0 {
|
||||
candidate = c
|
||||
} else {
|
||||
candidate = packages.Best(nil)
|
||||
}
|
||||
//Relax search, otherwise we cannot compute solutions for packages not in definitions
|
||||
// return nil, errors.Wrap(err, "Package not found between installed")
|
||||
}
|
||||
//Relax search, otherwise we cannot compute solutions for packages not in definitions
|
||||
// return nil, errors.Wrap(err, "Package not found between installed")
|
||||
|
||||
toRemove = append(toRemove, candidate)
|
||||
}
|
||||
// Build a fake "Installed" - Candidate and its requires tree
|
||||
var InstalledMinusCandidate pkg.Packages
|
||||
@@ -661,30 +669,38 @@ func (s *Parallel) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Pack
|
||||
// We are asked to not perform a full uninstall (checking all the possible requires that could
|
||||
// be removed). Let's only check if we can remove the selected package
|
||||
if !full && checkconflicts {
|
||||
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
|
||||
return nil, err
|
||||
} else {
|
||||
return pkg.Packages{candidate}, nil
|
||||
for _, candidate := range toRemove {
|
||||
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return toRemove, nil
|
||||
}
|
||||
|
||||
// TODO: Can be optimized
|
||||
for _, i := range s.Installed() {
|
||||
if !i.Matches(candidate) {
|
||||
contains, err := candidate.RequiresContains(s.ParallelDatabase, i)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed getting installed list")
|
||||
}
|
||||
if !contains {
|
||||
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
|
||||
matched := false
|
||||
for _, candidate := range toRemove {
|
||||
if !i.Matches(candidate) {
|
||||
contains, err := candidate.RequiresContains(s.ParallelDatabase, i)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed getting installed list")
|
||||
}
|
||||
if !contains {
|
||||
matched = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
|
||||
}
|
||||
}
|
||||
|
||||
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: pkg.NewInMemoryDatabase(false), DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
|
||||
s2.SetResolver(s.Resolver)
|
||||
// Get the requirements to install the candidate
|
||||
asserts, err := s2.Install(pkg.Packages{candidate})
|
||||
asserts, err := s2.Install(toRemove)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -593,7 +593,7 @@ var _ = Describe("Parallel", func() {
|
||||
}
|
||||
s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db}
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -619,7 +619,7 @@ var _ = Describe("Parallel", func() {
|
||||
}
|
||||
s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db}
|
||||
|
||||
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true, true)
|
||||
solution, err := s.Uninstall(true, true, &pkg.DefaultPackage{Name: "A", Version: ">1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -643,7 +643,7 @@ var _ = Describe("Parallel", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -667,7 +667,7 @@ var _ = Describe("Parallel", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -690,7 +690,7 @@ var _ = Describe("Parallel", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -699,6 +699,31 @@ var _ = Describe("Parallel", func() {
|
||||
Expect(len(solution)).To(Equal(1))
|
||||
})
|
||||
|
||||
It("Uninstalls multiple complex packages correctly, even if shared deps are required by system packages", func() {
|
||||
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||
|
||||
for _, p := range []pkg.Package{A, B, C, D} {
|
||||
_, err := dbDefinitions.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
for _, p := range []pkg.Package{A, B, C, D} {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(true, true, A, C)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(solution).To(ContainElement(C))
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
Expect(solution).ToNot(ContainElement(B))
|
||||
|
||||
Expect(len(solution)).To(Equal(2))
|
||||
})
|
||||
|
||||
It("Uninstalls complex packages in world correctly", func() {
|
||||
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||
@@ -715,7 +740,7 @@ var _ = Describe("Parallel", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -741,7 +766,7 @@ var _ = Describe("Parallel", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -1070,7 +1095,7 @@ var _ = Describe("Parallel", func() {
|
||||
}
|
||||
|
||||
val, err := s.Conflicts(D, dbInstalled.World())
|
||||
Expect(err.Error()).To(Equal("\n/A-\n/B-"))
|
||||
Expect(err.Error()).To(Or(Equal("\n/A-\n/B-"), Equal("\n/B-\n/A-")))
|
||||
Expect(val).To(BeTrue())
|
||||
})
|
||||
|
||||
|
@@ -37,7 +37,7 @@ const (
|
||||
type PackageSolver interface {
|
||||
SetDefinitionDatabase(pkg.PackageDatabase)
|
||||
Install(p pkg.Packages) (PackagesAssertions, error)
|
||||
Uninstall(candidate pkg.Package, checkconflicts, full bool) (pkg.Packages, error)
|
||||
Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error)
|
||||
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
||||
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
|
||||
Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error)
|
||||
@@ -518,23 +518,22 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
|
||||
}
|
||||
}
|
||||
// Then try to uninstall the versions in the system, and store that tree
|
||||
for _, p := range toUninstall {
|
||||
r, err := s.Uninstall(p, checkconflicts, false)
|
||||
r, err := s.Uninstall(checkconflicts, false, toUninstall...)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall candidates ")
|
||||
}
|
||||
for _, z := range r {
|
||||
err = installedcopy.RemovePackage(z)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
|
||||
}
|
||||
for _, z := range r {
|
||||
err = installedcopy.RemovePackage(z)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
|
||||
}
|
||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
|
||||
}
|
||||
}
|
||||
|
||||
if len(toInstall) == 0 {
|
||||
return toUninstall, PackagesAssertions{}, nil
|
||||
}
|
||||
r, e := s2.Install(toInstall)
|
||||
return toUninstall, r, e
|
||||
assertions, err := s2.Install(toInstall)
|
||||
return toUninstall, assertions, err
|
||||
// To that tree, ask to install the versions that should be upgraded, and try to solve
|
||||
// Return the solution
|
||||
|
||||
@@ -542,52 +541,71 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
|
||||
|
||||
// Uninstall takes a candidate package and return a list of packages that would be removed
|
||||
// in order to purge the candidate. Returns error if unsat.
|
||||
func (s *Solver) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packages, error) {
|
||||
var res pkg.Packages
|
||||
candidate, err := s.InstalledDatabase.FindPackage(c)
|
||||
if err != nil {
|
||||
|
||||
// return nil, errors.Wrap(err, "Couldn't find required package in db definition")
|
||||
packages, err := c.Expand(s.InstalledDatabase)
|
||||
// Info("Expanded", packages, err)
|
||||
if err != nil || len(packages) == 0 {
|
||||
candidate = c
|
||||
} else {
|
||||
candidate = packages.Best(nil)
|
||||
}
|
||||
//Relax search, otherwise we cannot compute solutions for packages not in definitions
|
||||
// return nil, errors.Wrap(err, "Package not found between installed")
|
||||
func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg.Packages, error) {
|
||||
if len(packs) == 0 {
|
||||
return pkg.Packages{}, nil
|
||||
}
|
||||
var res pkg.Packages
|
||||
|
||||
toRemove := pkg.Packages{}
|
||||
|
||||
for _, c := range packs {
|
||||
candidate, err := s.InstalledDatabase.FindPackage(c)
|
||||
if err != nil {
|
||||
|
||||
// return nil, errors.Wrap(err, "Couldn't find required package in db definition")
|
||||
packages, err := c.Expand(s.InstalledDatabase)
|
||||
// Info("Expanded", packages, err)
|
||||
if err != nil || len(packages) == 0 {
|
||||
candidate = c
|
||||
} else {
|
||||
candidate = packages.Best(nil)
|
||||
}
|
||||
//Relax search, otherwise we cannot compute solutions for packages not in definitions
|
||||
// return nil, errors.Wrap(err, "Package not found between installed")
|
||||
}
|
||||
|
||||
toRemove = append(toRemove, candidate)
|
||||
}
|
||||
|
||||
// Build a fake "Installed" - Candidate and its requires tree
|
||||
var InstalledMinusCandidate pkg.Packages
|
||||
|
||||
// We are asked to not perform a full uninstall (checking all the possible requires that could
|
||||
// be removed). Let's only check if we can remove the selected package
|
||||
if !full && checkconflicts {
|
||||
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
|
||||
return nil, err
|
||||
} else {
|
||||
return pkg.Packages{candidate}, nil
|
||||
for _, candidate := range toRemove {
|
||||
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return toRemove, nil
|
||||
}
|
||||
|
||||
// TODO: Can be optimized
|
||||
for _, i := range s.Installed() {
|
||||
if !i.Matches(candidate) {
|
||||
contains, err := candidate.RequiresContains(s.SolverDatabase, i)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed getting installed list")
|
||||
}
|
||||
if !contains {
|
||||
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
|
||||
matched := false
|
||||
for _, candidate := range toRemove {
|
||||
if !i.Matches(candidate) {
|
||||
contains, err := candidate.RequiresContains(s.SolverDatabase, i)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed getting installed list")
|
||||
}
|
||||
if !contains {
|
||||
matched = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
|
||||
}
|
||||
}
|
||||
|
||||
s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
|
||||
s2.SetResolver(s.Resolver)
|
||||
// Get the requirements to install the candidate
|
||||
asserts, err := s2.Install(pkg.Packages{candidate})
|
||||
asserts, err := s2.Install(toRemove)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -593,7 +593,7 @@ var _ = Describe("Solver", func() {
|
||||
}
|
||||
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -619,7 +619,7 @@ var _ = Describe("Solver", func() {
|
||||
}
|
||||
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||
|
||||
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true, true)
|
||||
solution, err := s.Uninstall(true, true, &pkg.DefaultPackage{Name: "A", Version: ">1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -643,7 +643,7 @@ var _ = Describe("Solver", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -667,7 +667,7 @@ var _ = Describe("Solver", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -690,7 +690,7 @@ var _ = Describe("Solver", func() {
|
||||
_, err := dbInstalled.CreatePackage(p)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -715,7 +715,7 @@ var _ = Describe("Solver", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -741,7 +741,7 @@ var _ = Describe("Solver", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
solution, err := s.Uninstall(A, true, true)
|
||||
solution, err := s.Uninstall(true, true, A)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(solution).To(ContainElement(A))
|
||||
@@ -1070,7 +1070,7 @@ var _ = Describe("Solver", func() {
|
||||
}
|
||||
|
||||
val, err := s.Conflicts(D, dbInstalled.World())
|
||||
Expect(err.Error()).To(Equal("\n/A-\n/B-"))
|
||||
Expect(err.Error()).To(Or(Equal("\n/A-\n/B-"), Equal("\n/B-\n/A-")))
|
||||
Expect(val).To(BeTrue())
|
||||
})
|
||||
|
||||
|
@@ -44,6 +44,14 @@ type DefaultPackageSanitized struct {
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
}
|
||||
|
||||
func NewDefaultPackageSanitizedFromYaml(data []byte) (*DefaultPackageSanitized, error) {
|
||||
ans := &DefaultPackageSanitized{}
|
||||
if err := yaml.Unmarshal(data, ans); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func NewDefaultPackageSanitized(p pkg.Package) *DefaultPackageSanitized {
|
||||
ans := &DefaultPackageSanitized{
|
||||
Name: p.GetName(),
|
||||
@@ -110,3 +118,12 @@ func NewDefaultPackageSanitized(p pkg.Package) *DefaultPackageSanitized {
|
||||
func (p *DefaultPackageSanitized) Yaml() ([]byte, error) {
|
||||
return yaml.Marshal(p)
|
||||
}
|
||||
|
||||
func (p *DefaultPackageSanitized) Clone() (*DefaultPackageSanitized, error) {
|
||||
data, err := p.Yaml()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDefaultPackageSanitizedFromYaml(data)
|
||||
}
|
||||
|
@@ -1,32 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 gentoo_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/mudler/luet/cmd"
|
||||
config "github.com/mudler/luet/pkg/config"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestGentooBuilder(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
LoadConfig(config.LuetCfg)
|
||||
RunSpecs(t, "Gentoo Suite")
|
||||
}
|
@@ -1,142 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 gentoo
|
||||
|
||||
// NOTE: Look here as an example of the builder definition executor
|
||||
// https://gist.github.com/adnaan/6ca68c7985c6f851def3
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
tree "github.com/mudler/luet/pkg/tree"
|
||||
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
)
|
||||
|
||||
type MemoryDB int
|
||||
|
||||
const (
|
||||
InMemory MemoryDB = iota
|
||||
BoltDB MemoryDB = iota
|
||||
)
|
||||
|
||||
func NewGentooBuilder(e EbuildParser, concurrency int, db MemoryDB) tree.Parser {
|
||||
return &GentooBuilder{EbuildParser: e, Concurrency: concurrency}
|
||||
}
|
||||
|
||||
type GentooBuilder struct {
|
||||
EbuildParser EbuildParser
|
||||
Concurrency int
|
||||
DBType MemoryDB
|
||||
}
|
||||
|
||||
type EbuildParser interface {
|
||||
ScanEbuild(string) (pkg.Packages, error)
|
||||
}
|
||||
|
||||
func (gb *GentooBuilder) scanEbuild(path string, db pkg.PackageDatabase) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
Error(r)
|
||||
}
|
||||
}()
|
||||
pkgs, err := gb.EbuildParser.ScanEbuild(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range pkgs {
|
||||
_, err := db.FindPackage(p)
|
||||
if err != nil {
|
||||
_, err := db.CreatePackage(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gb *GentooBuilder) worker(i int, wg *sync.WaitGroup, s <-chan string, db pkg.PackageDatabase) {
|
||||
defer wg.Done()
|
||||
|
||||
for path := range s {
|
||||
Info("#"+strconv.Itoa(i), "parsing", path)
|
||||
err := gb.scanEbuild(path, db)
|
||||
if err != nil {
|
||||
Error(path, ":", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (gb *GentooBuilder) Generate(dir string) (pkg.PackageDatabase, error) {
|
||||
|
||||
var toScan = make(chan string)
|
||||
Spinner(27)
|
||||
defer SpinnerStop()
|
||||
var db pkg.PackageDatabase
|
||||
// Support for
|
||||
switch gb.DBType {
|
||||
case InMemory:
|
||||
db = pkg.NewInMemoryDatabase(false)
|
||||
case BoltDB:
|
||||
tmpfile, err := ioutil.TempFile("", "boltdb")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db = pkg.NewBoltDatabase(tmpfile.Name())
|
||||
default:
|
||||
db = pkg.NewInMemoryDatabase(false)
|
||||
}
|
||||
|
||||
Debug("Concurrency", gb.Concurrency)
|
||||
// the waitgroup will allow us to wait for all the goroutines to finish at the end
|
||||
var wg = new(sync.WaitGroup)
|
||||
for i := 0; i < gb.Concurrency; i++ {
|
||||
wg.Add(1)
|
||||
go gb.worker(i, wg, toScan, db)
|
||||
}
|
||||
|
||||
// TODO: Handle cleaning after? Cleanup implemented in GetPackageSet().Clean()
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
// Ensure that only file with suffix .ebuild are elaborated.
|
||||
// and ignore .swp files or files with string ebuild on name
|
||||
if strings.HasSuffix(info.Name(), ".ebuild") {
|
||||
toScan <- path
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
close(toScan)
|
||||
wg.Wait()
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
@@ -1,177 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 gentoo_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
. "github.com/mudler/luet/pkg/tree/builder/gentoo"
|
||||
)
|
||||
|
||||
type FakeParser struct {
|
||||
}
|
||||
|
||||
func (f *FakeParser) ScanEbuild(path string) (pkg.Packages, error) {
|
||||
return pkg.Packages{&pkg.DefaultPackage{Name: path}}, nil
|
||||
}
|
||||
|
||||
var _ = Describe("GentooBuilder", func() {
|
||||
|
||||
Context("Simple test", func() {
|
||||
for _, dbType := range []MemoryDB{InMemory, BoltDB} {
|
||||
It("parses correctly deps", func() {
|
||||
gb := NewGentooBuilder(&FakeParser{}, 20, dbType)
|
||||
tree, err := gb.Generate("../../../../tests/fixtures/overlay")
|
||||
defer func() {
|
||||
Expect(tree.Clean()).ToNot(HaveOccurred())
|
||||
}()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(tree.GetPackages())).To(Equal(10))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Context("Parse ebuild1", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/overlay/app-crypt/pinentry-gnome/pinentry-gnome-1.0.0-r2.ebuild")
|
||||
It("parses correctly deps", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("GPL-2"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("GNOME 3 frontend for pinentry"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild2", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/mod_dav_svn-1.12.2.ebuild")
|
||||
|
||||
It("Parsing ebuild2", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("Subversion"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Subversion WebDAV support"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild3", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/linux-sources-1.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild3", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(0))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal(""))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Virtual for Linux kernel sources"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild4", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/sabayon-mce-1.1-r5.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild4", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(2))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("GPL-2"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Sabayon Linux Media Center Infrastructure"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild5", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/libreoffice-l10n-meta-6.2.8.2.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild5", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(146))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("LGPL-2"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("LibreOffice.org localisation meta-package"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild6", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/pkgs-checker-0.2.0.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild6", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(0))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("GPL-3"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Sabayon Packages Checker"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild7", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/calamares-sabayon-base-modules-1.15.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild7", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(2))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("CC-BY-SA-4.0"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Sabayon Official Calamares base modules"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild8", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/subversion-1.12.0.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild8", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(25))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("Subversion GPL-2"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Advanced version control system"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild9", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/kodi-raspberrypi-16.0.ebuild")
|
||||
|
||||
PIt("Check parsing of the ebuild9", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(66))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("GPL-2"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("Kodi is a free and open source media-player and entertainment hub"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse ebuild10", func() {
|
||||
parser := &SimpleEbuildParser{}
|
||||
pkgs, err := parser.ScanEbuild("../../../../tests/fixtures/parser/tango-icon-theme-0.8.90-r1.ebuild")
|
||||
|
||||
It("Check parsing of the ebuild10", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fmt.Println("PKG ", pkgs[0])
|
||||
Expect(len(pkgs[0].GetRequires())).To(Equal(2))
|
||||
Expect(pkgs[0].GetLicense()).To(Equal("public-domain"))
|
||||
Expect(pkgs[0].GetDescription()).To(Equal("SVG and PNG icon theme from the Tango project"))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
@@ -1,447 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 gentoo
|
||||
|
||||
// NOTE: Look here as an example of the builder definition executor
|
||||
// https://gist.github.com/adnaan/6ca68c7985c6f851def3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
|
||||
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
const (
|
||||
uriRegex = "(.*[.]tar[.].*|.*[.]zip|.*[.]run|.*[.]png|.*[.]rpm|.*[.]gz)"
|
||||
)
|
||||
|
||||
// SimpleEbuildParser ignores USE flags and generates just 1-1 package
|
||||
type SimpleEbuildParser struct {
|
||||
World pkg.PackageDatabase
|
||||
}
|
||||
|
||||
type GentooDependency struct {
|
||||
Use string
|
||||
UseCondition _gentoo.PackageCond
|
||||
SubDeps []*GentooDependency
|
||||
Dep *_gentoo.GentooPackage
|
||||
}
|
||||
|
||||
type GentooRDEPEND struct {
|
||||
Dependencies []*GentooDependency
|
||||
}
|
||||
|
||||
func NewGentooDependency(pkg, use string) (*GentooDependency, error) {
|
||||
var err error
|
||||
ans := &GentooDependency{
|
||||
Use: use,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
}
|
||||
|
||||
if strings.HasPrefix(use, "!") {
|
||||
ans.Use = ans.Use[1:]
|
||||
ans.UseCondition = _gentoo.PkgCondNot
|
||||
}
|
||||
|
||||
if pkg != "" {
|
||||
ans.Dep, err = _gentoo.ParsePackageStr(pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Fix this on parsing phase for handle correctly ${PV}
|
||||
if strings.HasSuffix(ans.Dep.Name, "-") {
|
||||
ans.Dep.Name = ans.Dep.Name[:len(ans.Dep.Name)-1]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (d *GentooDependency) String() string {
|
||||
if d.Dep != nil {
|
||||
return fmt.Sprintf("%s", d.Dep)
|
||||
} else {
|
||||
return fmt.Sprintf("%s %d %s", d.Use, d.UseCondition, d.SubDeps)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *GentooDependency) GetDepsList() []*GentooDependency {
|
||||
ans := make([]*GentooDependency, 0)
|
||||
|
||||
if len(d.SubDeps) > 0 {
|
||||
for _, d2 := range d.SubDeps {
|
||||
list := d2.GetDepsList()
|
||||
ans = append(ans, list...)
|
||||
}
|
||||
}
|
||||
|
||||
if d.Dep != nil {
|
||||
ans = append(ans, d)
|
||||
}
|
||||
|
||||
return ans
|
||||
}
|
||||
|
||||
func (d *GentooDependency) AddSubDependency(pkg, use string) (*GentooDependency, error) {
|
||||
ans, err := NewGentooDependency(pkg, use)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.SubDeps = append(d.SubDeps, ans)
|
||||
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (r *GentooRDEPEND) GetDependencies() []*GentooDependency {
|
||||
ans := make([]*GentooDependency, 0)
|
||||
|
||||
for _, d := range r.Dependencies {
|
||||
list := d.GetDepsList()
|
||||
ans = append(ans, list...)
|
||||
}
|
||||
|
||||
// the same dependency could be available in multiple use flags.
|
||||
// It's needed avoid duplicate.
|
||||
m := make(map[string]*GentooDependency, 0)
|
||||
|
||||
for _, p := range ans {
|
||||
m[p.String()] = p
|
||||
}
|
||||
|
||||
ans = make([]*GentooDependency, 0)
|
||||
for _, p := range m {
|
||||
ans = append(ans, p)
|
||||
}
|
||||
|
||||
return ans
|
||||
}
|
||||
|
||||
func ParseRDEPEND(rdepend string) (*GentooRDEPEND, error) {
|
||||
var lastdep []*GentooDependency = make([]*GentooDependency, 0)
|
||||
var pendingDep = false
|
||||
var orDep = false
|
||||
var dep *GentooDependency
|
||||
var err error
|
||||
|
||||
ans := &GentooRDEPEND{
|
||||
Dependencies: make([]*GentooDependency, 0),
|
||||
}
|
||||
|
||||
if rdepend != "" {
|
||||
rdepends := strings.Split(rdepend, "\n")
|
||||
for _, rr := range rdepends {
|
||||
rr = strings.TrimSpace(rr)
|
||||
if rr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(rr, "|| (") {
|
||||
orDep = true
|
||||
continue
|
||||
}
|
||||
|
||||
if orDep {
|
||||
rr = strings.TrimSpace(rr)
|
||||
if rr == ")" {
|
||||
orDep = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Index(rr, "?") > 0 {
|
||||
// use flag present
|
||||
|
||||
if pendingDep {
|
||||
dep, err = lastdep[len(lastdep)-1].AddSubDependency("", rr[:strings.Index(rr, "?")])
|
||||
if err != nil {
|
||||
Debug("Ignoring subdependency ", rr[:strings.Index(rr, "?")])
|
||||
}
|
||||
} else {
|
||||
dep, err = NewGentooDependency("", rr[:strings.Index(rr, "?")])
|
||||
if err != nil {
|
||||
Debug("Ignoring dep", rr)
|
||||
} else {
|
||||
ans.Dependencies = append(ans.Dependencies, dep)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Index(rr, ")") < 0 {
|
||||
pendingDep = true
|
||||
lastdep = append(lastdep, dep)
|
||||
}
|
||||
|
||||
if strings.Index(rr, "|| (") >= 0 {
|
||||
// Ignore dep in or
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Split(rr[strings.Index(rr, "?")+1:], " ")
|
||||
for _, f := range fields {
|
||||
f = strings.TrimSpace(f)
|
||||
if f == ")" || f == "(" || f == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = dep.AddSubDependency(f, "")
|
||||
if err != nil {
|
||||
Debug("Ignoring subdependency ", f)
|
||||
}
|
||||
}
|
||||
|
||||
} else if pendingDep {
|
||||
fields := strings.Split(rr, " ")
|
||||
for _, f := range fields {
|
||||
f = strings.TrimSpace(f)
|
||||
if f == ")" || f == "(" || f == "" {
|
||||
continue
|
||||
}
|
||||
_, err = lastdep[len(lastdep)-1].AddSubDependency(f, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Index(rr, ")") >= 0 {
|
||||
lastdep = lastdep[:len(lastdep)-1]
|
||||
if len(lastdep) == 0 {
|
||||
pendingDep = false
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
rr = strings.TrimSpace(rr)
|
||||
// Check if there multiple deps in single row
|
||||
|
||||
fields := strings.Split(rr, " ")
|
||||
if len(fields) > 1 {
|
||||
for _, rrr := range fields {
|
||||
rrr = strings.TrimSpace(rrr)
|
||||
if rrr == "" {
|
||||
continue
|
||||
}
|
||||
dep, err := NewGentooDependency(rrr, "")
|
||||
if err != nil {
|
||||
Debug("Ignoring dep", rr)
|
||||
} else {
|
||||
ans.Dependencies = append(ans.Dependencies, dep)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dep, err := NewGentooDependency(rr, "")
|
||||
if err != nil {
|
||||
Debug("Ignoring dep", rr)
|
||||
} else {
|
||||
ans.Dependencies = append(ans.Dependencies, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func SourceFile(ctx context.Context, path string, pkg *_gentoo.GentooPackage) (map[string]expand.Variable, error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open: %v", err)
|
||||
}
|
||||
scontent := string(content)
|
||||
|
||||
// Add default Genoo Variables
|
||||
ebuild := fmt.Sprintf("P=%s\n", pkg.GetP()) +
|
||||
fmt.Sprintf("PN=%s\n", pkg.GetPN()) +
|
||||
fmt.Sprintf("PV=%s\n", pkg.GetPV()) +
|
||||
fmt.Sprintf("PVR=%s\n", pkg.GetPVR())
|
||||
|
||||
// Disable inherit
|
||||
scontent = strings.ReplaceAll(scontent, "inherit", "#inherit")
|
||||
// Disable function from eclass (TODO: check how fix better this)
|
||||
scontent = strings.ReplaceAll(scontent, "need_apache", "#need_apache")
|
||||
scontent = strings.ReplaceAll(scontent, "want_apache", "#want_apache")
|
||||
|
||||
regexFuncs := regexp.MustCompile(
|
||||
"[a-zA-Z]+.*[_][a-z]+[(][)][\\s]{",
|
||||
)
|
||||
matches := regexFuncs.FindAllIndex([]byte(scontent), -1)
|
||||
// Drop section after functions (src_*, *() {)
|
||||
if len(matches) > 0 {
|
||||
ebuild = ebuild + scontent[:matches[0][0]]
|
||||
} else {
|
||||
ebuild = ebuild + scontent
|
||||
}
|
||||
|
||||
// [[ ${PV} == "9999" ]] is not supported. Workaround but we need a better solution.
|
||||
regexDoubleBrakets := regexp.MustCompile(
|
||||
//"[[][[].*",
|
||||
"^[[][[].*",
|
||||
//"^.*\[\[.*\]\]",
|
||||
)
|
||||
matchDB := regexDoubleBrakets.FindAllIndex([]byte(ebuild), -1)
|
||||
if len(matchDB) > 0 {
|
||||
ebuild = ebuild[:matchDB[0][0]] + "#" + ebuild[matchDB[0][0]:]
|
||||
}
|
||||
|
||||
//fmt.Println("EBUILD ", ebuild)
|
||||
|
||||
file, err := syntax.NewParser().Parse(strings.NewReader(ebuild), path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse: %v", err)
|
||||
}
|
||||
return shell.SourceNode(ctx, file)
|
||||
}
|
||||
|
||||
// ScanEbuild returns a list of packages (always one with SimpleEbuildParser) decoded from an ebuild.
|
||||
func (ep *SimpleEbuildParser) ScanEbuild(path string) (pkg.Packages, error) {
|
||||
Debug("Starting parsing of ebuild", path)
|
||||
|
||||
pkgstr := filepath.Base(path)
|
||||
paths := strings.Split(filepath.Dir(path), "/")
|
||||
pkgstr = paths[len(paths)-2] + "/" + strings.Replace(pkgstr, ".ebuild", "", -1)
|
||||
|
||||
gp, err := _gentoo.ParsePackageStr(pkgstr)
|
||||
if err != nil {
|
||||
return pkg.Packages{}, errors.New("Error on parsing package string")
|
||||
}
|
||||
|
||||
pack := &pkg.DefaultPackage{
|
||||
Name: gp.Name,
|
||||
Version: fmt.Sprintf("%s%s", gp.Version, gp.VersionSuffix),
|
||||
Category: gp.Category,
|
||||
Uri: make([]string, 0),
|
||||
}
|
||||
|
||||
Debug("Prepare package ", pack.Category+"/"+pack.Name+"-"+pack.Version)
|
||||
|
||||
// Adding a timeout of 60secs, as with some bash files it can hang indefinetly
|
||||
timeout, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
vars, err := SourceFile(timeout, path, gp)
|
||||
if err != nil {
|
||||
Error("Error on source file ", pack.Name, ": ", err)
|
||||
return pkg.Packages{}, err
|
||||
}
|
||||
|
||||
// Retrieve slot
|
||||
slot, ok := vars["SLOT"]
|
||||
if ok && slot.String() != "0" {
|
||||
pack.SetCategory(fmt.Sprintf("%s-%s", gp.Category, slot.String()))
|
||||
}
|
||||
|
||||
// TODO: Handle this a bit better
|
||||
iuse, ok := vars["IUSE"]
|
||||
if ok {
|
||||
uses := strings.Split(strings.TrimSpace(iuse.String()), " ")
|
||||
for _, u := range uses {
|
||||
pack.AddUse(u)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve package description
|
||||
descr, ok := vars["DESCRIPTION"]
|
||||
if ok {
|
||||
pack.SetDescription(descr.String())
|
||||
}
|
||||
// Retrieve package license
|
||||
license, ok := vars["LICENSE"]
|
||||
if ok {
|
||||
pack.SetLicense(license.String())
|
||||
}
|
||||
uri, ok := vars["SRC_URI"]
|
||||
if ok {
|
||||
// TODO: handle mirror:
|
||||
uris := strings.Split(uri.String(), "\n")
|
||||
for _, u := range uris {
|
||||
u = strings.TrimSpace(u)
|
||||
|
||||
if u == "" {
|
||||
continue
|
||||
}
|
||||
if match, _ := regexp.Match(uriRegex, []byte(u)); match {
|
||||
if strings.Index(u, "(") >= 0 {
|
||||
regexUri := regexp.MustCompile("(http|ftp|mirror).*[ ]")
|
||||
matches := regexUri.FindAllIndex([]byte(u), -1)
|
||||
if len(matches) > 0 {
|
||||
u = u[matches[0][0]:matches[0][1]]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pack.AddURI(u)
|
||||
Debug("Add uri ", u)
|
||||
} else {
|
||||
Debug("Skip uri ", u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rdepend, ok := vars["RDEPEND"]
|
||||
if ok {
|
||||
gRDEPEND, err := ParseRDEPEND(rdepend.String())
|
||||
if err != nil {
|
||||
Warning("Error on parsing RDEPEND for package ", pack.Category+"/"+pack.Name, err)
|
||||
return pkg.Packages{pack}, nil
|
||||
// return pkg.Packages{}, err
|
||||
}
|
||||
|
||||
pack.PackageConflicts = []*pkg.DefaultPackage{}
|
||||
pack.PackageRequires = []*pkg.DefaultPackage{}
|
||||
|
||||
// TODO: See how handle use flags enabled.
|
||||
// and if it's correct get list of deps directly.
|
||||
for _, d := range gRDEPEND.GetDependencies() {
|
||||
|
||||
//TODO: Resolve to db or create a new one.
|
||||
//TODO: handle SLOT too.
|
||||
dep := &pkg.DefaultPackage{
|
||||
Name: d.Dep.Name,
|
||||
Version: d.Dep.Version + d.Dep.VersionSuffix,
|
||||
Category: d.Dep.Category,
|
||||
}
|
||||
Debug(fmt.Sprintf("For package %s found dep: %s/%s %s",
|
||||
gp, dep.Category, dep.Name, dep.Version))
|
||||
if d.Dep.Condition == _gentoo.PkgCondNot {
|
||||
pack.PackageConflicts = append(pack.PackageConflicts, dep)
|
||||
} else {
|
||||
pack.PackageRequires = append(pack.PackageRequires, dep)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Debug("Finished processing ebuild", path, "deps ", len(pack.PackageRequires))
|
||||
|
||||
//TODO: Deps and conflicts
|
||||
return pkg.Packages{pack}, nil
|
||||
}
|
@@ -1,669 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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 gentoo_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
|
||||
. "github.com/mudler/luet/pkg/tree/builder/gentoo"
|
||||
)
|
||||
|
||||
var _ = Describe("GentooBuilder", func() {
|
||||
|
||||
Context("Parse RDEPEND1", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
x11-themes/sabayon-artwork-grub
|
||||
sys-boot/os-prober
|
||||
app-arch/xz-utils
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(5))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sabayon-artwork-grub",
|
||||
Category: "x11-themes",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep5", func() {
|
||||
Expect(*gr.Dependencies[4]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND2", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
x11-themes/sabayon-artwork-grub
|
||||
sys-boot/os-prober
|
||||
app-arch/xz-utils
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
mount? ( sys-fs/fuse )
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(6))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sabayon-artwork-grub",
|
||||
Category: "x11-themes",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep5", func() {
|
||||
Expect(*gr.Dependencies[4]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep6", func() {
|
||||
Expect(*gr.Dependencies[5]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "mount",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "fuse",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
Dep: nil,
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND3", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
x11-themes/sabayon-artwork-grub
|
||||
sys-boot/os-prober
|
||||
app-arch/xz-utils
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
mount? ( sys-fs/fuse =sys-apps/pmount-0.9.99_alpha-r5:= )
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(6))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sabayon-artwork-grub",
|
||||
Category: "x11-themes",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep5", func() {
|
||||
Expect(*gr.Dependencies[4]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep6", func() {
|
||||
Expect(*gr.Dependencies[5]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "mount",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "fuse",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "pmount",
|
||||
Category: "sys-apps",
|
||||
Condition: _gentoo.PkgCondEqual,
|
||||
Version: "0.9.99",
|
||||
VersionSuffix: "_alpha-r5",
|
||||
Slot: "=",
|
||||
},
|
||||
},
|
||||
},
|
||||
Dep: nil,
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND4", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
x11-themes/sabayon-artwork-grub
|
||||
sys-boot/os-prober
|
||||
app-arch/xz-utils
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
!mount? ( sys-fs/fuse =sys-apps/pmount-0.9.99_alpha-r5:= )
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(6))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sabayon-artwork-grub",
|
||||
Category: "x11-themes",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep5", func() {
|
||||
Expect(*gr.Dependencies[4]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep6", func() {
|
||||
Expect(*gr.Dependencies[5]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "mount",
|
||||
UseCondition: _gentoo.PkgCondNot,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "fuse",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "pmount",
|
||||
Category: "sys-apps",
|
||||
Condition: _gentoo.PkgCondEqual,
|
||||
Version: "0.9.99",
|
||||
VersionSuffix: "_alpha-r5",
|
||||
Slot: "=",
|
||||
},
|
||||
},
|
||||
},
|
||||
Dep: nil,
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND5", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
mount? (
|
||||
sys-fs/fuse
|
||||
=sys-apps/pmount-0.9.99_alpha-r5:=
|
||||
)
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(3))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep3", func() {
|
||||
Expect(*gr.Dependencies[2]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "mount",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "fuse",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "pmount",
|
||||
Category: "sys-apps",
|
||||
Condition: _gentoo.PkgCondEqual,
|
||||
Version: "0.9.99",
|
||||
VersionSuffix: "_alpha-r5",
|
||||
Slot: "=",
|
||||
},
|
||||
},
|
||||
},
|
||||
Dep: nil,
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND6", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
mount? (
|
||||
sys-fs/fuse
|
||||
=sys-apps/pmount-0.9.99_alpha-r5:= )
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(3))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Parse RDEPEND7", func() {
|
||||
|
||||
rdepend := `
|
||||
app-crypt/sbsigntools
|
||||
>=sys-libs/ncurses-5.2-r5:0=
|
||||
mount? (
|
||||
sys-fs/fuse
|
||||
=sys-apps/pmount-0.9.99_alpha-r5:=
|
||||
ext2? (
|
||||
sys-fs/genext2fs
|
||||
)
|
||||
)
|
||||
`
|
||||
gr, err := ParseRDEPEND(rdepend)
|
||||
It("Check error", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Check gr", func() {
|
||||
Expect(gr).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Check deps #", func() {
|
||||
Expect(len(gr.Dependencies)).Should(Equal(3))
|
||||
})
|
||||
|
||||
It("Check dep1", func() {
|
||||
Expect(*gr.Dependencies[0]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "sbsigntools",
|
||||
Category: "app-crypt",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep2", func() {
|
||||
Expect(*gr.Dependencies[1]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "ncurses",
|
||||
Category: "sys-libs",
|
||||
Slot: "0=",
|
||||
Version: "5.2",
|
||||
VersionSuffix: "-r5",
|
||||
Condition: _gentoo.PkgCondGreaterEqual,
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
It("Check dep3", func() {
|
||||
Expect(*gr.Dependencies[2]).Should(Equal(
|
||||
GentooDependency{
|
||||
Use: "mount",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "fuse",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "pmount",
|
||||
Category: "sys-apps",
|
||||
Condition: _gentoo.PkgCondEqual,
|
||||
Version: "0.9.99",
|
||||
VersionSuffix: "_alpha-r5",
|
||||
Slot: "=",
|
||||
},
|
||||
},
|
||||
&GentooDependency{
|
||||
Use: "ext2",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: []*GentooDependency{
|
||||
&GentooDependency{
|
||||
Use: "",
|
||||
UseCondition: _gentoo.PkgCondInvalid,
|
||||
SubDeps: make([]*GentooDependency, 0),
|
||||
Dep: &_gentoo.GentooPackage{
|
||||
Name: "genext2fs",
|
||||
Category: "sys-fs",
|
||||
Slot: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
Dep: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Context("Simple test", func() {
|
||||
for _, dbType := range []MemoryDB{InMemory, BoltDB} {
|
||||
It("parses correctly deps", func() {
|
||||
gb := NewGentooBuilder(&SimpleEbuildParser{}, 20, dbType)
|
||||
tree, err := gb.Generate("../../../../tests/fixtures/overlay")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer func() {
|
||||
Expect(tree.Clean()).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
Expect(len(tree.GetPackages())).To(Equal(10))
|
||||
|
||||
for _, p := range tree.World() {
|
||||
|
||||
Expect(p.GetName()).To(ContainSubstring("pinentry"))
|
||||
Expect(p.GetVersion()).To(ContainSubstring("1."))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
})
|
@@ -74,39 +74,92 @@ func (r *CompilerRecipe) Load(path string) error {
|
||||
return errors.Wrap(err, "Error on walk path "+currentpath)
|
||||
}
|
||||
|
||||
if info.Name() != DefinitionFile {
|
||||
if info.Name() != DefinitionFile && info.Name() != CollectionFile {
|
||||
return nil // Skip with no errors
|
||||
}
|
||||
|
||||
pack, err := ReadDefinitionFile(currentpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
switch info.Name() {
|
||||
case DefinitionFile:
|
||||
|
||||
// Instead of rdeps, have a different tree for build deps.
|
||||
compileDefPath := pack.Rel(CompilerDefinitionFile)
|
||||
if helpers.Exists(compileDefPath) {
|
||||
dat, err := ioutil.ReadFile(compileDefPath)
|
||||
pack, err := ReadDefinitionFile(currentpath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error reading file "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
return err
|
||||
}
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
|
||||
// Instead of rdeps, have a different tree for build deps.
|
||||
compileDefPath := pack.Rel(CompilerDefinitionFile)
|
||||
if helpers.Exists(compileDefPath) {
|
||||
|
||||
dat, err := helpers.RenderFiles(compileDefPath, currentpath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error templating file "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
}
|
||||
|
||||
packbuild, err := pkg.DefaultPackageFromYaml([]byte(dat))
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error reading yaml "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
}
|
||||
pack.Requires(packbuild.GetRequires())
|
||||
pack.Conflicts(packbuild.GetConflicts())
|
||||
}
|
||||
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
}
|
||||
|
||||
case CollectionFile:
|
||||
dat, err := ioutil.ReadFile(currentpath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading file "+currentpath)
|
||||
}
|
||||
packs, err := pkg.DefaultPackagesFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
packsRaw, err := pkg.GetRawPackages(dat)
|
||||
|
||||
for _, pack := range packs {
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
|
||||
// Instead of rdeps, have a different tree for build deps.
|
||||
compileDefPath := pack.Rel(CompilerDefinitionFile)
|
||||
if helpers.Exists(compileDefPath) {
|
||||
|
||||
raw := packsRaw.Find(pack.GetName(), pack.GetCategory(), pack.GetVersion())
|
||||
buildyaml, err := ioutil.ReadFile(compileDefPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading file "+currentpath)
|
||||
}
|
||||
dat, err := helpers.RenderHelm(string(buildyaml), raw)
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error templating file "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
}
|
||||
|
||||
packbuild, err := pkg.DefaultPackageFromYaml([]byte(dat))
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error reading yaml "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
}
|
||||
pack.Requires(packbuild.GetRequires())
|
||||
pack.Conflicts(packbuild.GetConflicts())
|
||||
}
|
||||
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
}
|
||||
}
|
||||
packbuild, err := pkg.DefaultPackageFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err,
|
||||
"Error reading yaml "+CompilerDefinitionFile+" from "+
|
||||
filepath.Dir(currentpath))
|
||||
}
|
||||
pack.Requires(packbuild.GetRequires())
|
||||
pack.Conflicts(packbuild.GetConflicts())
|
||||
}
|
||||
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -85,7 +85,7 @@ func (r *InstallerRecipe) Load(path string) error {
|
||||
// the function that handles each file or dir
|
||||
var ff = func(currentpath string, info os.FileInfo, err error) error {
|
||||
|
||||
if info.Name() != DefinitionFile {
|
||||
if info.Name() != DefinitionFile && info.Name() != CollectionFile {
|
||||
return nil // Skip with no errors
|
||||
}
|
||||
|
||||
@@ -93,16 +93,35 @@ func (r *InstallerRecipe) Load(path string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading file "+currentpath)
|
||||
}
|
||||
pack, err := pkg.DefaultPackageFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
switch info.Name() {
|
||||
case DefinitionFile:
|
||||
pack, err := pkg.DefaultPackageFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
}
|
||||
|
||||
case CollectionFile:
|
||||
packs, err := pkg.DefaultPackagesFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
for _, p := range packs {
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
p.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+p.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -34,6 +34,7 @@ import (
|
||||
|
||||
const (
|
||||
DefinitionFile = "definition.yaml"
|
||||
CollectionFile = "collection.yaml"
|
||||
)
|
||||
|
||||
func NewGeneralRecipe(db pkg.PackageDatabase) Builder { return &Recipe{Database: db} }
|
||||
@@ -94,7 +95,7 @@ func (r *Recipe) Load(path string) error {
|
||||
// the function that handles each file or dir
|
||||
var ff = func(currentpath string, info os.FileInfo, err error) error {
|
||||
|
||||
if info.Name() != DefinitionFile {
|
||||
if info.Name() != DefinitionFile && info.Name() != CollectionFile {
|
||||
return nil // Skip with no errors
|
||||
}
|
||||
|
||||
@@ -102,16 +103,34 @@ func (r *Recipe) Load(path string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading file "+currentpath)
|
||||
}
|
||||
pack, err := pkg.DefaultPackageFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
switch info.Name() {
|
||||
case DefinitionFile:
|
||||
pack, err := pkg.DefaultPackageFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
pack.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&pack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+pack.GetName())
|
||||
}
|
||||
case CollectionFile:
|
||||
packs, err := pkg.DefaultPackagesFromYaml(dat)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error reading yaml "+currentpath)
|
||||
}
|
||||
for _, p := range packs {
|
||||
// Path is set only internally when tree is loaded from disk
|
||||
p.SetPath(filepath.Dir(currentpath))
|
||||
_, err = r.Database.CreatePackage(&p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating package "+p.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -1,136 +0,0 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
//
|
||||
// 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/>.
|
||||
|
||||
// Recipe is a builder imeplementation.
|
||||
|
||||
// It reads a Tree and spit it in human readable form (YAML), called recipe,
|
||||
// It also loads a tree (recipe) from a YAML (to a db, e.g. BoltDB), allowing to query it
|
||||
// with the solver, using the package object.
|
||||
package tree_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"github.com/mudler/luet/pkg/solver"
|
||||
gentoo "github.com/mudler/luet/pkg/tree/builder/gentoo"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
. "github.com/mudler/luet/pkg/tree"
|
||||
)
|
||||
|
||||
type FakeParser struct {
|
||||
}
|
||||
|
||||
var _ = Describe("Recipe", func() {
|
||||
for _, dbType := range []gentoo.MemoryDB{gentoo.InMemory, gentoo.BoltDB} {
|
||||
Context("Tree generation and storing", func() {
|
||||
It("parses and writes a tree", func() {
|
||||
tmpdir, err := ioutil.TempDir("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{}, 20, dbType)
|
||||
tree, err := gb.Generate("../../tests/fixtures/overlay")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer func() {
|
||||
Expect(tree.Clean()).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
Expect(len(tree.GetPackages())).To(Equal(10))
|
||||
|
||||
generalRecipe := NewGeneralRecipe(tree)
|
||||
err = generalRecipe.Save(tmpdir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Reloading trees", func() {
|
||||
It("writes and reads back the same tree", func() {
|
||||
tmpdir, err := ioutil.TempDir("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{}, 20, dbType)
|
||||
tree, err := gb.Generate("../../tests/fixtures/overlay")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer func() {
|
||||
Expect(tree.Clean()).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
Expect(len(tree.GetPackages())).To(Equal(10))
|
||||
|
||||
generalRecipe := NewGeneralRecipe(tree)
|
||||
err = generalRecipe.Save(tmpdir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
db := pkg.NewInMemoryDatabase(false)
|
||||
generalRecipe = NewGeneralRecipe(db)
|
||||
|
||||
generalRecipe.WithDatabase(nil)
|
||||
Expect(generalRecipe.GetDatabase()).To(BeNil())
|
||||
|
||||
err = generalRecipe.Load(tmpdir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(10))
|
||||
|
||||
for _, p := range tree.World() {
|
||||
Expect(p.GetName()).To(ContainSubstring("pinentry"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("Simple solving with the fixture tree", func() {
|
||||
It("writes and reads back the same tree", func() {
|
||||
tmpdir, err := ioutil.TempDir("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{}, 20, dbType)
|
||||
tree, err := gb.Generate("../../tests/fixtures/overlay")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer func() {
|
||||
Expect(tree.Clean()).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
Expect(len(tree.GetPackages())).To(Equal(10))
|
||||
|
||||
pack, err := tree.FindPackage(&pkg.DefaultPackage{
|
||||
Name: "pinentry",
|
||||
Version: "1.0.0-r2",
|
||||
Category: "app-crypt",
|
||||
}) // Note: the definition depends on pinentry-base without an explicit version
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
s := solver.NewSolver(solver.Options{Type: solver.SingleCoreSimple}, pkg.NewInMemoryDatabase(false), tree, tree)
|
||||
solution, err := s.Install([]pkg.Package{pack})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(solution)).To(Equal(14))
|
||||
|
||||
var allSol string
|
||||
for _, sol := range solution {
|
||||
allSol = allSol + "\n" + sol.ToString()
|
||||
}
|
||||
|
||||
Expect(allSol).To(ContainSubstring("app-crypt/pinentry-base 1.0.0 installed"))
|
||||
Expect(allSol).To(ContainSubstring("app-crypt/pinentry 1.1.0-r2 not installed"))
|
||||
Expect(allSol).To(ContainSubstring("app-crypt/pinentry 1.0.0-r2 installed"))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
6
tests/fixtures/collections/build.yaml
vendored
Normal file
6
tests/fixtures/collections/build.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
image: quay.io/mocaccino/extra
|
||||
steps:
|
||||
- touch /{{.Values.name}}
|
||||
- touch /build-extra-{{.Values.foo}}
|
||||
|
||||
unpack: true
|
13
tests/fixtures/collections/collection.yaml
vendored
Normal file
13
tests/fixtures/collections/collection.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
packages:
|
||||
- name: "a"
|
||||
category: "distro"
|
||||
version: "0.1"
|
||||
foo: "baz"
|
||||
- name: "b"
|
||||
category: "distro"
|
||||
version: "0.3"
|
||||
foo: "f"
|
||||
- name: "c"
|
||||
category: "distro"
|
||||
version: "0.3"
|
||||
foo: "bar"
|
2
tests/fixtures/collections/finalize.yaml
vendored
Normal file
2
tests/fixtures/collections/finalize.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
install:
|
||||
- touch /finalize-{{.Values.name}}
|
14
tests/fixtures/excludeimage/build.yaml
vendored
Normal file
14
tests/fixtures/excludeimage/build.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
requires:
|
||||
- category: "layer"
|
||||
name: "seed"
|
||||
version: "1.0"
|
||||
prelude:
|
||||
- echo foo > /test
|
||||
- echo bar > /test2
|
||||
steps:
|
||||
- echo artifact5 > /test5
|
||||
- echo artifact6 > /test6
|
||||
- echo artifact43 > /marvin
|
||||
unpack: true
|
||||
excludes:
|
||||
- marvin
|
3
tests/fixtures/excludeimage/definition.yaml
vendored
Normal file
3
tests/fixtures/excludeimage/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "test"
|
||||
name: "b"
|
||||
version: "1.0"
|
2
tests/fixtures/excludeimage/seed/build.yaml
vendored
Normal file
2
tests/fixtures/excludeimage/seed/build.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
image: alpine
|
||||
unpack: true
|
3
tests/fixtures/excludeimage/seed/definition.yaml
vendored
Normal file
3
tests/fixtures/excludeimage/seed/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "layer"
|
||||
name: "seed"
|
||||
version: "1.0"
|
17
tests/fixtures/excludeincludeimage/build.yaml
vendored
Normal file
17
tests/fixtures/excludeincludeimage/build.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
requires:
|
||||
- category: "layer"
|
||||
name: "seed"
|
||||
version: "1.0"
|
||||
prelude:
|
||||
- echo foo > /test
|
||||
- echo bar > /test2
|
||||
steps:
|
||||
- echo artifact5 > /test5
|
||||
- echo artifact6 > /test6
|
||||
- echo artifact43 > /marvin
|
||||
unpack: true
|
||||
excludes:
|
||||
- marvin
|
||||
includes:
|
||||
- test.*
|
||||
- mar.*
|
3
tests/fixtures/excludeincludeimage/definition.yaml
vendored
Normal file
3
tests/fixtures/excludeincludeimage/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "test"
|
||||
name: "b"
|
||||
version: "1.0"
|
2
tests/fixtures/excludeincludeimage/seed/build.yaml
vendored
Normal file
2
tests/fixtures/excludeincludeimage/seed/build.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
image: alpine
|
||||
unpack: true
|
3
tests/fixtures/excludeincludeimage/seed/definition.yaml
vendored
Normal file
3
tests/fixtures/excludeincludeimage/seed/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "layer"
|
||||
name: "seed"
|
||||
version: "1.0"
|
11
tests/fixtures/excludes/build.yaml
vendored
Normal file
11
tests/fixtures/excludes/build.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
image: "alpine"
|
||||
prelude:
|
||||
- echo foo > /test
|
||||
- echo bar > /test2
|
||||
steps:
|
||||
- echo artifact5 > /test5
|
||||
- echo artifact6 > /test6
|
||||
- echo artifact43 > /marvin
|
||||
- echo "foo" > /marvot
|
||||
excludes:
|
||||
- marvot
|
3
tests/fixtures/excludes/definition.yaml
vendored
Normal file
3
tests/fixtures/excludes/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "test"
|
||||
name: "b"
|
||||
version: "1.0"
|
14
tests/fixtures/excludesincludes/build.yaml
vendored
Normal file
14
tests/fixtures/excludesincludes/build.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
image: "alpine"
|
||||
prelude:
|
||||
- echo foo > /test
|
||||
- echo bar > /test2
|
||||
steps:
|
||||
- echo artifact5 > /test5
|
||||
- echo artifact6 > /test6
|
||||
- echo artifact43 > /marvin
|
||||
- echo "foo" > /marvot
|
||||
excludes:
|
||||
- marvot
|
||||
includes:
|
||||
- /test5
|
||||
- mar.*
|
3
tests/fixtures/excludesincludes/definition.yaml
vendored
Normal file
3
tests/fixtures/excludesincludes/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "test"
|
||||
name: "b"
|
||||
version: "1.0"
|
3
tests/fixtures/plugin/test-foo
vendored
Executable file
3
tests/fixtures/plugin/test-foo
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
echo "$1" >> $EVENT_FILE
|
||||
echo "$2" >> $PAYLOAD_FILE
|
2
tests/fixtures/templatedfinalizers/alpine/build.yaml
vendored
Normal file
2
tests/fixtures/templatedfinalizers/alpine/build.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
image: "alpine"
|
||||
unpack: true
|
3
tests/fixtures/templatedfinalizers/alpine/definition.yaml
vendored
Normal file
3
tests/fixtures/templatedfinalizers/alpine/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
category: "seed"
|
||||
name: "alpine"
|
||||
version: "1.0"
|
2
tests/fixtures/templatedfinalizers/alpine/finalize.yaml
vendored
Normal file
2
tests/fixtures/templatedfinalizers/alpine/finalize.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
install:
|
||||
- echo "{{.Values.name}}" > /tmp/foo
|
@@ -43,6 +43,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -48,6 +48,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -51,6 +51,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -28,6 +28,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -43,6 +43,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -43,6 +43,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -71,6 +71,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -70,6 +70,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -42,6 +42,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -42,6 +42,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -50,6 +50,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -42,6 +42,7 @@ system:
|
||||
rootfs: /
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -44,6 +44,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -71,6 +71,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -11,18 +11,19 @@ oneTimeTearDown() {
|
||||
}
|
||||
|
||||
testBuild() {
|
||||
mkdir $tmpdir/testbuild
|
||||
luet build --tree "$ROOT_DIR/tests/fixtures/config_protect" --destination $tmpdir/testbuild --compression gzip test/a
|
||||
mkdir $tmpdir/testrootfs/testbuild -p
|
||||
luet build --tree "$ROOT_DIR/tests/fixtures/config_protect" \
|
||||
--destination $tmpdir/testrootfs/testbuild --compression gzip test/a
|
||||
buildst=$?
|
||||
assertEquals 'builds successfully' "$buildst" "0"
|
||||
assertTrue 'create package' "[ -e '$tmpdir/testbuild/a-test-1.0.package.tar.gz' ]"
|
||||
assertTrue 'create package' "[ -e '$tmpdir/testrootfs/testbuild/a-test-1.0.package.tar.gz' ]"
|
||||
}
|
||||
|
||||
testRepo() {
|
||||
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
luet create-repo --tree "$ROOT_DIR/tests/fixtures/config_protect" \
|
||||
--output $tmpdir/testbuild \
|
||||
--packages $tmpdir/testbuild \
|
||||
--output $tmpdir/testrootfs/testbuild \
|
||||
--packages $tmpdir/testrootfs/testbuild \
|
||||
--name "test" \
|
||||
--descr "Test Repo" \
|
||||
--urls $tmpdir/testrootfs \
|
||||
@@ -30,15 +31,14 @@ testRepo() {
|
||||
|
||||
createst=$?
|
||||
assertEquals 'create repo successfully' "$createst" "0"
|
||||
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
assertTrue 'create repository' "[ -e '$tmpdir/testrootfs/testbuild/repository.yaml' ]"
|
||||
}
|
||||
|
||||
testConfig() {
|
||||
mkdir $tmpdir/testrootfs
|
||||
|
||||
mkdir $tmpdir/config.protect.d
|
||||
mkdir $tmpdir/testrootfs/etc/luet/config.protect.d -p
|
||||
|
||||
cat <<EOF > $tmpdir/config.protect.d/conf1.yml
|
||||
cat <<EOF > $tmpdir/testrootfs/etc/luet/config.protect.d/conf1.yml
|
||||
name: "protect1"
|
||||
dirs:
|
||||
- /etc/
|
||||
@@ -52,13 +52,14 @@ system:
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_protect_confdir:
|
||||
- $tmpdir/config.protect.d
|
||||
- /etc/luet/config.protect.d
|
||||
config_from_host: false
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
enable: true
|
||||
urls:
|
||||
- "$tmpdir/testbuild"
|
||||
- "/testbuild"
|
||||
EOF
|
||||
luet config --config $tmpdir/luet.yaml
|
||||
res=$?
|
||||
|
@@ -54,6 +54,7 @@ system:
|
||||
database_engine: "boltdb"
|
||||
config_protect_confdir:
|
||||
- $tmpdir/config.protect.d
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -61,6 +61,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -42,6 +42,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -42,6 +42,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
@@ -52,6 +52,7 @@ system:
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_protect_skip: true
|
||||
config_from_host: true
|
||||
config_protect_confdir:
|
||||
- $tmpdir/config.protect.d
|
||||
repositories:
|
||||
|
@@ -43,6 +43,7 @@ system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
|
77
tests/integration/19_finalizer_templated.sh
Executable file
77
tests/integration/19_finalizer_templated.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
export LUET_NOLOCK=true
|
||||
|
||||
oneTimeSetUp() {
|
||||
export tmpdir="$(mktemp -d)"
|
||||
}
|
||||
|
||||
oneTimeTearDown() {
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
testBuild() {
|
||||
mkdir $tmpdir/testbuild
|
||||
luet build --tree "$ROOT_DIR/tests/fixtures/templatedfinalizers" --destination $tmpdir/testbuild --compression gzip --all > /dev/null
|
||||
buildst=$?
|
||||
assertEquals 'builds successfully' "$buildst" "0"
|
||||
assertTrue 'create package' "[ -e '$tmpdir/testbuild/alpine-seed-1.0.package.tar.gz' ]"
|
||||
}
|
||||
|
||||
testRepo() {
|
||||
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
luet create-repo --tree "$ROOT_DIR/tests/fixtures/templatedfinalizers" \
|
||||
--output $tmpdir/testbuild \
|
||||
--packages $tmpdir/testbuild \
|
||||
--name "test" \
|
||||
--descr "Test Repo" \
|
||||
--urls $tmpdir/testrootfs \
|
||||
--type disk > /dev/null
|
||||
|
||||
createst=$?
|
||||
assertEquals 'create repo successfully' "$createst" "0"
|
||||
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
}
|
||||
|
||||
testConfig() {
|
||||
mkdir $tmpdir/testrootfs
|
||||
cat <<EOF > $tmpdir/luet.yaml
|
||||
general:
|
||||
debug: true
|
||||
system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
enable: true
|
||||
urls:
|
||||
- "$tmpdir/testbuild"
|
||||
EOF
|
||||
luet config --config $tmpdir/luet.yaml
|
||||
res=$?
|
||||
assertEquals 'config test successfully' "$res" "0"
|
||||
}
|
||||
|
||||
testInstall() {
|
||||
luet install --config $tmpdir/luet.yaml seed/alpine
|
||||
#luet install --config $tmpdir/luet.yaml test/c-1.0 > /dev/null
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/bin/busybox' ]"
|
||||
assertTrue 'finalizer runs' "[ -e '$tmpdir/testrootfs/tmp/foo' ]"
|
||||
assertEquals 'finalizer printed used shell' "$(cat $tmpdir/testrootfs/tmp/foo)" 'alpine'
|
||||
}
|
||||
|
||||
|
||||
testCleanup() {
|
||||
luet cleanup --config $tmpdir/luet.yaml
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
}
|
||||
|
||||
# Load shUnit2.
|
||||
. "$ROOT_DIR/tests/integration/shunit2"/shunit2
|
||||
|
83
tests/integration/20_plugin.sh
Executable file
83
tests/integration/20_plugin.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
|
||||
export LUET_NOLOCK=true
|
||||
export PATH=$PATH:$ROOT_DIR/tests/fixtures/plugin
|
||||
|
||||
|
||||
oneTimeSetUp() {
|
||||
export tmpdir="$(mktemp -d)"
|
||||
export EVENT_FILE=$tmpdir/events.txt
|
||||
export PAYLOAD_FILE=$tmpdir/payloads.txt
|
||||
}
|
||||
|
||||
oneTimeTearDown() {
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
testBuild() {
|
||||
mkdir $tmpdir/testbuild
|
||||
luet build --plugin test-foo --tree "$ROOT_DIR/tests/fixtures/templatedfinalizers" --destination $tmpdir/testbuild --compression gzip --all
|
||||
buildst=$?
|
||||
assertEquals 'builds successfully' "$buildst" "0"
|
||||
assertContains 'event file contains corresponding event' "$(cat $EVENT_FILE)" 'package.pre.build'
|
||||
assertContains 'event file contains corresponding event' "$(cat $PAYLOAD_FILE)" 'alpine'
|
||||
}
|
||||
|
||||
testRepo() {
|
||||
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
luet --plugin test-foo create-repo --tree "$ROOT_DIR/tests/fixtures/templatedfinalizers" \
|
||||
--output $tmpdir/testbuild \
|
||||
--packages $tmpdir/testbuild \
|
||||
--name "test" \
|
||||
--descr "Test Repo" \
|
||||
--urls $tmpdir/testrootfs \
|
||||
--type disk > /dev/null
|
||||
|
||||
createst=$?
|
||||
assertEquals 'create repo successfully' "$createst" "0"
|
||||
assertContains 'event file contains corresponding event' "$(cat $EVENT_FILE)" 'repository.pre.build'
|
||||
}
|
||||
|
||||
testConfig() {
|
||||
mkdir $tmpdir/testrootfs
|
||||
cat <<EOF > $tmpdir/luet.yaml
|
||||
general:
|
||||
debug: true
|
||||
system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
enable: true
|
||||
urls:
|
||||
- "$tmpdir/testbuild"
|
||||
EOF
|
||||
luet config --config $tmpdir/luet.yaml
|
||||
res=$?
|
||||
assertEquals 'config test successfully' "$res" "0"
|
||||
}
|
||||
|
||||
testInstall() {
|
||||
luet --plugin test-foo install --config $tmpdir/luet.yaml seed/alpine
|
||||
#luet install --config $tmpdir/luet.yaml test/c-1.0 > /dev/null
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/bin/busybox' ]"
|
||||
assertTrue 'finalizer runs' "[ -e '$tmpdir/testrootfs/tmp/foo' ]"
|
||||
assertEquals 'finalizer printed used shell' "$(cat $tmpdir/testrootfs/tmp/foo)" 'alpine'
|
||||
assertContains 'event file contains corresponding event' "$(cat $EVENT_FILE)" 'package.install'
|
||||
|
||||
}
|
||||
|
||||
|
||||
testCleanup() {
|
||||
luet cleanup --config $tmpdir/luet.yaml
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
}
|
||||
|
||||
# Load shUnit2.
|
||||
. "$ROOT_DIR/tests/integration/shunit2"/shunit2
|
121
tests/integration/21_collections.sh
Executable file
121
tests/integration/21_collections.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
|
||||
export LUET_NOLOCK=true
|
||||
oneTimeSetUp() {
|
||||
export tmpdir="$(mktemp -d)"
|
||||
}
|
||||
|
||||
oneTimeTearDown() {
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
testBuild() {
|
||||
mkdir $tmpdir/testbuild
|
||||
luet build --tree "$ROOT_DIR/tests/fixtures/collections" --destination $tmpdir/testbuild --compression gzip --all
|
||||
buildst=$?
|
||||
assertEquals 'builds successfully' "$buildst" "0"
|
||||
assertTrue 'create package B' "[ -e '$tmpdir/testbuild/b-distro-0.3.package.tar.gz' ]"
|
||||
assertTrue 'create package A' "[ -e '$tmpdir/testbuild/a-distro-0.1.package.tar.gz' ]"
|
||||
assertTrue 'create package C' "[ -e '$tmpdir/testbuild/c-distro-0.3.package.tar.gz' ]"
|
||||
|
||||
|
||||
}
|
||||
|
||||
testRepo() {
|
||||
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
luet create-repo --tree "$ROOT_DIR/tests/fixtures/collections" \
|
||||
--output $tmpdir/testbuild \
|
||||
--packages $tmpdir/testbuild \
|
||||
--name "test" \
|
||||
--descr "Test Repo" \
|
||||
--urls $tmpdir/testrootfs \
|
||||
--type disk
|
||||
|
||||
createst=$?
|
||||
assertEquals 'create repo successfully' "$createst" "0"
|
||||
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||
}
|
||||
|
||||
testConfig() {
|
||||
mkdir $tmpdir/testrootfs
|
||||
cat <<EOF > $tmpdir/luet.yaml
|
||||
general:
|
||||
debug: true
|
||||
system:
|
||||
rootfs: $tmpdir/testrootfs
|
||||
database_path: "/"
|
||||
database_engine: "boltdb"
|
||||
config_from_host: true
|
||||
repositories:
|
||||
- name: "main"
|
||||
type: "disk"
|
||||
enable: true
|
||||
urls:
|
||||
- "$tmpdir/testbuild"
|
||||
EOF
|
||||
luet config --config $tmpdir/luet.yaml
|
||||
res=$?
|
||||
assertEquals 'config test successfully' "$res" "0"
|
||||
}
|
||||
|
||||
testInstall() {
|
||||
luet install --config $tmpdir/luet.yaml distro/a
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
|
||||
assertTrue 'package installed A' "[ -e '$tmpdir/testrootfs/a' ]"
|
||||
# Build time can interpolate on fields which aren't package properties.
|
||||
assertTrue 'extra field on A' "[ -e '$tmpdir/testrootfs/build-extra-baz' ]"
|
||||
# Finalizers can interpolate only on package field. No extra fields are allowed at this time.
|
||||
assertTrue 'finalizer executed on A' "[ -e '$tmpdir/testrootfs/finalize-a' ]"
|
||||
|
||||
installed=$(luet --config $tmpdir/luet.yaml search --installed .)
|
||||
searchst=$?
|
||||
assertEquals 'search exists successfully' "$searchst" "0"
|
||||
|
||||
assertContains 'contains distro/a-0.1' "$installed" 'distro/a-0.1'
|
||||
|
||||
luet uninstall --config $tmpdir/luet.yaml distro/a
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
|
||||
# We do the same check for the others
|
||||
luet install --config $tmpdir/luet.yaml distro/b
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
|
||||
assertTrue 'package installed B' "[ -e '$tmpdir/testrootfs/b' ]"
|
||||
assertTrue 'extra field on B' "[ -e '$tmpdir/testrootfs/build-extra-f' ]"
|
||||
assertTrue 'finalizer executed on B' "[ -e '$tmpdir/testrootfs/finalize-b' ]"
|
||||
|
||||
installed=$(luet --config $tmpdir/luet.yaml search --installed .)
|
||||
searchst=$?
|
||||
assertEquals 'search exists successfully' "$searchst" "0"
|
||||
|
||||
assertContains 'contains distro/b-0.3' "$installed" 'distro/b-0.3'
|
||||
|
||||
luet uninstall --config $tmpdir/luet.yaml distro/b
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
|
||||
luet install --config $tmpdir/luet.yaml distro/c
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
|
||||
assertTrue 'package installed C' "[ -e '$tmpdir/testrootfs/c' ]"
|
||||
assertTrue 'extra field on C' "[ -e '$tmpdir/testrootfs/build-extra-bar' ]"
|
||||
assertTrue 'finalizer executed on C' "[ -e '$tmpdir/testrootfs/finalize-c' ]"
|
||||
|
||||
installed=$(luet --config $tmpdir/luet.yaml search --installed .)
|
||||
searchst=$?
|
||||
assertEquals 'search exists successfully' "$searchst" "0"
|
||||
|
||||
assertContains 'contains distro/c-0.3' "$installed" 'distro/c-0.3'
|
||||
|
||||
luet uninstall --config $tmpdir/luet.yaml distro/c
|
||||
installst=$?
|
||||
assertEquals 'install test successfully' "$installst" "0"
|
||||
}
|
||||
# Load shUnit2.
|
||||
. "$ROOT_DIR/tests/integration/shunit2"/shunit2
|
||||
|
3
vendor/github.com/cavaliercoder/grab/.gitignore
generated
vendored
Normal file
3
vendor/github.com/cavaliercoder/grab/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# ignore IDE project files
|
||||
*.iml
|
||||
.idea/
|
1
vendor/github.com/cavaliercoder/grab/.travis.yml
generated
vendored
1
vendor/github.com/cavaliercoder/grab/.travis.yml
generated
vendored
@@ -1,6 +1,7 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
- 1.10.x
|
||||
- 1.9.x
|
||||
- 1.8.x
|
||||
|
2
vendor/github.com/cavaliercoder/grab/README.md
generated
vendored
2
vendor/github.com/cavaliercoder/grab/README.md
generated
vendored
@@ -70,7 +70,7 @@ Loop:
|
||||
case <-t.C:
|
||||
fmt.Printf(" transferred %v / %v bytes (%.2f%%)\n",
|
||||
resp.BytesComplete(),
|
||||
resp.Size,
|
||||
resp.Size(),
|
||||
100*resp.Progress())
|
||||
|
||||
case <-resp.Done:
|
||||
|
54
vendor/github.com/cavaliercoder/grab/bps/bps.go
generated
vendored
Normal file
54
vendor/github.com/cavaliercoder/grab/bps/bps.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Package bps provides gauges for calculating the Bytes Per Second transfer rate
|
||||
of data streams.
|
||||
*/
|
||||
package bps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Gauge is the common interface for all BPS gauges in this package. Given a
|
||||
// set of samples over time, each gauge type can be used to measure the Bytes
|
||||
// Per Second transfer rate of a data stream.
|
||||
//
|
||||
// All samples must monotonically increase in timestamp and value. Each sample
|
||||
// should represent the total number of bytes sent in a stream, rather than
|
||||
// accounting for the number sent since the last sample.
|
||||
//
|
||||
// To ensure a gauge can report progress as quickly as possible, take an initial
|
||||
// sample when your stream first starts.
|
||||
//
|
||||
// All gauge implementations are safe for concurrent use.
|
||||
type Gauge interface {
|
||||
// Sample adds a new sample of the progress of the monitored stream.
|
||||
Sample(t time.Time, n int64)
|
||||
|
||||
// BPS returns the calculated Bytes Per Second rate of the monitored stream.
|
||||
BPS() float64
|
||||
}
|
||||
|
||||
// SampleFunc is used by Watch to take periodic samples of a monitored stream.
|
||||
type SampleFunc func() (n int64)
|
||||
|
||||
// Watch will periodically call the given SampleFunc to sample the progress of
|
||||
// a monitored stream and update the given gauge. SampleFunc should return the
|
||||
// total number of bytes transferred by the stream since it started.
|
||||
//
|
||||
// Watch is a blocking call and should typically be called in a new goroutine.
|
||||
// To prevent the goroutine from leaking, make sure to cancel the given context
|
||||
// once the stream is completed or canceled.
|
||||
func Watch(ctx context.Context, g Gauge, f SampleFunc, interval time.Duration) {
|
||||
g.Sample(time.Now(), f())
|
||||
t := time.NewTicker(interval)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case now := <-t.C:
|
||||
g.Sample(now, f())
|
||||
}
|
||||
}
|
||||
}
|
81
vendor/github.com/cavaliercoder/grab/bps/sma.go
generated
vendored
Normal file
81
vendor/github.com/cavaliercoder/grab/bps/sma.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package bps
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewSMA returns a gauge that uses a Simple Moving Average with the given
|
||||
// number of samples to measure the bytes per second of a byte stream.
|
||||
//
|
||||
// BPS is computed using the timestamp of the most recent and oldest sample in
|
||||
// the sample buffer. When a new sample is added, the oldest sample is dropped
|
||||
// if the sample count exceeds maxSamples.
|
||||
//
|
||||
// The gauge does not account for any latency in arrival time of new samples or
|
||||
// the desired window size. Any variance in the arrival of samples will result
|
||||
// in a BPS measurement that is correct for the submitted samples, but over a
|
||||
// varying time window.
|
||||
//
|
||||
// maxSamples should be equal to 1 + (window size / sampling interval) where
|
||||
// window size is the number of seconds over which the moving average is
|
||||
// smoothed and sampling interval is the number of seconds between each sample.
|
||||
//
|
||||
// For example, if you want a five second window, sampling once per second,
|
||||
// maxSamples should be 1 + 5/1 = 6.
|
||||
func NewSMA(maxSamples int) Gauge {
|
||||
if maxSamples < 2 {
|
||||
panic("sample count must be greater than 1")
|
||||
}
|
||||
return &sma{
|
||||
maxSamples: uint64(maxSamples),
|
||||
samples: make([]int64, maxSamples),
|
||||
timestamps: make([]time.Time, maxSamples),
|
||||
}
|
||||
}
|
||||
|
||||
type sma struct {
|
||||
mu sync.Mutex
|
||||
index uint64
|
||||
maxSamples uint64
|
||||
sampleCount uint64
|
||||
samples []int64
|
||||
timestamps []time.Time
|
||||
}
|
||||
|
||||
func (c *sma) Sample(t time.Time, n int64) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.timestamps[c.index] = t
|
||||
c.samples[c.index] = n
|
||||
c.index = (c.index + 1) % c.maxSamples
|
||||
|
||||
// prevent integer overflow in sampleCount. Values greater or equal to
|
||||
// maxSamples have the same semantic meaning.
|
||||
c.sampleCount++
|
||||
if c.sampleCount > c.maxSamples {
|
||||
c.sampleCount = c.maxSamples
|
||||
}
|
||||
}
|
||||
|
||||
func (c *sma) BPS() float64 {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// we need two samples to start
|
||||
if c.sampleCount < 2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// First sample is always the oldest until ring buffer first overflows
|
||||
oldest := c.index
|
||||
if c.sampleCount < c.maxSamples {
|
||||
oldest = 0
|
||||
}
|
||||
|
||||
newest := (c.index + c.maxSamples - 1) % c.maxSamples
|
||||
seconds := c.timestamps[newest].Sub(c.timestamps[oldest]).Seconds()
|
||||
bytes := float64(c.samples[newest] - c.samples[oldest])
|
||||
return bytes / seconds
|
||||
}
|
165
vendor/github.com/cavaliercoder/grab/client.go
generated
vendored
165
vendor/github.com/cavaliercoder/grab/client.go
generated
vendored
@@ -4,20 +4,33 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPClient provides an interface allowing us to perform HTTP requests.
|
||||
type HTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// truncater is a private interface allowing different response
|
||||
// Writers to be truncated
|
||||
type truncater interface {
|
||||
Truncate(size int64) error
|
||||
}
|
||||
|
||||
// A Client is a file download client.
|
||||
//
|
||||
// Clients are safe for concurrent use by multiple goroutines.
|
||||
type Client struct {
|
||||
// HTTPClient specifies the http.Client which will be used for communicating
|
||||
// with the remote server during the file transfer.
|
||||
HTTPClient *http.Client
|
||||
HTTPClient HTTPClient
|
||||
|
||||
// UserAgent specifies the User-Agent string which will be set in the
|
||||
// headers of all requests made by this client.
|
||||
@@ -64,6 +77,7 @@ var DefaultClient = NewClient()
|
||||
func (c *Client) Do(req *Request) *Response {
|
||||
// cancel will be called on all code-paths via closeResponse
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
req = req.WithContext(ctx)
|
||||
resp := &Response{
|
||||
Request: req,
|
||||
Start: time.Now(),
|
||||
@@ -189,7 +203,7 @@ func (c *Client) run(resp *Response, f stateFunc) {
|
||||
//
|
||||
// If an error occurs, the next stateFunc is closeResponse.
|
||||
func (c *Client) statFileInfo(resp *Response) stateFunc {
|
||||
if resp.Filename == "" {
|
||||
if resp.Request.NoStore || resp.Filename == "" {
|
||||
return c.headRequest
|
||||
}
|
||||
fi, err := os.Stat(resp.Filename)
|
||||
@@ -225,31 +239,39 @@ func (c *Client) validateLocal(resp *Response) stateFunc {
|
||||
return c.closeResponse
|
||||
}
|
||||
|
||||
// determine expected file size
|
||||
size := resp.Request.Size
|
||||
if size == 0 && resp.HTTPResponse != nil {
|
||||
size = resp.HTTPResponse.ContentLength
|
||||
// determine target file size
|
||||
expectedSize := resp.Request.Size
|
||||
if expectedSize == 0 && resp.HTTPResponse != nil {
|
||||
expectedSize = resp.HTTPResponse.ContentLength
|
||||
}
|
||||
if size == 0 {
|
||||
|
||||
if expectedSize == 0 {
|
||||
// size is either actually 0 or unknown
|
||||
// if unknown, we ask the remote server
|
||||
// if known to be 0, we proceed with a GET
|
||||
return c.headRequest
|
||||
}
|
||||
|
||||
if size == resp.fi.Size() {
|
||||
if expectedSize == resp.fi.Size() {
|
||||
// local file matches remote file size - wrap it up
|
||||
resp.DidResume = true
|
||||
resp.bytesResumed = resp.fi.Size()
|
||||
return c.checksumFile
|
||||
}
|
||||
|
||||
if resp.Request.NoResume {
|
||||
// local file should be overwritten
|
||||
return c.getRequest
|
||||
}
|
||||
|
||||
if size < resp.fi.Size() {
|
||||
if expectedSize >= 0 && expectedSize < resp.fi.Size() {
|
||||
// remote size is known, is smaller than local size and we want to resume
|
||||
resp.err = ErrBadLength
|
||||
return c.closeResponse
|
||||
}
|
||||
|
||||
if resp.CanResume {
|
||||
// set resume range on GET request
|
||||
resp.Request.HTTPRequest.Header.Set(
|
||||
"Range",
|
||||
fmt.Sprintf("bytes=%d-", resp.fi.Size()))
|
||||
@@ -265,19 +287,24 @@ func (c *Client) checksumFile(resp *Response) stateFunc {
|
||||
return c.closeResponse
|
||||
}
|
||||
if resp.Filename == "" {
|
||||
panic("filename not set")
|
||||
panic("grab: developer error: filename not set")
|
||||
}
|
||||
if resp.Size() < 0 {
|
||||
panic("grab: developer error: size unknown")
|
||||
}
|
||||
req := resp.Request
|
||||
|
||||
// compare checksum
|
||||
// compute checksum
|
||||
var sum []byte
|
||||
sum, resp.err = checksum(req.Context(), resp.Filename, req.hash)
|
||||
sum, resp.err = resp.checksumUnsafe()
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
}
|
||||
|
||||
// compare checksum
|
||||
if !bytes.Equal(sum, req.checksum) {
|
||||
resp.err = ErrBadChecksum
|
||||
if req.deleteOnError {
|
||||
if !resp.Request.NoStore && req.deleteOnError {
|
||||
if err := os.Remove(resp.Filename); err != nil {
|
||||
// err should be os.PathError and include file path
|
||||
resp.err = fmt.Errorf(
|
||||
@@ -326,6 +353,14 @@ func (c *Client) headRequest(resp *Response) stateFunc {
|
||||
return c.getRequest
|
||||
}
|
||||
|
||||
// In case of redirects during HEAD, record the final URL and use it
|
||||
// instead of the original URL when sending future requests.
|
||||
// This way we avoid sending potentially unsupported requests to
|
||||
// the original URL, e.g. "Range", since it was the final URL
|
||||
// that advertised its support.
|
||||
resp.Request.HTTPRequest.URL = resp.HTTPResponse.Request.URL
|
||||
resp.Request.HTTPRequest.Host = resp.HTTPResponse.Request.Host
|
||||
|
||||
return c.readResponse
|
||||
}
|
||||
|
||||
@@ -335,6 +370,8 @@ func (c *Client) getRequest(resp *Response) stateFunc {
|
||||
return c.closeResponse
|
||||
}
|
||||
|
||||
// TODO: check Content-Range
|
||||
|
||||
// check status code
|
||||
if !resp.Request.IgnoreBadStatusCodes {
|
||||
if resp.HTTPResponse.StatusCode < 200 || resp.HTTPResponse.StatusCode > 299 {
|
||||
@@ -348,13 +385,15 @@ func (c *Client) getRequest(resp *Response) stateFunc {
|
||||
|
||||
func (c *Client) readResponse(resp *Response) stateFunc {
|
||||
if resp.HTTPResponse == nil {
|
||||
panic("Response.HTTPResponse is not ready")
|
||||
panic("grab: developer error: Response.HTTPResponse is nil")
|
||||
}
|
||||
|
||||
// check expected size
|
||||
resp.Size = resp.bytesResumed + resp.HTTPResponse.ContentLength
|
||||
if resp.HTTPResponse.ContentLength > 0 && resp.Request.Size > 0 {
|
||||
if resp.Request.Size != resp.Size {
|
||||
resp.sizeUnsafe = resp.HTTPResponse.ContentLength
|
||||
if resp.sizeUnsafe >= 0 {
|
||||
// remote size is known
|
||||
resp.sizeUnsafe += resp.bytesResumed
|
||||
if resp.Request.Size > 0 && resp.Request.Size != resp.sizeUnsafe {
|
||||
resp.err = ErrBadLength
|
||||
return c.closeResponse
|
||||
}
|
||||
@@ -371,7 +410,7 @@ func (c *Client) readResponse(resp *Response) stateFunc {
|
||||
resp.Filename = filepath.Join(resp.Request.Filename, filename)
|
||||
}
|
||||
|
||||
if resp.requestMethod() == "HEAD" {
|
||||
if !resp.Request.NoStore && resp.requestMethod() == "HEAD" {
|
||||
if resp.HTTPResponse.Header.Get("Accept-Ranges") == "bytes" {
|
||||
resp.CanResume = true
|
||||
}
|
||||
@@ -385,39 +424,45 @@ func (c *Client) readResponse(resp *Response) stateFunc {
|
||||
//
|
||||
// Requires that Response.Filename and resp.DidResume are already be set.
|
||||
func (c *Client) openWriter(resp *Response) stateFunc {
|
||||
if !resp.Request.NoCreateDirectories {
|
||||
if !resp.Request.NoStore && !resp.Request.NoCreateDirectories {
|
||||
resp.err = mkdirp(resp.Filename)
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// compute write flags
|
||||
flag := os.O_CREATE | os.O_WRONLY
|
||||
if resp.fi != nil {
|
||||
if resp.DidResume {
|
||||
flag = os.O_APPEND | os.O_WRONLY
|
||||
} else {
|
||||
flag = os.O_TRUNC | os.O_WRONLY
|
||||
if resp.Request.NoStore {
|
||||
resp.writer = &resp.storeBuffer
|
||||
} else {
|
||||
// compute write flags
|
||||
flag := os.O_CREATE | os.O_WRONLY
|
||||
if resp.fi != nil {
|
||||
if resp.DidResume {
|
||||
flag = os.O_APPEND | os.O_WRONLY
|
||||
} else {
|
||||
// truncate later in copyFile, if not cancelled
|
||||
// by BeforeCopy hook
|
||||
flag = os.O_WRONLY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.OpenFile(resp.Filename, flag, 0644)
|
||||
if err != nil {
|
||||
resp.err = err
|
||||
return c.closeResponse
|
||||
}
|
||||
resp.writer = f
|
||||
// open file
|
||||
f, err := os.OpenFile(resp.Filename, flag, 0666)
|
||||
if err != nil {
|
||||
resp.err = err
|
||||
return c.closeResponse
|
||||
}
|
||||
resp.writer = f
|
||||
|
||||
// seek to start or end
|
||||
whence := os.SEEK_SET
|
||||
if resp.bytesResumed > 0 {
|
||||
whence = os.SEEK_END
|
||||
}
|
||||
_, resp.err = f.Seek(0, whence)
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
// seek to start or end
|
||||
whence := os.SEEK_SET
|
||||
if resp.bytesResumed > 0 {
|
||||
whence = os.SEEK_END
|
||||
}
|
||||
_, resp.err = f.Seek(0, whence)
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// init transfer
|
||||
@@ -450,24 +495,42 @@ func (c *Client) copyFile(resp *Response) stateFunc {
|
||||
}
|
||||
}
|
||||
|
||||
var bytesCopied int64
|
||||
if resp.transfer == nil {
|
||||
panic("developer error: Response.transfer is not initialized")
|
||||
panic("grab: developer error: Response.transfer is nil")
|
||||
}
|
||||
go resp.watchBps()
|
||||
_, resp.err = resp.transfer.copy()
|
||||
|
||||
// We waited to truncate the file in openWriter() to make sure
|
||||
// the BeforeCopy didn't cancel the copy. If this was an existing
|
||||
// file that is not going to be resumed, truncate the contents.
|
||||
if t, ok := resp.writer.(truncater); ok && resp.fi != nil && !resp.DidResume {
|
||||
t.Truncate(0)
|
||||
}
|
||||
|
||||
bytesCopied, resp.err = resp.transfer.copy()
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
}
|
||||
closeWriter(resp)
|
||||
|
||||
// set timestamp
|
||||
if !resp.Request.IgnoreRemoteTime {
|
||||
// set file timestamp
|
||||
if !resp.Request.NoStore && !resp.Request.IgnoreRemoteTime {
|
||||
resp.err = setLastModified(resp.HTTPResponse, resp.Filename)
|
||||
if resp.err != nil {
|
||||
return c.closeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// update transfer size if previously unknown
|
||||
if resp.Size() < 0 {
|
||||
discoveredSize := resp.bytesResumed + bytesCopied
|
||||
atomic.StoreInt64(&resp.sizeUnsafe, discoveredSize)
|
||||
if resp.Request.Size > 0 && resp.Request.Size != discoveredSize {
|
||||
resp.err = ErrBadLength
|
||||
return c.closeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// run AfterCopy hook
|
||||
if f := resp.Request.AfterCopy; f != nil {
|
||||
resp.err = f(resp)
|
||||
@@ -480,16 +543,16 @@ func (c *Client) copyFile(resp *Response) stateFunc {
|
||||
}
|
||||
|
||||
func closeWriter(resp *Response) {
|
||||
if resp.writer != nil {
|
||||
resp.writer.Close()
|
||||
resp.writer = nil
|
||||
if closer, ok := resp.writer.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
resp.writer = nil
|
||||
}
|
||||
|
||||
// close finalizes the Response
|
||||
func (c *Client) closeResponse(resp *Response) stateFunc {
|
||||
if resp.IsComplete() {
|
||||
panic("response already closed")
|
||||
panic("grab: developer error: response already closed")
|
||||
}
|
||||
|
||||
resp.fi = nil
|
||||
|
3
vendor/github.com/cavaliercoder/grab/go.mod
generated
vendored
Normal file
3
vendor/github.com/cavaliercoder/grab/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/cavaliercoder/grab
|
||||
|
||||
go 1.14
|
5
vendor/github.com/cavaliercoder/grab/request.go
generated
vendored
5
vendor/github.com/cavaliercoder/grab/request.go
generated
vendored
@@ -51,6 +51,11 @@ type Request struct {
|
||||
// completed in full, it will not be restarted.
|
||||
NoResume bool
|
||||
|
||||
// NoStore specifies that grab should not write to the local file system.
|
||||
// Instead, the download will be stored in memory and accessible only via
|
||||
// Response.Open or Response.Bytes.
|
||||
NoStore bool
|
||||
|
||||
// NoCreateDirectories specifies that any missing directories in the given
|
||||
// Filename path should not be created automatically, if they do not already
|
||||
// exist.
|
||||
|
120
vendor/github.com/cavaliercoder/grab/response.go
generated
vendored
120
vendor/github.com/cavaliercoder/grab/response.go
generated
vendored
@@ -1,11 +1,13 @@
|
||||
package grab
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -31,7 +33,7 @@ type Response struct {
|
||||
Filename string
|
||||
|
||||
// Size specifies the total expected size of the file transfer.
|
||||
Size int64
|
||||
sizeUnsafe int64
|
||||
|
||||
// Start specifies the time at which the file transfer started.
|
||||
Start time.Time
|
||||
@@ -70,7 +72,11 @@ type Response struct {
|
||||
|
||||
// writer is the file handle used to write the downloaded file to local
|
||||
// storage
|
||||
writer io.WriteCloser
|
||||
writer io.Writer
|
||||
|
||||
// storeBuffer receives the contents of the transfer if Request.NoStore is
|
||||
// enabled.
|
||||
storeBuffer bytes.Buffer
|
||||
|
||||
// bytesCompleted specifies the number of bytes which were already
|
||||
// transferred before this transfer began.
|
||||
@@ -80,11 +86,6 @@ type Response struct {
|
||||
// file, tracking progress and allowing for cancelation.
|
||||
transfer *transfer
|
||||
|
||||
// bytesPerSecond specifies the number of bytes that have been transferred in
|
||||
// the last 1-second window.
|
||||
bytesPerSecond float64
|
||||
bytesPerSecondMu sync.Mutex
|
||||
|
||||
// bufferSize specifies the size in bytes of the transfer buffer.
|
||||
bufferSize int
|
||||
|
||||
@@ -125,6 +126,13 @@ func (c *Response) Err() error {
|
||||
return c.err
|
||||
}
|
||||
|
||||
// Size returns the size of the file transfer. If the remote server does not
|
||||
// specify the total size and the transfer is incomplete, the return value is
|
||||
// -1.
|
||||
func (c *Response) Size() int64 {
|
||||
return atomic.LoadInt64(&c.sizeUnsafe)
|
||||
}
|
||||
|
||||
// BytesComplete returns the total number of bytes which have been copied to
|
||||
// the destination, including any bytes that were resumed from a previous
|
||||
// download.
|
||||
@@ -132,25 +140,24 @@ func (c *Response) BytesComplete() int64 {
|
||||
return c.bytesResumed + c.transfer.N()
|
||||
}
|
||||
|
||||
// BytesPerSecond returns the number of bytes transferred in the last second. If
|
||||
// the download is already complete, the average bytes/sec for the life of the
|
||||
// download is returned.
|
||||
// BytesPerSecond returns the number of bytes per second transferred using a
|
||||
// simple moving average of the last five seconds. If the download is already
|
||||
// complete, the average bytes/sec for the life of the download is returned.
|
||||
func (c *Response) BytesPerSecond() float64 {
|
||||
if c.IsComplete() {
|
||||
return float64(c.transfer.N()) / c.Duration().Seconds()
|
||||
}
|
||||
c.bytesPerSecondMu.Lock()
|
||||
defer c.bytesPerSecondMu.Unlock()
|
||||
return c.bytesPerSecond
|
||||
return c.transfer.BPS()
|
||||
}
|
||||
|
||||
// Progress returns the ratio of total bytes that have been downloaded. Multiply
|
||||
// the returned value by 100 to return the percentage completed.
|
||||
func (c *Response) Progress() float64 {
|
||||
if c.Size == 0 {
|
||||
size := c.Size()
|
||||
if size <= 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(c.BytesComplete()) / float64(c.Size)
|
||||
return float64(c.BytesComplete()) / float64(size)
|
||||
}
|
||||
|
||||
// Duration returns the duration of a file transfer. If the transfer is in
|
||||
@@ -173,40 +180,53 @@ func (c *Response) ETA() time.Time {
|
||||
return c.End
|
||||
}
|
||||
bt := c.BytesComplete()
|
||||
bps := c.BytesPerSecond()
|
||||
bps := c.transfer.BPS()
|
||||
if bps == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
secs := float64(c.Size-bt) / bps
|
||||
secs := float64(c.Size()-bt) / bps
|
||||
return time.Now().Add(time.Duration(secs) * time.Second)
|
||||
}
|
||||
|
||||
// watchBps watches the progress of a transfer and maintains statistics.
|
||||
func (c *Response) watchBps() {
|
||||
var prev int64
|
||||
then := c.Start
|
||||
|
||||
t := time.NewTicker(time.Second)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.Done:
|
||||
return
|
||||
|
||||
case now := <-t.C:
|
||||
d := now.Sub(then)
|
||||
then = now
|
||||
|
||||
cur := c.transfer.N()
|
||||
bs := cur - prev
|
||||
prev = cur
|
||||
|
||||
c.bytesPerSecondMu.Lock()
|
||||
c.bytesPerSecond = float64(bs) / d.Seconds()
|
||||
c.bytesPerSecondMu.Unlock()
|
||||
}
|
||||
// Open blocks the calling goroutine until the underlying file transfer is
|
||||
// completed and then opens the transferred file for reading. If Request.NoStore
|
||||
// was enabled, the reader will read from memory.
|
||||
//
|
||||
// If an error occurred during the transfer, it will be returned.
|
||||
//
|
||||
// It is the callers responsibility to close the returned file handle.
|
||||
func (c *Response) Open() (io.ReadCloser, error) {
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.openUnsafe()
|
||||
}
|
||||
|
||||
func (c *Response) openUnsafe() (io.ReadCloser, error) {
|
||||
if c.Request.NoStore {
|
||||
return ioutil.NopCloser(bytes.NewReader(c.storeBuffer.Bytes())), nil
|
||||
}
|
||||
return os.Open(c.Filename)
|
||||
}
|
||||
|
||||
// Bytes blocks the calling goroutine until the underlying file transfer is
|
||||
// completed and then reads all bytes from the completed tranafer. If
|
||||
// Request.NoStore was enabled, the bytes will be read from memory.
|
||||
//
|
||||
// If an error occurred during the transfer, it will be returned.
|
||||
func (c *Response) Bytes() ([]byte, error) {
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.Request.NoStore {
|
||||
return c.storeBuffer.Bytes(), nil
|
||||
}
|
||||
f, err := c.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
func (c *Response) requestMethod() string {
|
||||
@@ -216,6 +236,20 @@ func (c *Response) requestMethod() string {
|
||||
return c.HTTPResponse.Request.Method
|
||||
}
|
||||
|
||||
func (c *Response) checksumUnsafe() ([]byte, error) {
|
||||
f, err := c.openUnsafe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
t := newTransfer(c.Request.Context(), nil, c.Request.hash, f, nil)
|
||||
if _, err = t.copy(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sum := c.Request.hash.Sum(nil)
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func (c *Response) closeResponseBody() error {
|
||||
if c.HTTPResponse == nil || c.HTTPResponse.Body == nil {
|
||||
return nil
|
||||
|
58
vendor/github.com/cavaliercoder/grab/transfer.go
generated
vendored
58
vendor/github.com/cavaliercoder/grab/transfer.go
generated
vendored
@@ -4,30 +4,42 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cavaliercoder/grab/bps"
|
||||
)
|
||||
|
||||
type transfer struct {
|
||||
n int64 // must be 64bit aligned on 386
|
||||
ctx context.Context
|
||||
lim RateLimiter
|
||||
w io.Writer
|
||||
r io.Reader
|
||||
b []byte
|
||||
n int64 // must be 64bit aligned on 386
|
||||
ctx context.Context
|
||||
gauge bps.Gauge
|
||||
lim RateLimiter
|
||||
w io.Writer
|
||||
r io.Reader
|
||||
b []byte
|
||||
}
|
||||
|
||||
func newTransfer(ctx context.Context, lim RateLimiter, dst io.Writer, src io.Reader, buf []byte) *transfer {
|
||||
return &transfer{
|
||||
ctx: ctx,
|
||||
lim: lim,
|
||||
w: dst,
|
||||
r: src,
|
||||
b: buf,
|
||||
ctx: ctx,
|
||||
gauge: bps.NewSMA(6), // five second moving average sampling every second
|
||||
lim: lim,
|
||||
w: dst,
|
||||
r: src,
|
||||
b: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// copy behaves similarly to io.CopyBuffer except that it checks for cancelation
|
||||
// of the given context.Context and reports progress in a thread-safe manner.
|
||||
// of the given context.Context, reports progress in a thread-safe manner and
|
||||
// tracks the transfer rate.
|
||||
func (c *transfer) copy() (written int64, err error) {
|
||||
// maintain a bps gauge in another goroutine
|
||||
ctx, cancel := context.WithCancel(c.ctx)
|
||||
defer cancel()
|
||||
go bps.Watch(ctx, c.gauge, c.N, time.Second)
|
||||
|
||||
// start the transfer
|
||||
if c.b == nil {
|
||||
c.b = make([]byte, 32*1024)
|
||||
}
|
||||
@@ -39,12 +51,6 @@ func (c *transfer) copy() (written int64, err error) {
|
||||
default:
|
||||
// keep working
|
||||
}
|
||||
if c.lim != nil {
|
||||
err = c.lim.WaitN(c.ctx, len(c.b))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
nr, er := c.r.Read(c.b)
|
||||
if nr > 0 {
|
||||
nw, ew := c.w.Write(c.b[0:nr])
|
||||
@@ -60,6 +66,13 @@ func (c *transfer) copy() (written int64, err error) {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
// wait for rate limiter
|
||||
if c.lim != nil {
|
||||
err = c.lim.WaitN(c.ctx, nr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
@@ -79,3 +92,12 @@ func (c *transfer) N() (n int64) {
|
||||
n = atomic.LoadInt64(&c.n)
|
||||
return
|
||||
}
|
||||
|
||||
// BPS returns the current bytes per second transfer rate using a simple moving
|
||||
// average.
|
||||
func (c *transfer) BPS() (bps float64) {
|
||||
if c == nil || c.gauge == nil {
|
||||
return 0
|
||||
}
|
||||
return c.gauge.BPS()
|
||||
}
|
||||
|
32
vendor/github.com/cavaliercoder/grab/util.go
generated
vendored
32
vendor/github.com/cavaliercoder/grab/util.go
generated
vendored
@@ -1,9 +1,7 @@
|
||||
package grab
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hash"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -36,22 +34,26 @@ func mkdirp(path string) error {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("error checking destination directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("error creating destination directory: %v", err)
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
panic("destination path is not directory")
|
||||
panic("grab: developer error: destination path is not directory")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// guessFilename returns a filename for the given http.Response. If none can be
|
||||
// determined ErrNoFilename is returned.
|
||||
//
|
||||
// TODO: NoStore operations should not require a filename
|
||||
func guessFilename(resp *http.Response) (string, error) {
|
||||
filename := resp.Request.URL.Path
|
||||
if cd := resp.Header.Get("Content-Disposition"); cd != "" {
|
||||
if _, params, err := mime.ParseMediaType(cd); err == nil {
|
||||
filename = params["filename"]
|
||||
if val, ok := params["filename"]; ok {
|
||||
filename = val
|
||||
} // else filename directive is missing.. fallback to URL.Path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,23 +69,3 @@ func guessFilename(resp *http.Response) (string, error) {
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
// checksum returns a hash of the given file, using the given hash algorithm.
|
||||
func checksum(ctx context.Context, filename string, h hash.Hash) (b []byte, err error) {
|
||||
var f *os.File
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err = f.Close()
|
||||
}()
|
||||
|
||||
t := newTransfer(ctx, nil, h, f, nil)
|
||||
if _, err = t.copy(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b = h.Sum(nil)
|
||||
return
|
||||
}
|
||||
|
1
vendor/github.com/chuckpreslar/emission/.gitignore
generated
vendored
Normal file
1
vendor/github.com/chuckpreslar/emission/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.DS_Store
|
82
vendor/github.com/chuckpreslar/emission/README.md
generated
vendored
Normal file
82
vendor/github.com/chuckpreslar/emission/README.md
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
emission
|
||||
--------
|
||||
|
||||
A simple event emitter for Go.
|
||||
|
||||
[](https://drone.io/github.com/chuckpreslar/emission/latest)
|
||||
|
||||
## Installation
|
||||
|
||||
With Google's [Go](http://www.golang.org) installed on your machine:
|
||||
|
||||
$ go get -u github.com/chuckpreslar/emission
|
||||
|
||||
## Usage
|
||||
|
||||
If you've ever used an event emitter before, using Emission should be very familiar.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
import (
|
||||
"github.com/chuckpreslar/emission"
|
||||
)
|
||||
|
||||
func main() {
|
||||
emitter := emission.NewEmitter()
|
||||
|
||||
hello := func(to string) {
|
||||
fmt.Printf("Hello %s!\n", to)
|
||||
}
|
||||
|
||||
count := func(count int) {
|
||||
for i := 0; i < count; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
}
|
||||
|
||||
emitter.On("hello", hello).
|
||||
On("count", count).
|
||||
Emit("hello", "world").
|
||||
Emit("count", 5)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## About
|
||||
|
||||
The `emission` package provides an event emitter making use of the reflect packages ability to call functions. Using the `Call` method on the value of a function allows passing any type of function to the event emiiter, regardless of the functions parameters.
|
||||
|
||||
## Documentation
|
||||
|
||||
View godoc's or visit [godoc.org](http://godoc.org/github.com/chuckpreslar/emission).
|
||||
|
||||
$ godoc emission
|
||||
|
||||
## License
|
||||
|
||||
> The MIT License (MIT)
|
||||
|
||||
> Copyright (c) 2013 - 2015 Chuck Preslar
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user