summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers_archive.go62
-rw-r--r--pkg/api/handlers/compat/images_build.go2
-rw-r--r--pkg/api/handlers/compat/networks.go6
-rw-r--r--pkg/api/handlers/libpod/images.go2
-rw-r--r--pkg/bindings/generator/generator.go234
-rw-r--r--pkg/bindings/images/build.go3
-rw-r--r--pkg/bindings/images/removeoptions_types.go93
-rw-r--r--pkg/bindings/images/rm.go10
-rw-r--r--pkg/bindings/images/types.go8
-rw-r--r--pkg/bindings/test/images_test.go17
-rw-r--r--pkg/bindings/test/manifests_test.go2
-rw-r--r--pkg/copy/copy.go68
-rw-r--r--pkg/copy/fileinfo.go56
-rw-r--r--pkg/copy/item.go13
-rw-r--r--pkg/copy/parse.go61
-rw-r--r--pkg/domain/entities/system.go1
-rw-r--r--pkg/domain/infra/abi/cp.go64
-rw-r--r--pkg/domain/infra/abi/manifest.go2
-rw-r--r--pkg/domain/infra/abi/play.go4
-rw-r--r--pkg/domain/infra/abi/system.go14
-rw-r--r--pkg/domain/infra/tunnel/containers.go3
-rw-r--r--pkg/specgen/generate/config_linux.go23
-rw-r--r--pkg/specgen/generate/kube/volume.go2
-rw-r--r--pkg/specgen/generate/security.go2
24 files changed, 605 insertions, 147 deletions
diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go
index 223eb2cd5..d8197415c 100644
--- a/pkg/api/handlers/compat/containers_archive.go
+++ b/pkg/api/handlers/compat/containers_archive.go
@@ -1,13 +1,8 @@
package compat
import (
- "bytes"
- "encoding/base64"
- "encoding/json"
"fmt"
"net/http"
- "os"
- "time"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
@@ -15,6 +10,7 @@ import (
"github.com/containers/podman/v2/pkg/copy"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func Archive(w http.ResponseWriter, r *http.Request) {
@@ -71,12 +67,12 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De
utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path))
return
}
- statHeader, err := fileInfoToDockerStats(info)
+ statHeader, err := copy.EncodeFileInfo(info)
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- w.Header().Add("X-Docker-Container-Path-Stat", statHeader)
+ w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader)
// Our work is done when the user is interested in the header only.
if r.Method == http.MethodHead {
@@ -91,47 +87,16 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De
return
}
- w.WriteHeader(http.StatusOK)
- if err := copy.Copy(&source, &destination, false); err != nil {
+ copier, err := copy.GetCopier(&source, &destination, false)
+ if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
-}
-
-func fileInfoToDockerStats(info *copy.FileInfo) (string, error) {
- dockerStats := struct {
- Name string `json:"name"`
- Size int64 `json:"size"`
- Mode os.FileMode `json:"mode"`
- ModTime time.Time `json:"mtime"`
- LinkTarget string `json:"linkTarget"`
- }{
- Name: info.Name,
- Size: info.Size,
- Mode: info.Mode,
- ModTime: info.ModTime,
- LinkTarget: info.LinkTarget,
- }
-
- jsonBytes, err := json.Marshal(&dockerStats)
- if err != nil {
- return "", errors.Wrap(err, "failed to serialize file stats")
- }
-
- buff := bytes.NewBuffer(make([]byte, 0, 128))
- base64encoder := base64.NewEncoder(base64.StdEncoding, buff)
-
- _, err = base64encoder.Write(jsonBytes)
- if err != nil {
- return "", err
- }
-
- err = base64encoder.Close()
- if err != nil {
- return "", err
+ w.WriteHeader(http.StatusOK)
+ if err := copier.Copy(); err != nil {
+ logrus.Errorf("Error during copy: %v", err)
+ return
}
-
- return buff.String(), nil
}
func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) {
@@ -170,9 +135,14 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder,
return
}
- w.WriteHeader(http.StatusOK)
- if err := copy.Copy(&source, &destination, false); err != nil {
+ copier, err := copy.GetCopier(&source, &destination, false)
+ if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
+ w.WriteHeader(http.StatusOK)
+ if err := copier.Copy(); err != nil {
+ logrus.Errorf("Error during copy: %v", err)
+ return
+ }
}
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 43478c1d3..415ff85cd 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -71,6 +71,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
ForceRm bool `schema:"forcerm"`
HTTPProxy bool `schema:"httpproxy"`
Labels string `schema:"labels"`
+ Layers bool `schema:"layers"`
MemSwap int64 `schema:"memswap"`
Memory int64 `schema:"memory"`
NetworkMode string `schema:"networkmode"`
@@ -165,6 +166,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Registry: query.Registry,
IgnoreUnrecognizedInstructions: true,
Quiet: query.Quiet,
+ Layers: query.Layers,
Isolation: buildah.IsolationChroot,
Compression: archive.Gzip,
Args: buildArgs,
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index fe13971b0..f0b922885 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -131,7 +131,7 @@ func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filt
Name: conf.Name,
ID: network.GetNetworkID(conf.Name),
Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
- Scope: "",
+ Scope: "local",
Driver: network.DefaultNetworkDriver,
EnableIPv6: false,
IPAM: dockerNetwork.IPAM{
@@ -197,7 +197,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
}
var reports []*types.NetworkResource
- logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
+ logrus.Debugf("netNames: %q", strings.Join(netNames, ", "))
for _, name := range netNames {
report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters)
if err != nil {
@@ -239,7 +239,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
Internal: networkCreate.Internal,
Labels: networkCreate.Labels,
}
- if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
+ if networkCreate.IPAM != nil && len(networkCreate.IPAM.Config) > 0 {
if len(networkCreate.IPAM.Config) > 1 {
utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config"))
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 6145207ca..505c96126 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -51,7 +51,7 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
return
}
if !report.Value {
- utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(nil, "failed to find image %s", name))
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Errorf("failed to find image %s", name))
return
}
utils.WriteResponse(w, http.StatusNoContent, "")
diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go
new file mode 100644
index 000000000..24c2310ff
--- /dev/null
+++ b/pkg/bindings/generator/generator.go
@@ -0,0 +1,234 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+ "text/template"
+ "time"
+)
+
+var bodyTmpl = `package {{.PackageName}}
+
+import (
+{{range $import := .Imports}} {{$import}}
+{{end}}
+
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+
+Created {{.Date}}
+*/
+
+// Changed
+func (o *{{.StructName}}) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *{{.StructName}}) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch f.Kind() {
+ case reflect.Bool:
+ params.Set(fieldName, strconv.FormatBool(f.Bool()))
+ case reflect.String:
+ params.Set(fieldName, f.String())
+ case reflect.Int, reflect.Int64:
+ // f.Int() is always an int64
+ params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
+ switch typ.Kind() {
+ case reflect.String:
+ s, ok := slice.Interface().([]string)
+ if !ok {
+ return nil, errors.New("failed to convert to string slice")
+ }
+ for _, val := range s {
+ params.Add(fieldName, val)
+ }
+ default:
+ return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
+ }
+ case reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ // I dont know if this code is needed anymore, TBD
+ // for k, v := range filters {
+ // lowerCaseKeys[strings.ToLower(k)] = v
+ // }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ default:
+ return nil, errors.Errorf("unknown type %s", f.Kind().String())
+ }
+ }
+ return params, nil
+}
+`
+
+var fieldTmpl = `
+// With{{.Name}}
+func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} {
+ v := &value
+ o.{{.Name}} = v
+ return o
+}
+`
+
+type fieldStruct struct {
+ Name string
+ StructName string
+ Type string
+}
+
+func main() {
+ var (
+ closed bool
+ fieldStructs []fieldStruct
+ structNode ast.Node
+ )
+ srcFile := os.Getenv("GOFILE")
+ pkg := os.Getenv("GOPACKAGE")
+ inputStructName := os.Args[1]
+ b, err := ioutil.ReadFile(srcFile)
+ if err != nil {
+ panic(err)
+ }
+ fset := token.NewFileSet() // positions are relative to fset
+ f, err := parser.ParseFile(fset, "", b, parser.ParseComments)
+ if err != nil {
+ panic(err)
+ }
+
+ // always add reflect
+ imports := []string{"\"reflect\""}
+ for _, imp := range f.Imports {
+ imports = append(imports, imp.Path.Value)
+ }
+
+ out, err := os.Create(strings.ToLower(inputStructName) + "_" + srcFile)
+ if err != nil {
+ panic(err)
+ }
+ defer func() {
+ if !closed {
+ out.Close()
+ }
+ }()
+ bodyStruct := struct {
+ PackageName string
+ Imports []string
+ Date string
+ StructName string
+ }{
+ PackageName: pkg,
+ Imports: imports,
+ Date: time.Now().String(),
+ StructName: inputStructName,
+ }
+
+ body := template.Must(template.New("body").Parse(bodyTmpl))
+ fields := template.Must(template.New("fields").Parse(fieldTmpl))
+ ast.Inspect(f, func(n ast.Node) bool {
+ ref, refOK := n.(*ast.TypeSpec)
+ if refOK {
+ if ref.Name.Name == inputStructName {
+ structNode = n
+ x := ref.Type.(*ast.StructType)
+ for _, field := range x.Fields.List {
+ var (
+ name string
+ )
+ typeExpr := field.Type
+ start := typeExpr.Pos() - 1
+ end := typeExpr.End() - 1
+ fieldType := strings.Replace(string(b[start:end]), "*", "", 1)
+ if len(field.Names) > 0 {
+ name = field.Names[0].Name
+ if len(name) < 1 {
+ panic(errors.New("bad name"))
+ }
+ }
+ fStruct := fieldStruct{
+ Name: name,
+ StructName: inputStructName,
+ Type: fieldType,
+ }
+ fieldStructs = append(fieldStructs, fStruct)
+ } // for
+
+ // create the body
+ if err := body.Execute(out, bodyStruct); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
+ // create with func from the struct fields
+ for _, fs := range fieldStructs {
+ if err := fields.Execute(out, fs); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ }
+
+ // close out file
+ if err := out.Close(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ closed = true
+
+ // go fmt file
+ gofmt := exec.Command("gofmt", "-w", "-s", out.Name())
+ gofmt.Stderr = os.Stdout
+ if err := gofmt.Run(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
+ // go import file
+ goimport := exec.Command("goimports", "-w", out.Name())
+ goimport.Stderr = os.Stdout
+ if err := goimport.Run(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ }
+
+ }
+ return true
+ })
+}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 815ab4e86..d34ab87d9 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -41,6 +41,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if options.NoCache {
params.Set("nocache", "1")
}
+ if options.Layers {
+ params.Set("layers", "1")
+ }
// TODO cachefrom
if options.PullPolicy == buildah.PullAlways {
params.Set("pull", "1")
diff --git a/pkg/bindings/images/removeoptions_types.go b/pkg/bindings/images/removeoptions_types.go
new file mode 100644
index 000000000..5902bf908
--- /dev/null
+++ b/pkg/bindings/images/removeoptions_types.go
@@ -0,0 +1,93 @@
+package images
+
+import (
+ "net/url"
+ "reflect"
+ "strconv"
+
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+
+Created 2020-12-10 12:51:06.090426622 -0600 CST m=+0.000133169
+*/
+
+// Changed
+func (o *RemoveOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *RemoveOptions) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch f.Kind() {
+ case reflect.Bool:
+ params.Set(fieldName, strconv.FormatBool(f.Bool()))
+ case reflect.String:
+ params.Set(fieldName, f.String())
+ case reflect.Int, reflect.Int64:
+ // f.Int() is always an int64
+ params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
+ switch typ.Kind() {
+ case reflect.String:
+ s, ok := slice.Interface().([]string)
+ if !ok {
+ return nil, errors.New("failed to convert to string slice")
+ }
+ for _, val := range s {
+ params.Add(fieldName, val)
+ }
+ default:
+ return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
+ }
+ case reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ // I dont know if this code is needed anymore, TBD
+ // for k, v := range filters {
+ // lowerCaseKeys[strings.ToLower(k)] = v
+ // }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ default:
+ return nil, errors.Errorf("unknown type %s", f.Kind().String())
+ }
+ }
+ return params, nil
+}
+
+// WithForce
+func (o *RemoveOptions) WithForce(value bool) *RemoveOptions {
+ v := &value
+ o.Force = v
+ return o
+}
diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go
index 9685b75e4..0b3b88165 100644
--- a/pkg/bindings/images/rm.go
+++ b/pkg/bindings/images/rm.go
@@ -41,17 +41,19 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove
return &report.ImageRemoveReport, errorhandling.StringsToErrors(report.Errors)
}
-// Remove removes an image from the local storage. Use force to remove an
+// Remove removes an image from the local storage. Use optional force option to remove an
// image, even if it's used by containers.
-func Remove(ctx context.Context, nameOrID string, force bool) (*entities.ImageRemoveReport, error) {
+func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.ImageRemoveReport, error) {
var report handlers.LibpodImagesRemoveReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
- params := url.Values{}
- params.Set("force", strconv.FormatBool(force))
+ params, err := options.ToParams()
+ if err != nil {
+ return nil, err
+ }
response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID)
if err != nil {
return nil, err
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
new file mode 100644
index 000000000..340c7bdb9
--- /dev/null
+++ b/pkg/bindings/images/types.go
@@ -0,0 +1,8 @@
+package images
+
+//go:generate go run ../generator/generator.go RemoveOptions
+// RemoveOptions are optional options for image removal
+type RemoveOptions struct {
+ // Forces removes all containers based on the image
+ Force *bool
+}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 7d9415f91..684f110e8 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -84,7 +84,7 @@ var _ = Describe("Podman images", func() {
// Test to validate the remove image api
It("remove image", func() {
// Remove invalid image should be a 404
- response, err := images.Remove(bt.conn, "foobar5000", false)
+ response, err := images.Remove(bt.conn, "foobar5000", nil)
Expect(err).ToNot(BeNil())
Expect(response).To(BeNil())
code, _ := bindings.CheckResponseCode(err)
@@ -93,7 +93,7 @@ var _ = Describe("Podman images", func() {
// Remove an image by name, validate image is removed and error is nil
inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil)
Expect(err).To(BeNil())
- response, err = images.Remove(bt.conn, busybox.shortName, false)
+ response, err = images.Remove(bt.conn, busybox.shortName, nil)
Expect(err).To(BeNil())
code, _ = bindings.CheckResponseCode(err)
@@ -113,12 +113,13 @@ var _ = Describe("Podman images", func() {
// try to remove the image "alpine". This should fail since we are not force
// deleting hence image cannot be deleted until the container is deleted.
- response, err = images.Remove(bt.conn, alpine.shortName, false)
+ response, err = images.Remove(bt.conn, alpine.shortName, nil)
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusConflict))
// Removing the image "alpine" where force = true
- response, err = images.Remove(bt.conn, alpine.shortName, true)
+ options := images.RemoveOptions{}
+ response, err = images.Remove(bt.conn, alpine.shortName, options.WithForce(true))
Expect(err).To(BeNil())
// To be extra sure, check if the previously created container
// is gone as well.
@@ -213,7 +214,7 @@ var _ = Describe("Podman images", func() {
It("Load|Import Image", func() {
// load an image
- _, err := images.Remove(bt.conn, alpine.name, false)
+ _, err := images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err := images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -231,7 +232,7 @@ var _ = Describe("Podman images", func() {
// load with a repo name
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
Expect(err).To(BeNil())
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -247,7 +248,7 @@ var _ = Describe("Podman images", func() {
// load with a bad repo name should trigger a 500
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
Expect(err).To(BeNil())
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
@@ -275,7 +276,7 @@ var _ = Describe("Podman images", func() {
It("Import Image", func() {
// load an image
- _, err = images.Remove(bt.conn, alpine.name, false)
+ _, err = images.Remove(bt.conn, alpine.name, nil)
Expect(err).To(BeNil())
exists, err := images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go
index 55fc4cb0d..a4ecaa20f 100644
--- a/pkg/bindings/test/manifests_test.go
+++ b/pkg/bindings/test/manifests_test.go
@@ -47,7 +47,7 @@ var _ = Describe("Podman containers ", func() {
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
- _, err = images.Remove(bt.conn, id, false)
+ _, err = images.Remove(bt.conn, id, nil)
Expect(err).To(BeNil())
// create manifest list with images
diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go
index 0e68eb450..13893deb2 100644
--- a/pkg/copy/copy.go
+++ b/pkg/copy/copy.go
@@ -25,31 +25,61 @@ import (
//
// ****************************************************************************
-// Copy the source item to destination. Use extract to untar the source if
-// it's a tar archive.
-func Copy(source *CopyItem, destination *CopyItem, extract bool) error {
+// Copier copies data from a source to a destination CopyItem.
+type Copier struct {
+ copyFunc func() error
+ cleanUpFuncs []deferFunc
+}
+
+// cleanUp releases resources the Copier may hold open.
+func (c *Copier) cleanUp() {
+ for _, f := range c.cleanUpFuncs {
+ f()
+ }
+}
+
+// Copy data from a source to a destination CopyItem.
+func (c *Copier) Copy() error {
+ defer c.cleanUp()
+ return c.copyFunc()
+}
+
+// GetCopiers returns a Copier to copy the source item to destination. Use
+// extract to untar the source if it's a tar archive.
+func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) {
+ copier := &Copier{}
+
// First, do the man-page dance. See podman-cp(1) for details.
if err := enforceCopyRules(source, destination); err != nil {
- return err
+ return nil, err
}
// Destination is a stream (e.g., stdout or an http body).
if destination.info.IsStream {
// Source is a stream (e.g., stdin or an http body).
if source.info.IsStream {
- _, err := io.Copy(destination.writer, source.reader)
- return err
+ copier.copyFunc = func() error {
+ _, err := io.Copy(destination.writer, source.reader)
+ return err
+ }
+ return copier, nil
}
root, glob, err := source.buildahGlobs()
if err != nil {
- return err
+ return nil, err
}
- return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer)
+ copier.copyFunc = func() error {
+ return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer)
+ }
+ return copier, nil
}
// Destination is either a file or a directory.
if source.info.IsStream {
- return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader)
+ copier.copyFunc = func() error {
+ return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader)
+ }
+ return copier, nil
}
tarOptions := &archive.TarOptions{
@@ -71,33 +101,36 @@ func Copy(source *CopyItem, destination *CopyItem, extract bool) error {
var tarReader io.ReadCloser
if extract && archive.IsArchivePath(source.resolved) {
if !destination.info.IsDir {
- return errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original)
+ return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original)
}
reader, err := os.Open(source.resolved)
if err != nil {
- return err
+ return nil, err
}
- defer reader.Close()
+ copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() })
// The stream from stdin may be compressed (e.g., via gzip).
decompressedStream, err := archive.DecompressStream(reader)
if err != nil {
- return err
+ return nil, err
}
- defer decompressedStream.Close()
+ copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() })
tarReader = decompressedStream
} else {
reader, err := archive.TarWithOptions(source.resolved, tarOptions)
if err != nil {
- return err
+ return nil, err
}
- defer reader.Close()
+ copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() })
tarReader = reader
}
- return buildahCopiah.Put(root, dir, source.putOptions(), tarReader)
+ copier.copyFunc = func() error {
+ return buildahCopiah.Put(root, dir, source.putOptions(), tarReader)
+ }
+ return copier, nil
}
// enforceCopyRules enforces the rules for copying from a source to a
@@ -114,7 +147,6 @@ func enforceCopyRules(source, destination *CopyItem) error {
return nil
}
- // Source is a *stream*.
if source.info.IsStream {
if !(destination.info.IsDir || destination.info.IsStream) {
return errors.New("destination must be a directory or stream when copying from a stream")
diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go
new file mode 100644
index 000000000..08b4eb377
--- /dev/null
+++ b/pkg/copy/fileinfo.go
@@ -0,0 +1,56 @@
+package copy
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+// XDockerContainerPathStatHeader is the *key* in http headers pointing to the
+// base64 encoded JSON payload of stating a path in a container.
+const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat"
+
+// FileInfo describes a file or directory and is returned by
+// (*CopyItem).Stat().
+type FileInfo struct {
+ Name string `json:"name"`
+ Size int64 `json:"size"`
+ Mode os.FileMode `json:"mode"`
+ ModTime time.Time `json:"mtime"`
+ IsDir bool `json:"isDir"`
+ IsStream bool `json:"isStream"`
+ LinkTarget string `json:"linkTarget"`
+}
+
+// EncodeFileInfo serializes the specified FileInfo as a base64 encoded JSON
+// payload. Intended for Docker compat.
+func EncodeFileInfo(info *FileInfo) (string, error) {
+ buf, err := json.Marshal(&info)
+ if err != nil {
+ return "", errors.Wrap(err, "failed to serialize file stats")
+ }
+ return base64.URLEncoding.EncodeToString(buf), nil
+}
+
+// ExtractFileInfoFromHeader extracts a base64 encoded JSON payload of a
+// FileInfo in the http header. If no such header entry is found, nil is
+// returned. Intended for Docker compat.
+func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) {
+ rawData := header.Get(XDockerContainerPathStatHeader)
+ if len(rawData) == 0 {
+ return nil, nil
+ }
+
+ info := FileInfo{}
+ base64Decoder := base64.NewDecoder(base64.URLEncoding, strings.NewReader(rawData))
+ if err := json.NewDecoder(base64Decoder).Decode(&info); err != nil {
+ return nil, err
+ }
+
+ return &info, nil
+}
diff --git a/pkg/copy/item.go b/pkg/copy/item.go
index db6bca610..df8bf30b9 100644
--- a/pkg/copy/item.go
+++ b/pkg/copy/item.go
@@ -5,7 +5,6 @@ import (
"os"
"path/filepath"
"strings"
- "time"
buildahCopiah "github.com/containers/buildah/copier"
"github.com/containers/buildah/pkg/chrootuser"
@@ -75,18 +74,6 @@ type CopyItem struct {
// deferFunc allows for returning functions that must be deferred at call sites.
type deferFunc func()
-// FileInfo describes a file or directory and is returned by
-// (*CopyItem).Stat().
-type FileInfo struct {
- Name string `json:"name"`
- Size int64 `json:"size"`
- Mode os.FileMode `json:"mode"`
- ModTime time.Time `json:"mtime"`
- IsDir bool `json:"isDir"`
- IsStream bool `json:"isStream"`
- LinkTarget string `json:"linkTarget"`
-}
-
// Stat returns the FileInfo.
func (item *CopyItem) Stat() (*FileInfo, error) {
return &item.info, item.statError
diff --git a/pkg/copy/parse.go b/pkg/copy/parse.go
new file mode 100644
index 000000000..39e0e1547
--- /dev/null
+++ b/pkg/copy/parse.go
@@ -0,0 +1,61 @@
+package copy
+
+import (
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+// ParseSourceAndDestination parses the source and destination input into a
+// possibly specified container and path. The input format is described in
+// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long
+// they start with a dot or slash.
+//
+// It returns, in order, the source container and path, followed by the
+// destination container and path, and an error. Note that exactly one
+// container must be specified.
+func ParseSourceAndDestination(source, destination string) (string, string, string, string, error) {
+ sourceContainer, sourcePath := parseUserInput(source)
+ destContainer, destPath := parseUserInput(destination)
+
+ numContainers := 0
+ if len(sourceContainer) > 0 {
+ numContainers++
+ }
+ if len(destContainer) > 0 {
+ numContainers++
+ }
+
+ if numContainers != 1 {
+ return "", "", "", "", errors.Errorf("invalid arguments %q, %q: exactly 1 container expected but %d specified", source, destination, numContainers)
+ }
+
+ if len(sourcePath) == 0 || len(destPath) == 0 {
+ return "", "", "", "", errors.Errorf("invalid arguments %q, %q: you must specify paths", source, destination)
+ }
+
+ return sourceContainer, sourcePath, destContainer, destPath, nil
+}
+
+// parseUserInput parses the input string and returns, if specified, the name
+// or ID of the container and the path. The input format is described in
+// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long
+// they start with a dot or slash.
+func parseUserInput(input string) (container string, path string) {
+ if len(input) == 0 {
+ return
+ }
+ path = input
+
+ // If the input starts with a dot or slash, it cannot refer to a
+ // container.
+ if input[0] == '.' || input[0] == '/' {
+ return
+ }
+
+ if spl := strings.SplitN(path, ":", 2); len(spl) == 2 {
+ container = spl[0]
+ path = spl[1]
+ }
+ return
+}
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index bde2b6ef2..4af013134 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -19,6 +19,7 @@ type ServiceOptions struct {
type SystemPruneOptions struct {
All bool
Volume bool
+ Filter []string
}
// SystemPruneReport provides report after system prune is executed.
diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go
index 9409df743..362053cce 100644
--- a/pkg/domain/infra/abi/cp.go
+++ b/pkg/domain/infra/abi/cp.go
@@ -2,46 +2,53 @@ package abi
import (
"context"
- "strings"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/copy"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/pkg/errors"
)
func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error {
- srcCtr, srcPath := parsePath(ic.Libpod, source)
- destCtr, destPath := parsePath(ic.Libpod, dest)
-
- if srcCtr != nil && destCtr != nil {
- return errors.Errorf("invalid arguments %q, %q: you must use just one container", source, dest)
+ // Parse user input.
+ sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest)
+ if err != nil {
+ return err
}
- if srcCtr == nil && destCtr == nil {
- return errors.Errorf("invalid arguments %q, %q: you must specify one container", source, dest)
+
+ // Look up containers.
+ var sourceContainer, destContainer *libpod.Container
+ if len(sourceContainerStr) > 0 {
+ sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr)
+ if err != nil {
+ return err
+ }
}
- if len(srcPath) == 0 || len(destPath) == 0 {
- return errors.Errorf("invalid arguments %q, %q: you must specify paths", source, dest)
+ if len(destContainerStr) > 0 {
+ destContainer, err = ic.Libpod.LookupContainer(destContainerStr)
+ if err != nil {
+ return err
+ }
}
var sourceItem, destinationItem copy.CopyItem
- var err error
- // Copy from the container to the host.
- if srcCtr != nil {
- sourceItem, err = copy.CopyItemForContainer(srcCtr, srcPath, options.Pause, true)
+
+ // Source ... container OR host.
+ if sourceContainer != nil {
+ sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true)
defer sourceItem.CleanUp()
if err != nil {
return err
}
} else {
- sourceItem, err = copy.CopyItemForHost(srcPath, true)
+ sourceItem, err = copy.CopyItemForHost(sourcePath, true)
if err != nil {
return err
}
}
- if destCtr != nil {
- destinationItem, err = copy.CopyItemForContainer(destCtr, destPath, options.Pause, false)
+ // Destination ... container OR host.
+ if destContainer != nil {
+ destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false)
defer destinationItem.CleanUp()
if err != nil {
return err
@@ -55,22 +62,9 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string,
}
// Copy from the host to the container.
- return copy.Copy(&sourceItem, &destinationItem, options.Extract)
-}
-
-func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) {
- if len(path) == 0 {
- return nil, ""
- }
- if path[0] == '.' || path[0] == '/' { // A path cannot point to a container.
- return nil, path
- }
- pathArr := strings.SplitN(path, ":", 2)
- if len(pathArr) == 2 {
- ctr, err := runtime.LookupContainer(pathArr[0])
- if err == nil {
- return ctr, pathArr[1]
- }
+ copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract)
+ if err != nil {
+ return err
}
- return nil, path
+ return copier.Copy()
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index ad7128b42..600d64b1d 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -54,7 +54,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
}
return buf, nil
// no return if local image is not a list of images type
- // continue on getting valid manifest through remote serice
+ // continue on getting valid manifest through remote service
} else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
return nil, errors.Wrapf(err, "loading manifest %q", name)
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 3aeb6a2ee..5b983a3f4 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -212,8 +212,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return nil, errors.Wrapf(err, "Failed to parse image %q", container.Image)
}
// In kube, if the image is tagged with latest, it should always pull
+ // but if the domain is localhost, that means the image was built locally
+ // so do not attempt a pull.
if tagged, isTagged := named.(reference.NamedTagged); isTagged {
- if tagged.Tag() == image.LatestTag {
+ if tagged.Tag() == image.LatestTag && reference.Domain(named) != image.DefaultLocalRegistry {
pullPolicy = util.PullImageAlways
}
}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 7ed58092b..d6881fdc4 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io/ioutil"
+ "net/url"
"os"
"os/exec"
"path/filepath"
@@ -179,7 +180,16 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys
found = true
}
systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...)
- containerPruneReport, err := ic.pruneContainersHelper(nil)
+ containerPruneOptions := entities.ContainerPruneOptions{}
+ for _, f := range options.Filter {
+ t := strings.SplitN(f, "=", 2)
+ containerPruneOptions.Filters = make(url.Values)
+ if len(t) < 2 {
+ return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ containerPruneOptions.Filters.Add(t[0], t[1])
+ }
+ containerPruneReport, err := ic.ContainerPrune(ctx, containerPruneOptions)
if err != nil {
return nil, err
}
@@ -194,7 +204,7 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys
}
}
- results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil)
+ results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, options.Filter)
if err != nil {
return nil, err
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 3584668c7..e65fef0a4 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -732,7 +732,8 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o
}
func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error {
- return errors.New("not implemented")
+ return nil
+ // return containers.Copy(ic.ClientCxt, source, dest, options)
}
// Shutdown Libpod engine
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index 1808f99b8..e0b039fb7 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -167,22 +167,23 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask
g.AddLinuxMaskedPaths(mp)
}
}
+ for _, rp := range []string{
+ "/proc/asound",
+ "/proc/bus",
+ "/proc/fs",
+ "/proc/irq",
+ "/proc/sys",
+ "/proc/sysrq-trigger",
+ } {
+ if !util.StringInSlice(rp, unmask) {
+ g.AddLinuxReadonlyPaths(rp)
+ }
+ }
}
if pidModeIsHost && rootless.IsRootless() {
return
}
-
- for _, rp := range []string{
- "/proc/asound",
- "/proc/bus",
- "/proc/fs",
- "/proc/irq",
- "/proc/sys",
- "/proc/sysrq-trigger",
- } {
- g.AddLinuxReadonlyPaths(rp)
- }
}
// mask the paths provided by the user
diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go
index 2ef0f4c23..bb8edabb7 100644
--- a/pkg/specgen/generate/kube/volume.go
+++ b/pkg/specgen/generate/kube/volume.go
@@ -103,7 +103,7 @@ func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) {
} else if volumeSource.PersistentVolumeClaim != nil {
return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
} else {
- return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the conly supported VolumeSource")
+ return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the only supported VolumeSource")
}
}
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index dee140282..56947ff24 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -141,7 +141,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
} else {
- userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd)
+ userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil)
if err != nil {
return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ","))
}