summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/Makefile3
-rw-r--r--pkg/api/handlers/compat/containers_start.go2
-rw-r--r--pkg/api/handlers/compat/images_build.go28
-rw-r--r--pkg/api/handlers/compat/volumes.go2
-rw-r--r--pkg/api/handlers/libpod/images.go10
-rw-r--r--pkg/api/handlers/libpod/manifests.go16
-rw-r--r--pkg/api/handlers/libpod/play.go22
-rw-r--r--pkg/api/handlers/libpod/swagger.go2
-rw-r--r--pkg/api/handlers/libpod/system.go1
-rw-r--r--pkg/api/handlers/libpod/volumes.go2
-rw-r--r--pkg/api/server/register_containers.go19
-rw-r--r--pkg/api/server/register_generate.go4
-rw-r--r--pkg/api/server/register_healthcheck.go4
-rw-r--r--pkg/api/server/register_images.go60
-rw-r--r--pkg/api/server/register_manifest.go13
-rw-r--r--pkg/api/server/register_networks.go1
-rw-r--r--pkg/api/server/register_play.go8
-rw-r--r--pkg/api/server/register_pods.go4
-rw-r--r--pkg/api/server/register_volumes.go12
-rw-r--r--pkg/bindings/containers/types.go1
-rw-r--r--pkg/bindings/containers/types_start_options.go16
-rw-r--r--pkg/bindings/images/build.go21
-rw-r--r--pkg/bindings/network/network.go10
-rw-r--r--pkg/bindings/network/types.go3
-rw-r--r--pkg/bindings/network/types_prune_options.go16
-rw-r--r--pkg/bindings/play/types.go4
-rw-r--r--pkg/bindings/play/types_kube_options.go17
-rw-r--r--pkg/bindings/test/networks_test.go133
-rw-r--r--pkg/bindings/test/volumes_test.go8
-rw-r--r--pkg/cgroups/cgroups.go2
-rw-r--r--pkg/cgroups/cpu.go8
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/play.go16
-rw-r--r--pkg/domain/entities/system.go5
-rw-r--r--pkg/domain/entities/types/auth.go3
-rw-r--r--pkg/domain/entities/types/types.go3
-rw-r--r--pkg/domain/filters/volumes.go20
-rw-r--r--pkg/domain/infra/abi/containers.go14
-rw-r--r--pkg/domain/infra/abi/containers_runlabel.go25
-rw-r--r--pkg/domain/infra/abi/generate.go172
-rw-r--r--pkg/domain/infra/abi/play.go127
-rw-r--r--pkg/domain/infra/abi/system.go26
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/domain/infra/tunnel/network.go3
-rw-r--r--pkg/domain/infra/tunnel/play.go2
-rw-r--r--pkg/domain/infra/tunnel/system.go2
-rw-r--r--pkg/errorhandling/errorhandling.go3
-rw-r--r--pkg/registrar/registrar.go127
-rw-r--r--pkg/registrar/registrar_test.go213
-rw-r--r--pkg/specgen/generate/container_create.go11
-rw-r--r--pkg/specgen/generate/kube/kube.go6
-rw-r--r--pkg/specgen/generate/storage.go9
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--pkg/util/kube.go16
55 files changed, 751 insertions, 519 deletions
diff --git a/pkg/api/Makefile b/pkg/api/Makefile
index 6b24bfd83..6da5fb57e 100644
--- a/pkg/api/Makefile
+++ b/pkg/api/Makefile
@@ -5,6 +5,9 @@ SWAGGER_OUT ?= swagger.yaml
validate: ${SWAGGER_OUT}
swagger validate ${SWAGGER_OUT}
+serve: ${SWAGGER_OUT}
+ swagger serve -F redoc -p=8080 swagger.yaml
+
.PHONY: ${SWAGGER_OUT}
${SWAGGER_OUT}:
# generate doesn't remove file on error
diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go
index 391aa752d..f1ed1b2b8 100644
--- a/pkg/api/handlers/compat/containers_start.go
+++ b/pkg/api/handlers/compat/containers_start.go
@@ -42,7 +42,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
- if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil {
+ if err := con.Start(r.Context(), true); err != nil {
utils.InternalServerError(w, err)
return
}
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index ab92434b1..ec40fdd2d 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/buildah"
buildahDefine "github.com/containers/buildah/define"
+ "github.com/containers/buildah/pkg/parse"
"github.com/containers/buildah/util"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
@@ -445,17 +446,34 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
},
}
+ if len(query.Platform) > 0 {
+ variant := ""
+ buildOptions.OS, buildOptions.Architecture, variant, err = parse.Platform(query.Platform)
+ if err != nil {
+ utils.BadRequest(w, "platform", query.Platform, err)
+ return
+ }
+ buildOptions.SystemContext.OSChoice = buildOptions.OS
+ buildOptions.SystemContext.ArchitectureChoice = buildOptions.Architecture
+ buildOptions.SystemContext.VariantChoice = variant
+ }
if _, found := r.URL.Query()["timestamp"]; found {
ts := time.Unix(query.Timestamp, 0)
buildOptions.Timestamp = &ts
}
+ var (
+ imageID string
+ success bool
+ )
+
runCtx, cancel := context.WithCancel(context.Background())
- var imageID string
go func() {
defer cancel()
imageID, _, err = runtime.Build(r.Context(), buildOptions, query.Dockerfile)
- if err != nil {
+ if err == nil {
+ success = true
+ } else {
stderr.Write([]byte(err.Error() + "\n"))
}
}()
@@ -471,8 +489,6 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
flush()
- var failed bool
-
body := w.(io.Writer)
if logrus.IsLevelEnabled(logrus.DebugLevel) {
if v, found := os.LookupEnv("PODMAN_RETAIN_BUILD_ARTIFACT"); found {
@@ -513,14 +529,14 @@ loop:
}
flush()
case e := <-stderr.Chan():
- failed = true
m.Error = string(e)
if err := enc.Encode(m); err != nil {
logrus.Warnf("Failed to json encode error %v", err)
}
flush()
case <-runCtx.Done():
- if !failed {
+ flush()
+ if success {
if !utils.IsLibpodRequest(r) {
m.Stream = fmt.Sprintf("Successfully built %12.12s\n", imageID)
if err := enc.Encode(m); err != nil {
diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go
index 42ece643b..d86fc1e19 100644
--- a/pkg/api/handlers/compat/volumes.go
+++ b/pkg/api/handlers/compat/volumes.go
@@ -266,7 +266,7 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
}
f := (url.Values)(*filterMap)
- filterFuncs, err := filters.GenerateVolumeFilters(f)
+ filterFuncs, err := filters.GeneratePruneVolumeFilters(f)
if err != nil {
utils.Error(w, "Something when wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse filters for %s", f.Encode()))
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 158babcdc..92882cc40 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -270,6 +270,16 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
return
}
+ // if format is dir, server will save to an archive
+ // the client will unArchive after receive the archive file
+ // so must convert is at here
+ switch query.Format {
+ case define.OCIManifestDir:
+ query.Format = define.OCIArchive
+ case define.V2s2ManifestDir:
+ query.Format = define.V2s2Archive
+ }
+
switch query.Format {
case define.V2s2Archive, define.OCIArchive:
tmpfile, err := ioutil.TempFile("", "api.tar")
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index 5ababc36b..6a491ae48 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"net/http"
+ "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
@@ -34,6 +35,16 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
+
+ // TODO: (jhonce) When c/image is refactored the roadmap calls for this check to be pushed into that library.
+ for _, n := range query.Name {
+ if _, err := reference.ParseNormalizedNamed(n); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "invalid image name %s", n))
+ return
+ }
+ }
+
rtc, err := runtime.GetConfig()
if err != nil {
utils.InternalServerError(w, err)
@@ -75,11 +86,16 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusNotFound, inspectError)
return
}
+
var list manifest.Schema2List
if err := json.Unmarshal(inspectReport, &list); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Unmarshal()"))
return
}
+ if list.Manifests == nil {
+ list.Manifests = make([]manifest.Schema2ManifestDescriptor, 0)
+ }
+
utils.WriteResponse(w, http.StatusOK, &list)
}
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index eba5386b6..96f572a8b 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -3,6 +3,7 @@ package libpod
import (
"io"
"io/ioutil"
+ "net"
"net/http"
"os"
@@ -20,10 +21,11 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Network string `schema:"network"`
- TLSVerify bool `schema:"tlsVerify"`
- LogDriver string `schema:"logDriver"`
- Start bool `schema:"start"`
+ Network string `schema:"network"`
+ TLSVerify bool `schema:"tlsVerify"`
+ LogDriver string `schema:"logDriver"`
+ Start bool `schema:"start"`
+ StaticIPs []string `schema:"staticIPs"`
}{
TLSVerify: true,
Start: true,
@@ -35,6 +37,17 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
return
}
+ staticIPs := make([]net.IP, 0, len(query.StaticIPs))
+ for _, ipString := range query.StaticIPs {
+ ip := net.ParseIP(ipString)
+ if ip == nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("Invalid IP address %s", ipString))
+ return
+ }
+ staticIPs = append(staticIPs, ip)
+ }
+
// Fetch the K8s YAML file from the body, and copy it to a temp file.
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
if err != nil {
@@ -71,6 +84,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
Network: query.Network,
Quiet: true,
LogDriver: query.LogDriver,
+ StaticIPs: staticIPs,
}
if _, found := r.URL.Query()["tlsVerify"]; found {
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index 2631f19ac..9450a70d9 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -25,7 +25,7 @@ type swagInspectPodResponse struct {
// swagger:response InspectManifest
type swagInspectManifestResponse struct {
// in:body
- Body manifest.List
+ Body manifest.Schema2List
}
// Kill Pod
diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go
index 02457eb8f..2b4cef1bb 100644
--- a/pkg/api/handlers/libpod/system.go
+++ b/pkg/api/handlers/libpod/system.go
@@ -72,6 +72,7 @@ func DiskUsage(w http.ResponseWriter, r *http.Request) {
response, err := ic.SystemDf(r.Context(), options)
if err != nil {
utils.InternalServerError(w, err)
+ return
}
utils.WriteResponse(w, http.StatusOK, response)
}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 442b53d1e..68aec30d5 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -150,7 +150,7 @@ func pruneVolumesHelper(r *http.Request) ([]*reports.PruneReport, error) {
}
f := (url.Values)(*filterMap)
- filterFuncs, err := filters.GenerateVolumeFilters(f)
+ filterFuncs, err := filters.GeneratePruneVolumeFilters(f)
if err != nil {
return nil, err
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 3bf3e4e11..536c4707a 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -774,7 +774,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/prune"), s.APIHandler(compat.PruneContainers)).Methods(http.MethodPost)
- // swagger:operation GET /libpod/containers/showmounted libpod ShowMountedContainersLibpod
+ // swagger:operation GET /libpod/containers/showmounted libpod ContainerShowMountedLibpod
// ---
// tags:
// - containers
@@ -1194,11 +1194,22 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - removing
// - stopping
// description: "Conditions to wait for. If no condition provided the 'exited' condition is assumed."
+ // - in: query
+ // name: interval
+ // type: string
+ // default: "250ms"
+ // description: Time Interval to wait before polling for completion.
// produces:
// - application/json
+ // - text/plain
// responses:
// 200:
- // $ref: "#/responses/ContainerWaitResponse"
+ // description: Status code
+ // schema:
+ // type: integer
+ // format: int32
+ // examples:
+ // text/plain: 137
// 404:
// $ref: "#/responses/NoSuchContainer"
// 500:
@@ -1468,8 +1479,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost)
- // swagger:operation GET /containers/{name}/changes libpod ContainerChangesLibpod
- // swagger:operation GET /libpod/containers/{name}/changes compat ContainerChanges
+ // swagger:operation GET /containers/{name}/changes compat ContainerChanges
+ // swagger:operation GET /libpod/containers/{name}/changes libpod ContainerChangesLibpod
// ---
// tags:
// - containers
diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go
index 208bf668c..e10c7029c 100644
--- a/pkg/api/server/register_generate.go
+++ b/pkg/api/server/register_generate.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
- // swagger:operation GET /libpod/generate/{name:.*}/systemd libpod GenerateSystemdLibpod
+ // swagger:operation GET /libpod/generate/{name}/systemd libpod GenerateSystemdLibpod
// ---
// tags:
// - containers
@@ -17,7 +17,7 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// description: Generate Systemd Units based on a pod or container.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: Name or ID of the container or pod.
diff --git a/pkg/api/server/register_healthcheck.go b/pkg/api/server/register_healthcheck.go
index e2fd5ac68..7d8541a51 100644
--- a/pkg/api/server/register_healthcheck.go
+++ b/pkg/api/server/register_healthcheck.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerHealthCheckHandlers(r *mux.Router) error {
- // swagger:operation GET /libpod/containers/{name:.*}/healthcheck libpod ContainerHealthcheckLibpod
+ // swagger:operation GET /libpod/containers/{name}/healthcheck libpod ContainerHealthcheckLibpod
// ---
// tags:
// - containers
@@ -16,7 +16,7 @@ func (s *APIServer) registerHealthCheckHandlers(r *mux.Router) error {
// description: Execute the defined healthcheck and return information about the results
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index de8d50cc9..b32c0df20 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -188,7 +188,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/search", s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
- // swagger:operation DELETE /images/{name:.*} compat ImageDelete
+ // swagger:operation DELETE /images/{name} compat ImageDelete
// ---
// tags:
// - images (compat)
@@ -196,7 +196,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Delete an image from local storage
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: name or ID of image to delete
@@ -222,7 +222,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}"), s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}", s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
- // swagger:operation POST /images/{name:.*}/push compat ImagePush
+ // swagger:operation POST /images/{name}/push compat ImagePush
// ---
// tags:
// - images (compat)
@@ -230,7 +230,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Push an image to a container registry
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: Name of image to push.
@@ -269,7 +269,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}/push"), s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}/push", s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
- // swagger:operation GET /images/{name:.*}/get compat ImageGet
+ // swagger:operation GET /images/{name}/get compat ImageGet
// ---
// tags:
// - images (compat)
@@ -277,7 +277,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Export an image in tarball format
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -319,7 +319,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/get"), s.APIHandler(compat.ExportImages)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/get", s.APIHandler(compat.ExportImages)).Methods(http.MethodGet)
- // swagger:operation GET /images/{name:.*}/history compat ImageHistory
+ // swagger:operation GET /images/{name}/history compat ImageHistory
// ---
// tags:
// - images (compat)
@@ -327,7 +327,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Return parent layers of an image.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -343,7 +343,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}/history"), s.APIHandler(compat.HistoryImage)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}/history", s.APIHandler(compat.HistoryImage)).Methods(http.MethodGet)
- // swagger:operation GET /images/{name:.*}/json compat ImageInspect
+ // swagger:operation GET /images/{name}/json compat ImageInspect
// ---
// tags:
// - images (compat)
@@ -351,7 +351,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Return low-level information about an image.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -367,7 +367,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}/json"), s.APIHandler(compat.GetImage)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}/json", s.APIHandler(compat.GetImage)).Methods(http.MethodGet)
- // swagger:operation POST /images/{name:.*}/tag compat ImageTag
+ // swagger:operation POST /images/{name}/tag compat ImageTag
// ---
// tags:
// - images (compat)
@@ -375,7 +375,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Tag an image so that it becomes part of a repository.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -667,7 +667,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
libpod endpoints
*/
- // swagger:operation POST /libpod/images/{name:.*}/push libpod ImagePushLibpod
+ // swagger:operation POST /libpod/images/{name}/push libpod ImagePushLibpod
// ---
// tags:
// - images
@@ -675,7 +675,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Push an image to a container registry
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: Name of image to push.
@@ -705,7 +705,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/push"), s.APIHandler(libpod.PushImage)).Methods(http.MethodPost)
- // swagger:operation GET /libpod/images/{name:.*}/exists libpod ImageExistsLibpod
+ // swagger:operation GET /libpod/images/{name}/exists libpod ImageExistsLibpod
// ---
// tags:
// - images
@@ -713,7 +713,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Check if image exists in local store
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -727,7 +727,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/exists"), s.APIHandler(libpod.ImageExists)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/images/{name:.*}/tree libpod ImageTreeLibpod
+ // swagger:operation GET /libpod/images/{name}/tree libpod ImageTreeLibpod
// ---
// tags:
// - images
@@ -735,7 +735,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Retrieve the image tree for the provided image name or ID
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -753,7 +753,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/tree"), s.APIHandler(libpod.ImageTree)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/images/{name:.*}/history libpod ImageHistoryLibpod
+ // swagger:operation GET /libpod/images/{name}/history libpod ImageHistoryLibpod
// ---
// tags:
// - images
@@ -761,7 +761,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Return parent layers of an image.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -906,7 +906,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/remove"), s.APIHandler(libpod.ImagesBatchRemove)).Methods(http.MethodDelete)
- // swagger:operation DELETE /libpod/images/{name:.*} libpod ImageDeleteLibpod
+ // swagger:operation DELETE /libpod/images/{name} libpod ImageDeleteLibpod
// ---
// tags:
// - images
@@ -914,7 +914,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Remove an image from the local storage.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: name or ID of image to remove
@@ -1042,7 +1042,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/images/{name:.*}/get libpod ImageGetLibpod
+ // swagger:operation GET /libpod/images/{name}/get libpod ImageGetLibpod
// ---
// tags:
// - images
@@ -1050,7 +1050,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Export an image
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -1109,7 +1109,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/export"), s.APIHandler(libpod.ExportImages)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/images/{name:.*}/json libpod ImageInspectLibpod
+ // swagger:operation GET /libpod/images/{name}/json libpod ImageInspectLibpod
// ---
// tags:
// - images
@@ -1117,7 +1117,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Obtain low-level information about an image
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -1131,7 +1131,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/json"), s.APIHandler(libpod.GetImage)).Methods(http.MethodGet)
- // swagger:operation POST /libpod/images/{name:.*}/tag libpod ImageTagLibpod
+ // swagger:operation POST /libpod/images/{name}/tag libpod ImageTagLibpod
// ---
// tags:
// - images
@@ -1139,7 +1139,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Tag an image so that it becomes part of a repository.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
@@ -1217,7 +1217,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost)
- // swagger:operation POST /libpod/images/{name:.*}/untag libpod ImageUntagLibpod
+ // swagger:operation POST /libpod/images/{name}/untag libpod ImageUntagLibpod
// ---
// tags:
// - images
@@ -1225,7 +1225,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: Untag an image. If not repo and tag are specified, all tags are removed from the image.
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the container
diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go
index 0417462a6..010d8a79e 100644
--- a/pkg/api/server/register_manifest.go
+++ b/pkg/api/server/register_manifest.go
@@ -59,7 +59,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/manifests/{name}/exists"), s.APIHandler(libpod.ExistsManifest)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/manifests/{name:.*}/json manifests ManifestInspectLibpod
+ // swagger:operation GET /libpod/manifests/{name}/json manifests ManifestInspectLibpod
// ---
// summary: Inspect
// description: Display a manifest list
@@ -67,7 +67,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// - application/json
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the manifest
@@ -79,14 +79,15 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
- // swagger:operation POST /libpod/manifests/{name:.*}/add manifests ManifestAddLibpod
+ // swagger:operation POST /libpod/manifests/{name}/add manifests ManifestAddLibpod
// ---
+ // summary: Add image
// description: Add an image to a manifest list
// produces:
// - application/json
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the name or ID of the manifest
@@ -106,7 +107,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/{name:.*}/add"), s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost)
- // swagger:operation DELETE /libpod/manifests/{name:.*} manifests ManifestDeleteLibpod
+ // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteLibpod
// ---
// summary: Remove
// description: Remove an image from a manifest list
@@ -114,7 +115,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// - application/json
// parameters:
// - in: path
- // name: name:.*
+ // name: name
// type: string
// required: true
// description: the image associated with the manifest
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index e25fd071c..dcec61bef 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -377,7 +377,6 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// name: filters
// type: string
// description: |
- // NOT IMPLEMENTED
// Filters to process on the prune list, encoded as JSON (a map[string][]string).
// Available filters:
// - until=<timestamp> Prune networks created before this timestamp. The <timestamp> can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time.
diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go
index b9d3349be..da37abb70 100644
--- a/pkg/api/server/register_play.go
+++ b/pkg/api/server/register_play.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
- // swagger:operation POST /libpod/play/kube libpod KubePlayLibpod
+ // swagger:operation POST /libpod/play/kube libpod PlayKubeLibpod
// ---
// tags:
// - containers
@@ -34,6 +34,12 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// type: boolean
// default: true
// description: Start the pod after creating it.
+ // - in: query
+ // name: staticIPs
+ // type: array
+ // description: Static IPs used for the pods.
+ // items:
+ // type: string
// - in: body
// name: request
// description: Kubernetes YAML file.
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 226cbce11..3bcc50ba4 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -38,7 +38,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// schema:
// $ref: "#/definitions/PodSpecGenerator"
// responses:
- // 200:
+ // 201:
// schema:
// $ref: "#/definitions/IdResponse"
// 400:
@@ -305,7 +305,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name}/top"), s.APIHandler(libpod.PodTop)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/pods/stats pods PodStatsLibpod
+ // swagger:operation GET /libpod/pods/stats pods PodStatsAllLibpod
// ---
// tags:
// - pods
diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go
index 58f056626..e5d6cf195 100644
--- a/pkg/api/server/register_volumes.go
+++ b/pkg/api/server/register_volumes.go
@@ -81,6 +81,14 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// summary: Prune volumes
// produces:
// - application/json
+ // parameters:
+ // - in: query
+ // name: filters
+ // type: string
+ // description: |
+ // JSON encoded value of filters (a map[string][]string) to match volumes against before pruning.
+ // Available filters:
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune volumes with (or without, in case label!=... is used) the specified labels.
// responses:
// '200':
// "$ref": "#/responses/VolumePruneResponse"
@@ -259,8 +267,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// type: string
// description: |
// JSON encoded value of filters (a map[string][]string) to match volumes against before pruning.
- //
- // Note: No filters are currently supported and any filters specified will cause an error response.
+ // Available filters:
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune volumes with (or without, in case label!=... is used) the specified labels.
// responses:
// '200':
// "$ref": "#/responses/DockerVolumePruneResponse"
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index f63e35bf1..0d22c32f8 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -154,6 +154,7 @@ type RestartOptions struct {
// StartOptions are optional options for starting containers
type StartOptions struct {
DetachKeys *string
+ Recursive *bool
}
//go:generate go run ../generator/generator.go StatsOptions
diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go
index f8ba29623..d419c755c 100644
--- a/pkg/bindings/containers/types_start_options.go
+++ b/pkg/bindings/containers/types_start_options.go
@@ -35,3 +35,19 @@ func (o *StartOptions) GetDetachKeys() string {
}
return *o.DetachKeys
}
+
+// WithRecursive
+func (o *StartOptions) WithRecursive(value bool) *StartOptions {
+ v := &value
+ o.Recursive = v
+ return o
+}
+
+// GetRecursive
+func (o *StartOptions) GetRecursive() bool {
+ var recursive bool
+ if o.Recursive == nil {
+ return recursive
+ }
+ return *o.Recursive
+}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index c47a16551..c0e5706a5 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"regexp"
+ "runtime"
"strconv"
"strings"
@@ -190,6 +191,10 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
platform = "linux"
}
platform += "/" + options.Architecture
+ } else {
+ if len(platform) > 0 {
+ platform += "/" + runtime.GOARCH
+ }
}
if len(platform) > 0 {
params.Set("platform", platform)
@@ -335,6 +340,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
re := regexp.MustCompile(`[0-9a-f]{12}`)
var id string
+ var mErr error
for {
var s struct {
Stream string `json:"stream,omitempty"`
@@ -342,11 +348,21 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
if err := dec.Decode(&s); err != nil {
if errors.Is(err, io.EOF) {
- return &entities.BuildReport{ID: id}, nil
+ if mErr == nil && id == "" {
+ mErr = errors.New("stream dropped, unexpected failure")
+ }
+ break
}
s.Error = err.Error() + "\n"
}
+ select {
+ case <-response.Request.Context().Done():
+ return &entities.BuildReport{ID: id}, mErr
+ default:
+ // non-blocking select
+ }
+
switch {
case s.Stream != "":
stdout.Write([]byte(s.Stream))
@@ -354,11 +370,12 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
id = strings.TrimSuffix(s.Stream, "\n")
}
case s.Error != "":
- return nil, errors.New(s.Error)
+ mErr = errors.New(s.Error)
default:
return &entities.BuildReport{ID: id}, errors.New("failed to parse build results stream, unexpected input")
}
}
+ return &entities.BuildReport{ID: id}, mErr
}
func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
index 6f3aa8594..17451c273 100644
--- a/pkg/bindings/network/network.go
+++ b/pkg/bindings/network/network.go
@@ -184,7 +184,13 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
// Prune removes unused CNI networks
func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPruneReport, error) {
- // TODO Filters is not implemented
+ if options == nil {
+ options = new(PruneOptions)
+ }
+ params, err := options.ToParams()
+ if err != nil {
+ return nil, err
+ }
var (
prunedNetworks []*entities.NetworkPruneReport
)
@@ -193,7 +199,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPrune
return nil, err
}
- response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", nil, nil)
+ response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", params, nil)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go
index 47dce67c7..e62ae8f52 100644
--- a/pkg/bindings/network/types.go
+++ b/pkg/bindings/network/types.go
@@ -79,4 +79,7 @@ type ExistsOptions struct {
// PruneOptions are optional options for removing unused
// CNI networks
type PruneOptions struct {
+ // Filters are applied to the prune of networks to be more
+ // specific on choosing
+ Filters map[string][]string
}
diff --git a/pkg/bindings/network/types_prune_options.go b/pkg/bindings/network/types_prune_options.go
index 84e1a8b32..f17e09d69 100644
--- a/pkg/bindings/network/types_prune_options.go
+++ b/pkg/bindings/network/types_prune_options.go
@@ -19,3 +19,19 @@ func (o *PruneOptions) Changed(fieldName string) bool {
func (o *PruneOptions) ToParams() (url.Values, error) {
return util.ToParams(o)
}
+
+// WithFilters
+func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions {
+ v := value
+ o.Filters = v
+ return o
+}
+
+// GetFilters
+func (o *PruneOptions) GetFilters() map[string][]string {
+ var filters map[string][]string
+ if o.Filters == nil {
+ return filters
+ }
+ return o.Filters
+}
diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go
index 5fb9a4d41..6598ec3c2 100644
--- a/pkg/bindings/play/types.go
+++ b/pkg/bindings/play/types.go
@@ -1,5 +1,7 @@
package play
+import "net"
+
//go:generate go run ../generator/generator.go KubeOptions
// KubeOptions are optional options for replaying kube YAML files
type KubeOptions struct {
@@ -23,6 +25,8 @@ type KubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot *string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs *[]net.IP
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
ConfigMaps *[]string
// LogDriver for the container. For example: journald
diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go
index 78396a090..a1786f553 100644
--- a/pkg/bindings/play/types_kube_options.go
+++ b/pkg/bindings/play/types_kube_options.go
@@ -1,6 +1,7 @@
package play
import (
+ "net"
"net/url"
"github.com/containers/podman/v3/pkg/bindings/internal/util"
@@ -164,6 +165,22 @@ func (o *KubeOptions) GetSeccompProfileRoot() string {
return *o.SeccompProfileRoot
}
+// WithStaticIPs
+func (o *KubeOptions) WithStaticIPs(value []net.IP) *KubeOptions {
+ v := &value
+ o.StaticIPs = v
+ return o
+}
+
+// GetStaticIPs
+func (o *KubeOptions) GetStaticIPs() []net.IP {
+ var staticIPs []net.IP
+ if o.StaticIPs == nil {
+ return staticIPs
+ }
+ return *o.StaticIPs
+}
+
// WithConfigMaps
func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions {
v := &value
diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go
index ef20235ae..b53fc4bd3 100644
--- a/pkg/bindings/test/networks_test.go
+++ b/pkg/bindings/test/networks_test.go
@@ -2,10 +2,12 @@ package test_bindings
import (
"context"
+ "fmt"
"net/http"
"time"
"github.com/containers/podman/v3/pkg/bindings"
+ "github.com/containers/podman/v3/pkg/bindings/containers"
"github.com/containers/podman/v3/pkg/bindings/network"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -37,6 +39,53 @@ var _ = Describe("Podman networks", func() {
bt.cleanup()
})
+ It("podman prune unused networks with filters", func() {
+ name := "foobar"
+ opts := network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+
+ // Invalid filters should return error
+ filtersIncorrect := map[string][]string{
+ "status": {"dummy"},
+ }
+ _, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).ToNot(BeNil())
+
+ // List filter params should not work with prune.
+ filtersIncorrect = map[string][]string{
+ "name": {name},
+ }
+ _, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).ToNot(BeNil())
+
+ // Mismatched label, correct filter params => no network should be pruned.
+ filtersIncorrect = map[string][]string{
+ "label": {"xyz"},
+ }
+ pruneResponse, err := network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(0))
+
+ // Mismatched until, correct filter params => no network should be pruned.
+ filters := map[string][]string{
+ "until": {"50"}, // January 1, 1970
+ }
+ pruneResponse, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filters))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(0))
+
+ // Valid filter params => network should be pruned now.
+ filters = map[string][]string{
+ "until": {"5000000000"}, //June 11, 2128
+ }
+ pruneResponse, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filters))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(1))
+ })
+
It("create network", func() {
// create a network with blank config should work
_, err = network.Create(connText, &network.CreateOptions{})
@@ -69,4 +118,88 @@ var _ = Describe("Podman networks", func() {
Expect(err).To(BeNil())
Expect(data[0]["name"]).To(Equal(name))
})
+
+ It("list networks", func() {
+ // create a bunch of named networks and make verify with list
+ netNames := []string{"homer", "bart", "lisa", "maggie", "marge"}
+ for i := 0; i < 5; i++ {
+ opts := network.CreateOptions{
+ Name: &netNames[i],
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+ }
+ list, err := network.List(connText, nil)
+ Expect(err).To(BeNil())
+ Expect(len(list)).To(BeNumerically(">=", 5))
+ for _, n := range list {
+ if n.Name != "podman" {
+ Expect(StringInSlice(n.Name, netNames)).To(BeTrue())
+ }
+ }
+
+ // list with bad filter should be 500
+ filters := make(map[string][]string)
+ filters["foobar"] = []string{"1234"}
+ options := new(network.ListOptions).WithFilters(filters)
+ _, err = network.List(connText, options)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+
+ // filter list with success
+ filters = make(map[string][]string)
+ filters["name"] = []string{"homer"}
+ options = new(network.ListOptions).WithFilters(filters)
+ list, err = network.List(connText, options)
+ Expect(err).To(BeNil())
+ Expect(len(list)).To(BeNumerically("==", 1))
+ Expect(list[0].Name).To(Equal("homer"))
+ })
+
+ It("remove network", func() {
+ // removing a noName network should result in 404
+ _, err := network.Remove(connText, "noName", nil)
+ code, err := bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Removing an unused network should work
+ name := "unused"
+ opts := network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+ report, err := network.Remove(connText, name, nil)
+ Expect(err).To(BeNil())
+ Expect(report[0].Name).To(Equal(name))
+
+ // Removing a network that is being used without force should be 500
+ name = "used"
+ opts = network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+
+ // Start container and wait
+ container := "ntest"
+ session := bt.runPodman([]string{"run", "-dt", fmt.Sprintf("--network=%s", name), "--name", container, alpine.name, "top"})
+ session.Wait(45)
+ Expect(session.ExitCode()).To(BeZero())
+
+ _, err = network.Remove(connText, name, nil)
+ code, err = bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+
+ // Removing with a network in use with force should work with a stopped container
+ err = containers.Stop(connText, container, new(containers.StopOptions).WithTimeout(0))
+ Expect(err).To(BeNil())
+ options := new(network.RemoveOptions).WithForce(true)
+ report, err = network.Remove(connText, name, options)
+ Expect(err).To(BeNil())
+ Expect(report[0].Name).To(Equal(name))
+ })
})
diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go
index 91f6444cc..14bda114e 100644
--- a/pkg/bindings/test/volumes_test.go
+++ b/pkg/bindings/test/volumes_test.go
@@ -83,7 +83,8 @@ var _ = Describe("Podman volumes", func() {
It("remove volume", func() {
// removing a bogus volume should result in 404
err := volumes.Remove(connText, "foobar", nil)
- code, _ := bindings.CheckResponseCode(err)
+ code, err := bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Removing an unused volume should work
@@ -97,9 +98,12 @@ var _ = Describe("Podman volumes", func() {
Expect(err).To(BeNil())
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/foobar", vol.Name), "--name", "vtest", alpine.name, "top"})
session.Wait(45)
+ Expect(session.ExitCode()).To(BeZero())
+
err = volumes.Remove(connText, vol.Name, nil)
Expect(err).ToNot(BeNil())
- code, _ = bindings.CheckResponseCode(err)
+ code, err = bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
Expect(code).To(BeNumerically("==", http.StatusConflict))
// Removing with a volume in use with force should work with a stopped container
diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go
index 608e1647a..aefb5183b 100644
--- a/pkg/cgroups/cgroups.go
+++ b/pkg/cgroups/cgroups.go
@@ -265,7 +265,7 @@ func readFileAsUint64(path string) (uint64, error) {
if v == "max" {
return math.MaxUint64, nil
}
- ret, err := strconv.ParseUint(v, 10, 0)
+ ret, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return ret, errors.Wrapf(err, "parse %s from %s", v, path)
}
diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go
index 05223c2e1..23539757d 100644
--- a/pkg/cgroups/cpu.go
+++ b/pkg/cgroups/cpu.go
@@ -40,7 +40,7 @@ func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) {
if s == "" {
break
}
- v, err := strconv.ParseUint(s, 10, 0)
+ v, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return nil, errors.Wrapf(err, "parsing %s", s)
}
@@ -80,14 +80,14 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error {
return err
}
if val, found := values["usage_usec"]; found {
- usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
+ usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
if err != nil {
return err
}
usage.Kernel *= 1000
}
if val, found := values["system_usec"]; found {
- usage.Kernel, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
+ usage.Kernel, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
if err != nil {
return err
}
@@ -149,7 +149,7 @@ func GetSystemCPUUsage() (uint64, error) {
}
if val, found := values["usage_usec"]; found {
- v, err := strconv.ParseUint(cleanString(val[0]), 10, 0)
+ v, err := strconv.ParseUint(cleanString(val[0]), 10, 64)
if err != nil {
return 0, err
}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index bcab617af..f695d32fd 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -88,7 +88,7 @@ type ContainerEngine interface {
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
Shutdown(ctx context.Context)
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
- Unshare(ctx context.Context, args []string) error
+ Unshare(ctx context.Context, args []string, options SystemUnshareOptions) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeExists(ctx context.Context, namesOrID string) (*BoolReport, error)
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
index 6883fe6c5..c69bb0867 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -1,6 +1,10 @@
package entities
-import "github.com/containers/image/v5/types"
+import (
+ "net"
+
+ "github.com/containers/image/v5/types"
+)
// PlayKubeOptions controls playing kube YAML files.
type PlayKubeOptions struct {
@@ -24,6 +28,8 @@ type PlayKubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs []net.IP
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
ConfigMaps []string
// LogDriver for the container. For example: journald
@@ -45,8 +51,16 @@ type PlayKubePod struct {
ContainerErrors []string
}
+// PlayKubeVolume represents a single volume created by play kube.
+type PlayKubeVolume struct {
+ // Name - Name of the volume created by play kube.
+ Name string
+}
+
// PlayKubeReport contains the results of running play kube.
type PlayKubeReport struct {
// Pods - pods created by play kube.
Pods []PlayKubePod
+ // Volumes - volumes created by play kube.
+ Volumes []PlayKubeVolume
}
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index 1a671d59e..31a6185dc 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -98,6 +98,11 @@ type SystemVersionReport struct {
Server *define.Version `json:",omitempty"`
}
+// SystemUnshareOptions describes the options for the unshare command
+type SystemUnshareOptions struct {
+ RootlessCNI bool
+}
+
type ComponentVersion struct {
types.Version
}
diff --git a/pkg/domain/entities/types/auth.go b/pkg/domain/entities/types/auth.go
index ddf15bb18..7f2480173 100644
--- a/pkg/domain/entities/types/auth.go
+++ b/pkg/domain/entities/types/auth.go
@@ -1,4 +1,5 @@
-package types // import "github.com/docker/docker/api/types"
+// copied from github.com/docker/docker/api/types
+package types
// AuthConfig contains authorization information for connecting to a Registry
type AuthConfig struct {
diff --git a/pkg/domain/entities/types/types.go b/pkg/domain/entities/types/types.go
index 77834c0cb..7dc785078 100644
--- a/pkg/domain/entities/types/types.go
+++ b/pkg/domain/entities/types/types.go
@@ -1,4 +1,5 @@
-package types // import "github.com/docker/docker/api/types"
+// copied from github.com/docker/docker/api/types
+package types
// ComponentVersion describes the version information for a specific component.
type ComponentVersion struct {
diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go
index 9b2fc5280..9a08adf82 100644
--- a/pkg/domain/filters/volumes.go
+++ b/pkg/domain/filters/volumes.go
@@ -75,7 +75,25 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
return dangling
})
default:
- return nil, errors.Errorf("%q is in an invalid volume filter", filter)
+ return nil, errors.Errorf("%q is an invalid volume filter", filter)
+ }
+ }
+ }
+ return vf, nil
+}
+
+func GeneratePruneVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
+ var vf []libpod.VolumeFilter
+ for filter, v := range filters {
+ for _, val := range v {
+ switch filter {
+ case "label":
+ filter := val
+ vf = append(vf, func(v *libpod.Volume) bool {
+ return util.MatchLabelFilters([]string{filter}, v.Labels())
+ })
+ default:
+ return nil, errors.Errorf("%q is an invalid volume filter", filter)
}
}
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 24261e5ed..6f8845f10 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -585,7 +585,7 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string,
}
// If the container is in a pod, also set to recursively start dependencies
- err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "")
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false)
if err != nil && errors.Cause(err) != define.ErrDetach {
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
}
@@ -708,7 +708,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
ctrRunning := ctrState == define.ContainerStateRunning
if options.Attach {
- err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "")
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning)
if errors.Cause(err) == define.ErrDetach {
// User manually detached
// Exit cleanly immediately
@@ -784,7 +784,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
RawInput: rawInput,
ExitCode: 125,
}
- if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
+ if err := ctr.Start(ctx, true); err != nil {
// if lastError != nil {
// fmt.Fprintln(os.Stderr, lastError)
// }
@@ -845,10 +845,6 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
}
- var joinPod bool
- if len(ctr.PodID()) > 0 {
- joinPod = true
- }
report := entities.ContainerRunReport{Id: ctr.ID()}
if logrus.GetLevel() == logrus.DebugLevel {
@@ -859,7 +855,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
if opts.Detach {
// if the container was created as part of a pod, also start its dependencies, if any.
- if err := ctr.Start(ctx, joinPod); err != nil {
+ if err := ctr.Start(ctx, true); err != nil {
// This means the command did not exist
report.ExitCode = define.ExitCode(err)
return &report, err
@@ -869,7 +865,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
// if the container was created as part of a pod, also start its dependencies, if any.
- if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil {
+ if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true); err != nil {
// We've manually detached from the container
// Do not perform cleanup, or wait for container exit code
// Just exit immediately
diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go
index 8de383926..2cabab988 100644
--- a/pkg/domain/infra/abi/containers_runlabel.go
+++ b/pkg/domain/infra/abi/containers_runlabel.go
@@ -177,6 +177,16 @@ func generateRunlabelCommand(runlabel string, img *image.Image, args []string, o
return cmd, env, nil
}
+func replaceName(arg, name string) string {
+ newarg := strings.ReplaceAll(arg, "$NAME", name)
+ return strings.ReplaceAll(newarg, "${NAME}", name)
+}
+
+func replaceImage(arg, image string) string {
+ newarg := strings.ReplaceAll(arg, "$IMAGE", image)
+ return strings.ReplaceAll(newarg, "${IMAGE}", image)
+}
+
// generateCommand takes a label (string) and converts it to an executable command
func generateCommand(command, imageName, name, globalOpts string) ([]string, error) {
if name == "" {
@@ -196,26 +206,15 @@ func generateCommand(command, imageName, name, globalOpts string) ([]string, err
for _, arg := range cmd[1:] {
var newArg string
switch arg {
- case "IMAGE":
- newArg = imageName
- case "$IMAGE":
- newArg = imageName
case "IMAGE=IMAGE":
newArg = fmt.Sprintf("IMAGE=%s", imageName)
- case "IMAGE=$IMAGE":
- newArg = fmt.Sprintf("IMAGE=%s", imageName)
- case "NAME":
- newArg = name
case "NAME=NAME":
newArg = fmt.Sprintf("NAME=%s", name)
- case "NAME=$NAME":
- newArg = fmt.Sprintf("NAME=%s", name)
- case "$NAME":
- newArg = name
case "$GLOBAL_OPTS":
newArg = globalOpts
default:
- newArg = arg
+ newArg = replaceName(arg, name)
+ newArg = replaceImage(newArg, imageName)
}
newCommand = append(newCommand, newArg)
}
diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go
index 94f649e15..b0853b554 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
+ "strings"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
@@ -43,120 +44,174 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
var (
- pods []*libpod.Pod
- ctrs []*libpod.Container
- kubePods []*k8sAPI.Pod
- kubeServices []k8sAPI.Service
- content []byte
+ pods []*libpod.Pod
+ ctrs []*libpod.Container
+ vols []*libpod.Volume
+ podContent [][]byte
+ content [][]byte
)
+
+ // Lookup for podman objects.
for _, nameOrID := range nameOrIDs {
- // Get the container in question
+ // Let's assume it's a container, so get the container.
ctr, err := ic.Libpod.LookupContainer(nameOrID)
if err != nil {
- pod, err := ic.Libpod.LookupPod(nameOrID)
- if err != nil {
+ if !strings.Contains(err.Error(), "no such container") {
return nil, err
}
- pods = append(pods, pod)
} else {
if len(ctr.Dependencies()) > 0 {
return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
}
- // we cannot deal with ctrs already in a pod
+ // we cannot deal with ctrs already in a pod.
if len(ctr.PodID()) > 0 {
return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID())
}
ctrs = append(ctrs, ctr)
+ continue
+ }
+
+ // Maybe it's a pod.
+ pod, err := ic.Libpod.LookupPod(nameOrID)
+ if err != nil {
+ if !strings.Contains(err.Error(), "no such pod") {
+ return nil, err
+ }
+ } else {
+ pods = append(pods, pod)
+ continue
+ }
+
+ // Or volume.
+ vol, err := ic.Libpod.LookupVolume(nameOrID)
+ if err != nil {
+ if !strings.Contains(err.Error(), "no such volume") {
+ return nil, err
+ }
+ } else {
+ vols = append(vols, vol)
+ continue
}
+
+ // If it reaches here is because the name or id did not exist.
+ return nil, errors.Errorf("Name or ID %q not found", nameOrID)
}
- // check our inputs
- if len(pods) > 0 && len(ctrs) > 0 {
- return nil, errors.New("cannot generate pods and containers at the same time")
+ // Generate kube persistent volume claims from volumes.
+ if len(vols) >= 1 {
+ pvs, err := getKubePVCs(vols)
+ if err != nil {
+ return nil, err
+ }
+
+ content = append(content, pvs...)
}
+ // Generate kube pods and services from pods.
if len(pods) >= 1 {
pos, svcs, err := getKubePods(pods, options.Service)
if err != nil {
return nil, err
}
- kubePods = append(kubePods, pos...)
+ podContent = append(podContent, pos...)
if options.Service {
- kubeServices = append(kubeServices, svcs...)
+ content = append(content, svcs...)
}
- } else {
+ }
+
+ // Generate the kube pods from containers.
+ if len(ctrs) >= 1 {
po, err := libpod.GenerateForKube(ctrs)
if err != nil {
return nil, err
}
- kubePods = append(kubePods, po)
+ b, err := generateKubeYAML(po)
+ if err != nil {
+ return nil, err
+ }
+
+ podContent = append(podContent, b)
if options.Service {
- kubeServices = append(kubeServices, libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}))
+ b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}))
+ if err != nil {
+ return nil, err
+ }
+ content = append(content, b)
}
}
- content, err := generateKubeOutput(kubePods, kubeServices, options.Service)
+ // Content order is based on helm install order (secret, persistentVolumeClaim, service, pod).
+ content = append(content, podContent...)
+
+ // Generate kube YAML file from all kube kinds.
+ k, err := generateKubeOutput(content)
if err != nil {
return nil, err
}
- return &entities.GenerateKubeReport{Reader: bytes.NewReader(content)}, nil
+ return &entities.GenerateKubeReport{Reader: bytes.NewReader(k)}, nil
}
-func getKubePods(pods []*libpod.Pod, getService bool) ([]*k8sAPI.Pod, []k8sAPI.Service, error) {
- kubePods := make([]*k8sAPI.Pod, 0)
- kubeServices := make([]k8sAPI.Service, 0)
+// getKubePods returns kube pod and service YAML files from podman pods.
+func getKubePods(pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) {
+ pos := [][]byte{}
+ svcs := [][]byte{}
for _, p := range pods {
- po, svc, err := p.GenerateForKube()
+ po, sp, err := p.GenerateForKube()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ b, err := generateKubeYAML(po)
if err != nil {
return nil, nil, err
}
+ pos = append(pos, b)
- kubePods = append(kubePods, po)
if getService {
- kubeServices = append(kubeServices, libpod.GenerateKubeServiceFromV1Pod(po, svc))
+ b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, sp))
+ if err != nil {
+ return nil, nil, err
+ }
+ svcs = append(svcs, b)
}
}
- return kubePods, kubeServices, nil
+ return pos, svcs, nil
}
-func generateKubeOutput(kubePods []*k8sAPI.Pod, kubeServices []k8sAPI.Service, hasService bool) ([]byte, error) {
- output := make([]byte, 0)
- marshalledPods := make([]byte, 0)
- marshalledServices := make([]byte, 0)
+// getKubePVCs returns kube persistent volume claim YAML files from podman volumes.
+func getKubePVCs(volumes []*libpod.Volume) ([][]byte, error) {
+ pvs := [][]byte{}
- for i, p := range kubePods {
- if i != 0 {
- marshalledPods = append(marshalledPods, []byte("---\n")...)
- }
-
- b, err := yaml.Marshal(p)
+ for _, v := range volumes {
+ b, err := generateKubeYAML(v.GenerateForKube())
if err != nil {
return nil, err
}
-
- marshalledPods = append(marshalledPods, b...)
+ pvs = append(pvs, b)
}
- if hasService {
- for i, s := range kubeServices {
- if i != 0 {
- marshalledServices = append(marshalledServices, []byte("---\n")...)
- }
-
- b, err := yaml.Marshal(s)
- if err != nil {
- return nil, err
- }
+ return pvs, nil
+}
- marshalledServices = append(marshalledServices, b...)
- }
+// generateKubeYAML marshalls a kube kind into a YAML file.
+func generateKubeYAML(kubeKind interface{}) ([]byte, error) {
+ b, err := yaml.Marshal(kubeKind)
+ if err != nil {
+ return nil, err
}
+ return b, nil
+}
+
+// generateKubeOutput generates kube YAML file containing multiple kube kinds.
+func generateKubeOutput(content [][]byte) ([]byte, error) {
+ output := make([]byte, 0)
+
header := `# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
@@ -169,13 +224,18 @@ func generateKubeOutput(kubePods []*k8sAPI.Pod, kubeServices []k8sAPI.Service, h
return nil, err
}
+ // Add header to kube YAML file.
output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...)
- // kube generate order is based on helm install order (service, pod...)
- if hasService {
- output = append(output, marshalledServices...)
- output = append(output, []byte("---\n")...)
+
+ // kube generate order is based on helm install order (secret, persistentVolume, service, pod...).
+ // Add kube kinds.
+ for i, b := range content {
+ if i != 0 {
+ output = append(output, []byte("---\n")...)
+ }
+
+ output = append(output, b...)
}
- output = append(output, marshalledPods...)
return output, nil
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 3b5c141d7..4a13a8029 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"os"
+ "strconv"
"strings"
"github.com/containers/common/pkg/secrets"
@@ -15,6 +16,7 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
"github.com/containers/podman/v3/pkg/util"
@@ -43,6 +45,14 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, err
}
+ // sort kube kinds
+ documentList, err = sortKubeKinds(documentList)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
+ }
+
+ ipIndex := 0
+
// create pod on each document if it is a pod or deployment
// any other kube kind will be skipped
for _, document := range documentList {
@@ -63,7 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec
- r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options)
+ r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex)
if err != nil {
return nil, err
}
@@ -77,13 +87,27 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
}
- r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options)
+ r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex)
if err != nil {
return nil, err
}
report.Pods = append(report.Pods, r.Pods...)
validKinds++
+ case "PersistentVolumeClaim":
+ var pvcYAML v1.PersistentVolumeClaim
+
+ if err := yaml.Unmarshal(document, &pvcYAML); err != nil {
+ return nil, errors.Wrapf(err, "unable to read YAML %q as Kube PersistentVolumeClaim", path)
+ }
+
+ r, err := ic.playKubePVC(ctx, &pvcYAML, options)
+ if err != nil {
+ return nil, err
+ }
+
+ report.Volumes = append(report.Volumes, r.Volumes...)
+ validKinds++
default:
logrus.Infof("kube kind %s not supported", kind)
continue
@@ -97,7 +121,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return report, nil
}
-func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
deploymentName string
podSpec v1.PodTemplateSpec
@@ -119,7 +143,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
// create "replicas" number of pods
for i = 0; i < numReplicas; i++ {
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
- podReport, err := ic.playKubePod(ctx, podName, &podSpec, options)
+ podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex)
if err != nil {
return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
}
@@ -128,7 +152,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
return &report, nil
}
-func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
registryCreds *types.DockerAuthConfig
writer io.Writer
@@ -169,9 +193,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// networks.
networks := strings.Split(options.Network, ",")
logrus.Debugf("Pod joining CNI networks: %v", networks)
+ p.NetNS.NSMode = specgen.Bridge
p.CNINetworks = append(p.CNINetworks, networks...)
}
}
+ if len(options.StaticIPs) > *ipIndex {
+ p.StaticIP = &options.StaticIPs[*ipIndex]
+ *ipIndex++
+ } else if len(options.StaticIPs) > 0 {
+ // only warn if the user has set at least one ip ip
+ logrus.Warn("No more static ips left using a random one")
+ }
// Create the Pod
pod, err := generate.MakePod(p, ic.Libpod)
@@ -279,6 +311,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
RestartPolicy: ctrRestartPolicy,
NetNSIsHost: p.NetNS.IsHost(),
SecretsManager: secretsManager,
+ LogDriver: options.LogDriver,
}
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
if err != nil {
@@ -313,6 +346,68 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return &report, nil
}
+// playKubePVC creates a podman volume from a kube persistent volume claim.
+func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.PersistentVolumeClaim, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+ var report entities.PlayKubeReport
+ opts := make(map[string]string)
+
+ // Get pvc name.
+ // This is the only required pvc attribute to create a podman volume.
+ name := pvcYAML.GetName()
+ if strings.TrimSpace(name) == "" {
+ return nil, fmt.Errorf("persistent volume claim name can not be empty")
+ }
+
+ // Create podman volume options.
+ volOptions := []libpod.VolumeCreateOption{
+ libpod.WithVolumeName(name),
+ libpod.WithVolumeLabels(pvcYAML.GetLabels()),
+ }
+
+ // Get pvc annotations and create remaining podman volume options if available.
+ // These are podman volume options that do not match any of the persistent volume claim
+ // attributes, so they can be configured using annotations since they will not affect k8s.
+ for k, v := range pvcYAML.GetAnnotations() {
+ switch k {
+ case util.VolumeDriverAnnotation:
+ volOptions = append(volOptions, libpod.WithVolumeDriver(v))
+ case util.VolumeDeviceAnnotation:
+ opts["device"] = v
+ case util.VolumeTypeAnnotation:
+ opts["type"] = v
+ case util.VolumeUIDAnnotation:
+ uid, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert uid %s to integer", v)
+ }
+ volOptions = append(volOptions, libpod.WithVolumeUID(uid))
+ opts["UID"] = v
+ case util.VolumeGIDAnnotation:
+ gid, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert gid %s to integer", v)
+ }
+ volOptions = append(volOptions, libpod.WithVolumeGID(gid))
+ opts["GID"] = v
+ case util.VolumeMountOptsAnnotation:
+ opts["o"] = v
+ }
+ }
+ volOptions = append(volOptions, libpod.WithVolumeOptions(opts))
+
+ // Create volume.
+ vol, err := ic.Libpod.NewVolume(ctx, volOptions...)
+ if err != nil {
+ return nil, err
+ }
+
+ report.Volumes = append(report.Volumes, entities.PlayKubeVolume{
+ Name: vol.Name(),
+ })
+
+ return &report, nil
+}
+
// readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag
func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) {
var cm v1.ConfigMap
@@ -374,3 +469,25 @@ func getKubeKind(obj []byte) (string, error) {
return kubeObject.Kind, nil
}
+
+// sortKubeKinds adds the correct creation order for the kube kinds.
+// Any pod dependecy will be created first like volumes, secrets, etc.
+func sortKubeKinds(documentList [][]byte) ([][]byte, error) {
+ var sortedDocumentList [][]byte
+
+ for _, document := range documentList {
+ kind, err := getKubeKind(document)
+ if err != nil {
+ return nil, err
+ }
+
+ switch kind {
+ case "Pod", "Deployment":
+ sortedDocumentList = append(sortedDocumentList, document)
+ default:
+ sortedDocumentList = append([][]byte{document}, sortedDocumentList...)
+ }
+ }
+
+ return sortedDocumentList, nil
+}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index a3e753384..f87f9e370 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -390,13 +390,25 @@ func unshareEnv(graphroot, runroot string) []string {
fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
+ unshare := func() error {
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+ }
+
+ if options.RootlessCNI {
+ rootlesscni, err := ic.Libpod.GetRootlessCNINetNs(true)
+ if err != nil {
+ return err
+ }
+ defer rootlesscni.Cleanup(ic.Libpod)
+ return rootlesscni.Do(unshare)
+ }
+ return unshare()
}
func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 7a0c2907c..ab71f8f6f 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -39,7 +39,7 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo
// StartAttachCtr starts and (if required) attaches to a container
// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
// error. we may need to just lint disable this one.
-func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
+func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint-interfacer
resize := make(chan define.TerminalSize)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
@@ -88,7 +88,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
return ctr.Attach(streams, detachKeys, resize)
}
- attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
+ attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, true)
if err != nil {
return err
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index a0f65f11f..4545d266b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -629,7 +629,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
if opts.Detach {
// Detach and return early
- err := containers.Start(ic.ClientCtx, con.ID, nil)
+ err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true))
if err != nil {
report.ExitCode = define.ExitCode(err)
}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index adf34460c..7e59e44c2 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -92,5 +92,6 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
// Network prune removes unused cni networks
func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
- return network.Prune(ic.ClientCtx, nil)
+ opts := new(network.PruneOptions).WithFilters(options.Filters)
+ return network.Prune(ic.ClientCtx, opts)
}
diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go
index 9f9076114..e52e1a1f7 100644
--- a/pkg/domain/infra/tunnel/play.go
+++ b/pkg/domain/infra/tunnel/play.go
@@ -11,7 +11,7 @@ import (
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
- options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot)
+ options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot).WithStaticIPs(opts.StaticIPs)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index d2c5063c9..7400d3771 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -28,7 +28,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
return system.DiskUsage(ic.ClientCtx, nil)
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
return errors.New("unshare is not supported on remote clients")
}
diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go
index b1923be98..9dc545ebb 100644
--- a/pkg/errorhandling/errorhandling.go
+++ b/pkg/errorhandling/errorhandling.go
@@ -24,6 +24,9 @@ func JoinErrors(errs []error) error {
if finalErr == nil {
return finalErr
}
+ if len(multiE.WrappedErrors()) == 1 && logrus.IsLevelEnabled(logrus.TraceLevel) {
+ return multiE.WrappedErrors()[0]
+ }
return errors.New(strings.TrimSpace(finalErr.Error()))
}
diff --git a/pkg/registrar/registrar.go b/pkg/registrar/registrar.go
deleted file mode 100644
index 1e75ee995..000000000
--- a/pkg/registrar/registrar.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Package registrar provides name registration. It reserves a name to a given key.
-package registrar
-
-import (
- "errors"
- "sync"
-)
-
-var (
- // ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
- ErrNameReserved = errors.New("name is reserved")
- // ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
- ErrNameNotReserved = errors.New("name is not reserved")
- // ErrNoSuchKey is returned when trying to find the names for a key which is not known
- ErrNoSuchKey = errors.New("provided key does not exist")
-)
-
-// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registered to
-// Names must be unique.
-// Registrar is safe for concurrent access.
-type Registrar struct {
- idx map[string][]string
- names map[string]string
- mu sync.Mutex
-}
-
-// NewRegistrar creates a new Registrar with the an empty index
-func NewRegistrar() *Registrar {
- return &Registrar{
- idx: make(map[string][]string),
- names: make(map[string]string),
- }
-}
-
-// Reserve registers a key to a name
-// Reserve is idempotent
-// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved`
-// A name reservation is globally unique
-func (r *Registrar) Reserve(name, key string) error {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- if k, exists := r.names[name]; exists {
- if k != key {
- return ErrNameReserved
- }
- return nil
- }
-
- r.idx[key] = append(r.idx[key], name)
- r.names[name] = key
- return nil
-}
-
-// Release releases the reserved name
-// Once released, a name can be reserved again
-func (r *Registrar) Release(name string) {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- key, exists := r.names[name]
- if !exists {
- return
- }
-
- for i, n := range r.idx[key] {
- if n != name {
- continue
- }
- r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...)
- break
- }
-
- delete(r.names, name)
-
- if len(r.idx[key]) == 0 {
- delete(r.idx, key)
- }
-}
-
-// Delete removes all reservations for the passed in key.
-// All names reserved to this key are released.
-func (r *Registrar) Delete(key string) {
- r.mu.Lock()
- for _, name := range r.idx[key] {
- delete(r.names, name)
- }
- delete(r.idx, key)
- r.mu.Unlock()
-}
-
-// GetNames lists all the reserved names for the given key
-func (r *Registrar) GetNames(key string) ([]string, error) {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- names, exists := r.idx[key]
- if !exists {
- return nil, ErrNoSuchKey
- }
- return names, nil
-}
-
-// Get returns the key that the passed in name is reserved to
-func (r *Registrar) Get(name string) (string, error) {
- r.mu.Lock()
- key, exists := r.names[name]
- r.mu.Unlock()
-
- if !exists {
- return "", ErrNameNotReserved
- }
- return key, nil
-}
-
-// GetAll returns all registered names
-func (r *Registrar) GetAll() map[string][]string {
- out := make(map[string][]string)
-
- r.mu.Lock()
- // copy index into out
- for id, names := range r.idx {
- out[id] = names
- }
- r.mu.Unlock()
- return out
-}
diff --git a/pkg/registrar/registrar_test.go b/pkg/registrar/registrar_test.go
deleted file mode 100644
index dc9942e80..000000000
--- a/pkg/registrar/registrar_test.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package registrar_test
-
-import (
- "testing"
-
- "github.com/containers/podman/v3/pkg/registrar"
- . "github.com/containers/podman/v3/test/framework"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-)
-
-// TestRegistrar runs the created specs
-func TestRegistrar(t *testing.T) {
- RegisterFailHandler(Fail)
- RunSpecs(t, "Registrar")
-}
-
-// nolint: gochecknoglobals
-var t *TestFramework
-
-var _ = BeforeSuite(func() {
- t = NewTestFramework(NilFunc, NilFunc)
- t.Setup()
-})
-
-var _ = AfterSuite(func() {
- t.Teardown()
-})
-
-// The actual test suite
-var _ = t.Describe("Registrar", func() {
- // Constant test data needed by some tests
- const (
- testKey = "testKey"
- testName = "testName"
- anotherKey = "anotherKey"
- )
-
- // The system under test
- var sut *registrar.Registrar
-
- // Prepare the system under test and register a test name and key before
- // each test
- BeforeEach(func() {
- sut = registrar.NewRegistrar()
- Expect(sut.Reserve(testName, testKey)).To(BeNil())
- })
-
- t.Describe("Reserve", func() {
- It("should succeed to reserve a new registrar", func() {
- // Given
- // When
- err := sut.Reserve("name", "key")
-
- // Then
- Expect(err).To(BeNil())
- })
-
- It("should succeed to reserve a registrar twice", func() {
- // Given
- // When
- err := sut.Reserve(testName, testKey)
-
- // Then
- Expect(err).To(BeNil())
- })
-
- It("should fail to reserve an already reserved registrar", func() {
- // Given
- // When
- err := sut.Reserve(testName, anotherKey)
-
- // Then
- Expect(err).NotTo(BeNil())
- Expect(err).To(Equal(registrar.ErrNameReserved))
- })
- })
-
- t.Describe("Release", func() {
- It("should succeed to release a registered registrar multiple times", func() {
- // Given
- // When
- // Then
- sut.Release(testName)
- sut.Release(testName)
- })
-
- It("should succeed to release a unknown registrar multiple times", func() {
- // Given
- // When
- // Then
- sut.Release(anotherKey)
- sut.Release(anotherKey)
- })
-
- It("should succeed to release and re-register a registrar", func() {
- // Given
- // When
- sut.Release(testName)
- err := sut.Reserve(testName, testKey)
-
- // Then
- Expect(err).To(BeNil())
- })
- })
-
- t.Describe("GetNames", func() {
- It("should succeed to retrieve a single name for a registrar", func() {
- // Given
- // When
- names, err := sut.GetNames(testKey)
-
- // Then
- Expect(err).To(BeNil())
- Expect(len(names)).To(Equal(1))
- Expect(names[0]).To(Equal(testName))
- })
-
- It("should succeed to retrieve all names for a registrar", func() {
- // Given
- testNames := []string{"test1", "test2"}
- for _, name := range testNames {
- Expect(sut.Reserve(name, anotherKey)).To(BeNil())
- }
-
- // When
- names, err := sut.GetNames(anotherKey)
-
- // Then
- Expect(err).To(BeNil())
- Expect(len(names)).To(Equal(2))
- Expect(names).To(Equal(testNames))
- })
- })
-
- t.Describe("GetNames", func() {
- It("should succeed to retrieve a single name for a registrar", func() {
- // Given
- // When
- names, err := sut.GetNames(testKey)
-
- // Then
- Expect(err).To(BeNil())
- Expect(len(names)).To(Equal(1))
- Expect(names[0]).To(Equal(testName))
- })
-
- It("should succeed to retrieve all names for a registrar", func() {
- // Given
- anotherKey := "anotherKey"
- testNames := []string{"test1", "test2"}
- for _, name := range testNames {
- Expect(sut.Reserve(name, anotherKey)).To(BeNil())
- }
-
- // When
- names, err := sut.GetNames(anotherKey)
-
- // Then
- Expect(err).To(BeNil())
- Expect(len(names)).To(Equal(2))
- Expect(names).To(Equal(testNames))
- })
- })
-
- t.Describe("Delete", func() {
- It("should succeed to delete a registrar", func() {
- // Given
- // When
- sut.Delete(testKey)
-
- // Then
- names, err := sut.GetNames(testKey)
- Expect(len(names)).To(BeZero())
- Expect(err).To(Equal(registrar.ErrNoSuchKey))
- })
- })
-
- t.Describe("Get", func() {
- It("should succeed to get a key for a registrar", func() {
- // Given
- // When
- key, err := sut.Get(testName)
-
- // Then
- Expect(err).To(BeNil())
- Expect(key).To(Equal(testKey))
- })
-
- It("should fail to get a key for a not existing registrar", func() {
- // Given
- // When
- key, err := sut.Get("notExistingName")
-
- // Then
- Expect(key).To(BeEmpty())
- Expect(err).To(Equal(registrar.ErrNameNotReserved))
- })
- })
-
- t.Describe("GetAll", func() {
- It("should succeed to get all names", func() {
- // Given
- // When
- names := sut.GetAll()
-
- // Then
- Expect(len(names)).To(Equal(1))
- Expect(len(names[testKey])).To(Equal(1))
- Expect(names[testKey][0]).To(Equal(testName))
- })
- })
-})
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 1d724ffb0..ef9975021 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -364,6 +364,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if len(s.Secrets) != 0 {
options = append(options, libpod.WithSecrets(s.Secrets))
}
+ if len(s.DependencyContainers) > 0 {
+ deps := make([]*libpod.Container, 0, len(s.DependencyContainers))
+ for _, ctr := range s.DependencyContainers {
+ depCtr, err := rt.LookupContainer(ctr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr)
+ }
+ deps = append(deps, depCtr)
+ }
+ options = append(options, libpod.WithDependencyCtrs(deps))
+ }
return options, nil
}
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index 31ed3fd7c..7aeec9d41 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -98,6 +98,8 @@ type CtrSpecGenOptions struct {
NetNSIsHost bool
// SecretManager to access the secrets
SecretsManager *secrets.SecretsManager
+ // LogDriver which should be used for the container
+ LogDriver string
}
func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) {
@@ -115,6 +117,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
s.Pod = opts.PodID
+ s.LogConfiguration = &specgen.LogConfig{
+ Driver: opts.LogDriver,
+ }
+
setupSecurityContext(s, opts.Container)
// Since we prefix the container name with pod name to work-around the uniqueness requirement,
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index e135f4728..8066834f7 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -57,10 +57,13 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
}
for _, m := range s.Mounts {
- if _, ok := unifiedMounts[m.Destination]; ok {
- return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination)
+ // Ensure that mount dest is clean, so that it can be
+ // compared against named volumes and avoid duplicate mounts.
+ cleanDestination := filepath.Clean(m.Destination)
+ if _, ok := unifiedMounts[cleanDestination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination)
}
- unifiedMounts[m.Destination] = m
+ unifiedMounts[cleanDestination] = m
}
for _, m := range commonMounts {
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index c10dc5ef5..28111f96d 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -160,10 +160,17 @@ type ContainerBasicConfig struct {
// to 0, 1, 2) that will be passed to the executed process. The total FDs
// passed will be 3 + PreserveFDs.
// set tags as `json:"-"` for not supported remote
+ // Optional.
PreserveFDs uint `json:"-"`
// Timezone is the timezone inside the container.
// Local means it has the same timezone as the host machine
+ // Optional.
Timezone string `json:"timezone,omitempty"`
+ // DependencyContainers is an array of containers this container
+ // depends on. Dependency containers must be started before this
+ // container. Dependencies can be specified by name or full/partial ID.
+ // Optional.
+ DependencyContainers []string `json:"dependencyContainers,omitempty"`
}
// ContainerStorageConfig contains information on the storage configuration of a
diff --git a/pkg/util/kube.go b/pkg/util/kube.go
new file mode 100644
index 000000000..1255cdfc5
--- /dev/null
+++ b/pkg/util/kube.go
@@ -0,0 +1,16 @@
+package util
+
+const (
+ // Kube annotation for podman volume driver.
+ VolumeDriverAnnotation = "volume.podman.io/driver"
+ // Kube annotation for podman volume type.
+ VolumeTypeAnnotation = "volume.podman.io/type"
+ // Kube annotation for podman volume device.
+ VolumeDeviceAnnotation = "volume.podman.io/device"
+ // Kube annotation for podman volume UID.
+ VolumeUIDAnnotation = "volume.podman.io/uid"
+ // Kube annotation for podman volume GID.
+ VolumeGIDAnnotation = "volume.podman.io/gid"
+ // Kube annotation for podman volume mount options.
+ VolumeMountOptsAnnotation = "volume.podman.io/mount-options"
+)