summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers.go55
-rw-r--r--pkg/api/handlers/compat/images_search.go84
-rw-r--r--pkg/api/handlers/libpod/images.go86
-rw-r--r--pkg/api/handlers/libpod/images_pull.go18
-rw-r--r--pkg/api/handlers/libpod/manifests.go18
-rw-r--r--pkg/api/handlers/libpod/volumes.go19
-rw-r--r--pkg/api/handlers/swagger/swagger.go3
-rw-r--r--pkg/api/server/register_images.go8
-rw-r--r--pkg/api/server/register_manifest.go20
-rw-r--r--pkg/api/server/register_volumes.go22
-rw-r--r--pkg/api/server/server.go8
-rw-r--r--pkg/auth/auth.go8
-rw-r--r--pkg/bindings/containers/containers.go12
-rw-r--r--pkg/bindings/containers/types.go2
-rw-r--r--pkg/bindings/containers/types_remove_options.go32
-rw-r--r--pkg/bindings/images/types.go12
-rw-r--r--pkg/bindings/images/types_pull_options.go54
-rw-r--r--pkg/bindings/manifests/manifests.go13
-rw-r--r--pkg/bindings/manifests/types.go6
-rw-r--r--pkg/bindings/manifests/types_exists_options.go88
-rw-r--r--pkg/bindings/volumes/types.go6
-rw-r--r--pkg/bindings/volumes/types_exists_options.go88
-rw-r--r--pkg/bindings/volumes/volumes.go13
-rw-r--r--pkg/domain/entities/containers.go11
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/engine_image.go1
-rw-r--r--pkg/domain/entities/images.go12
-rw-r--r--pkg/domain/infra/abi/containers.go30
-rw-r--r--pkg/domain/infra/abi/images.go6
-rw-r--r--pkg/domain/infra/abi/manifest.go12
-rw-r--r--pkg/domain/infra/abi/volumes.go9
-rw-r--r--pkg/domain/infra/tunnel/containers.go8
-rw-r--r--pkg/domain/infra/tunnel/images.go4
-rw-r--r--pkg/domain/infra/tunnel/manifest.go9
-rw-r--r--pkg/domain/infra/tunnel/volumes.go11
-rw-r--r--pkg/rootlessport/rootlessport_linux.go6
-rw-r--r--pkg/specgen/generate/container.go8
-rw-r--r--pkg/systemd/generate/common.go12
-rw-r--r--pkg/systemd/generate/containers.go48
-rw-r--r--pkg/systemd/generate/containers_test.go39
-rw-r--r--pkg/systemd/generate/pods.go52
-rw-r--r--pkg/systemd/generate/pods_test.go43
42 files changed, 682 insertions, 315 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 6e1945db1..aa12afc82 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -14,7 +14,9 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/filters"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/containers/podman/v2/pkg/ps"
"github.com/containers/podman/v2/pkg/signal"
"github.com/docker/docker/api/types"
@@ -29,9 +31,11 @@ import (
func RemoveContainer(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Force bool `schema:"force"`
- Vols bool `schema:"v"`
- Link bool `schema:"link"`
+ All bool `schema:"all"`
+ Force bool `schema:"force"`
+ Ignore bool `schema:"ignore"`
+ Link bool `schema:"link"`
+ Volumes bool `schema:"v"`
}{
// override any golang type defaults
}
@@ -49,34 +53,31 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r)
- con, err := runtime.LookupContainer(name)
- if err != nil && errors.Cause(err) == define.ErrNoSuchCtr {
- // Failed to get container. If force is specified, get the container's ID
- // and evict it
- if !query.Force {
+ options := entities.RmOptions{
+ All: query.All,
+ Force: query.Force,
+ Volumes: query.Volumes,
+ Ignore: query.Ignore,
+ }
+ report, err := containerEngine.ContainerRm(r.Context(), []string{name}, options)
+ if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err)
return
}
- if _, err := runtime.EvictContainer(r.Context(), name, query.Vols); err != nil {
- if errors.Cause(err) == define.ErrNoSuchCtr {
- logrus.Debugf("Ignoring error (--allow-missing): %q", err)
- w.WriteHeader(http.StatusNoContent)
- return
- }
- logrus.Warn(errors.Wrapf(err, "failed to evict container: %q", name))
- utils.InternalServerError(w, err)
- return
- }
- w.WriteHeader(http.StatusNoContent)
+ utils.InternalServerError(w, err)
return
}
-
- if err := runtime.RemoveContainer(r.Context(), con, query.Force, query.Vols); err != nil {
- utils.InternalServerError(w, err)
+ if report[0].Err != nil {
+ utils.InternalServerError(w, report[0].Err)
return
}
+
utils.WriteResponse(w, http.StatusNoContent, nil)
}
@@ -326,6 +327,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
state.Running = true
}
+ // docker calls the configured state "created"
+ if state.Status == define.ContainerStateConfigured.String() {
+ state.Status = define.ContainerStateCreated.String()
+ }
+
formatCapabilities(inspect.HostConfig.CapDrop)
formatCapabilities(inspect.HostConfig.CapAdd)
@@ -337,6 +343,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
if err := json.Unmarshal(h, &hc); err != nil {
return nil, err
}
+
+ // k8s-file == json-file
+ if hc.LogConfig.Type == define.KubernetesLogging {
+ hc.LogConfig.Type = define.JSONLogging
+ }
g, err := json.Marshal(inspect.GraphDriver)
if err != nil {
return nil, err
diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go
index 6808cdad5..885112b31 100644
--- a/pkg/api/handlers/compat/images_search.go
+++ b/pkg/api/handlers/compat/images_search.go
@@ -1,25 +1,30 @@
package compat
import (
+ "fmt"
"net/http"
- "strconv"
"github.com/containers/image/v5/types"
- "github.com/containers/podman/v2/libpod/image"
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/auth"
- "github.com/docker/docker/api/types/registry"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
func SearchImages(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Term string `json:"term"`
Limit int `json:"limit"`
+ NoTrunc bool `json:"noTrunc"`
Filters map[string][]string `json:"filters"`
TLSVerify bool `json:"tlsVerify"`
+ ListTags bool `json:"listTags"`
}{
// This is where you can override the golang default value for one of fields
}
@@ -29,67 +34,40 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
return
}
- filter := image.SearchFilter{}
- if len(query.Filters) > 0 {
- if len(query.Filters["stars"]) > 0 {
- stars, err := strconv.Atoi(query.Filters["stars"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.Stars = stars
- }
- if len(query.Filters["is-official"]) > 0 {
- isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsOfficial = types.NewOptionalBool(isOfficial)
- }
- if len(query.Filters["is-automated"]) > 0 {
- isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsAutomated = types.NewOptionalBool(isAutomated)
- }
- }
- options := image.SearchOptions{
- Filter: filter,
- Limit: query.Limit,
- }
-
- if _, found := r.URL.Query()["tlsVerify"]; found {
- options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
- }
-
_, authfile, key, err := auth.GetCredentials(r)
if err != nil {
utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
- options.Authfile = authfile
- results, err := image.SearchImages(query.Term, options)
+ filters := []string{}
+ for key, val := range query.Filters {
+ filters = append(filters, fmt.Sprintf("%s=%s", key, val[0]))
+ }
+
+ options := entities.ImageSearchOptions{
+ Authfile: authfile,
+ Limit: query.Limit,
+ NoTrunc: query.NoTrunc,
+ ListTags: query.ListTags,
+ Filters: filters,
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+ ir := abi.ImageEngine{Libpod: runtime}
+ reports, err := ir.Search(r.Context(), query.Term, options)
if err != nil {
- utils.BadRequest(w, "term", query.Term, err)
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
-
- compatResults := make([]registry.SearchResult, 0, len(results))
- for _, result := range results {
- compatResult := registry.SearchResult{
- Name: result.Name,
- Description: result.Description,
- StarCount: result.Stars,
- IsAutomated: result.Automated == "[OK]",
- IsOfficial: result.Official == "[OK]",
+ if !utils.IsLibpodRequest(r) {
+ if len(reports) == 0 {
+ utils.ImageNotFound(w, query.Term, define.ErrNoSuchImage)
+ return
}
- compatResults = append(compatResults, compatResult)
}
- utils.WriteResponse(w, http.StatusOK, compatResults)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 97cd5a65e..3b531652f 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -589,92 +589,6 @@ func UntagImage(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusCreated, "")
}
-func SearchImages(w http.ResponseWriter, r *http.Request) {
- decoder := r.Context().Value("decoder").(*schema.Decoder)
- query := struct {
- Term string `json:"term"`
- Limit int `json:"limit"`
- NoTrunc bool `json:"noTrunc"`
- Filters map[string][]string `json:"filters"`
- TLSVerify bool `json:"tlsVerify"`
- ListTags bool `json:"listTags"`
- }{
- // This is where you can override the golang default value for one of fields
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
- return
- }
-
- filter := image.SearchFilter{}
- if len(query.Filters) > 0 {
- if len(query.Filters["stars"]) > 0 {
- stars, err := strconv.Atoi(query.Filters["stars"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.Stars = stars
- }
- if len(query.Filters["is-official"]) > 0 {
- isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsOfficial = types.NewOptionalBool(isOfficial)
- }
- if len(query.Filters["is-automated"]) > 0 {
- isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsAutomated = types.NewOptionalBool(isAutomated)
- }
- }
- options := image.SearchOptions{
- Limit: query.Limit,
- NoTrunc: query.NoTrunc,
- ListTags: query.ListTags,
- Filter: filter,
- }
-
- if _, found := r.URL.Query()["tlsVerify"]; found {
- options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
- }
-
- _, authfile, key, err := auth.GetCredentials(r)
- if err != nil {
- utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
- return
- }
- defer auth.RemoveAuthfile(authfile)
- options.Authfile = authfile
-
- searchResults, err := image.SearchImages(query.Term, options)
- if err != nil {
- utils.BadRequest(w, "term", query.Term, err)
- return
- }
- // Convert from image.SearchResults to entities.ImageSearchReport. We don't
- // want to leak any low-level packages into the remote client, which
- // requires converting.
- reports := make([]entities.ImageSearchReport, len(searchResults))
- for i := range searchResults {
- reports[i].Index = searchResults[i].Index
- reports[i].Name = searchResults[i].Name
- reports[i].Description = searchResults[i].Description
- reports[i].Stars = searchResults[i].Stars
- reports[i].Official = searchResults[i].Official
- reports[i].Automated = searchResults[i].Automated
- reports[i].Tag = searchResults[i].Tag
- }
-
- utils.WriteResponse(w, http.StatusOK, reports)
-}
-
// ImagesBatchRemove is the endpoint for batch image removal.
func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go
index bacba006d..efb15b14d 100644
--- a/pkg/api/handlers/libpod/images_pull.go
+++ b/pkg/api/handlers/libpod/images_pull.go
@@ -30,12 +30,12 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Reference string `schema:"reference"`
- OverrideOS string `schema:"overrideOS"`
- OverrideArch string `schema:"overrideArch"`
- OverrideVariant string `schema:"overrideVariant"`
- TLSVerify bool `schema:"tlsVerify"`
- AllTags bool `schema:"allTags"`
+ Reference string `schema:"reference"`
+ OS string `schema:"OS"`
+ Arch string `schema:"Arch"`
+ Variant string `schema:"Variant"`
+ TLSVerify bool `schema:"tlsVerify"`
+ AllTags bool `schema:"allTags"`
}{
TLSVerify: true,
}
@@ -83,9 +83,9 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
// Setup the registry options
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: authConf,
- OSChoice: query.OverrideOS,
- ArchitectureChoice: query.OverrideArch,
- VariantChoice: query.OverrideVariant,
+ OSChoice: query.OS,
+ ArchitectureChoice: query.Arch,
+ VariantChoice: query.Variant,
}
if _, found := r.URL.Query()["tlsVerify"]; found {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index dce861f6f..35221ecf1 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -48,6 +48,24 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
}
+// ExistsManifest check if a manifest list exists
+func ExistsManifest(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+
+ ic := abi.ImageEngine{Libpod: runtime}
+ report, err := ic.ManifestExists(r.Context(), name)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ if !report.Value {
+ utils.Error(w, "manifest not found", http.StatusNotFound, errors.New("manifest not found"))
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
+
func ManifestInspect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 38fdf1b4d..1a8759c6c 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
"github.com/containers/podman/v2/pkg/domain/filters"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/containers/podman/v2/pkg/domain/infra/abi/parse"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -203,3 +204,21 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
+
+// ExistsVolume check if a volume exists
+func ExistsVolume(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+
+ ic := abi.ContainerEngine{Libpod: runtime}
+ report, err := ic.VolumeExists(r.Context(), name)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ if !report.Value {
+ utils.Error(w, "volume not found", http.StatusNotFound, define.ErrNoSuchVolume)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go
index 22670d795..32f041dd3 100644
--- a/pkg/api/handlers/swagger/swagger.go
+++ b/pkg/api/handlers/swagger/swagger.go
@@ -1,7 +1,6 @@
package swagger
import (
- "github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers"
@@ -166,7 +165,7 @@ type swagInspectPodResponse struct {
type swagInspectVolumeResponse struct {
// in:body
Body struct {
- libpod.InspectVolumeData
+ define.InspectVolumeData
}
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 8d0c0800b..a7d350d50 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -930,15 +930,15 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: "username:password for the registry"
// type: string
// - in: query
- // name: overrideArch
+ // name: Arch
// description: Pull image for the specified architecture.
// type: string
// - in: query
- // name: overrideOS
+ // name: OS
// description: Pull image for the specified operating system.
// type: string
// - in: query
- // name: overrideVariant
+ // name: Variant
// description: Pull image for the specified variant.
// type: string
// - in: query
@@ -1019,7 +1019,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/DocsSearchResponse"
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
// swagger:operation GET /libpod/images/{name:.*}/get libpod libpodExportImage
// ---
// tags:
diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go
index 5e7d94538..eefcc396e 100644
--- a/pkg/api/server/register_manifest.go
+++ b/pkg/api/server/register_manifest.go
@@ -38,6 +38,26 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/manifests/{name}/exists manifests Exists
+ // ---
+ // summary: Exists
+ // description: Check if manifest list exists
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the manifest list
+ // produces:
+ // - application/json
+ // responses:
+ // 204:
+ // description: manifest list exists
+ // 404:
+ // $ref: '#/responses/NoSuchManifest'
+ // 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 Inspect
// ---
// summary: Inspect
diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go
index aa0f67604..68727f2e1 100644
--- a/pkg/api/server/register_volumes.go
+++ b/pkg/api/server/register_volumes.go
@@ -28,6 +28,28 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// '500':
// "$ref": "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/volumes/create"), s.APIHandler(libpod.CreateVolume)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/volumes/{name}/exists libpod libpodExistsVolume
+ // ---
+ // tags:
+ // - volumes
+ // summary: Volume exists
+ // description: Check if a volume exists
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the volume
+ // produces:
+ // - application/json
+ // responses:
+ // 204:
+ // description: volume exists
+ // 404:
+ // $ref: '#/responses/NoSuchVolume'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/volumes/{name}/exists"), s.APIHandler(libpod.ExistsVolume)).Methods(http.MethodGet)
// swagger:operation GET /libpod/volumes/json libpod libpodListVolumes
// ---
// tags:
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 046f6561c..d612041f6 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -179,15 +179,15 @@ func setupSystemd() {
func (s *APIServer) Serve() error {
setupSystemd()
- // Start the shutdown signal handler.
- if err := shutdown.Start(); err != nil {
- return err
- }
if err := shutdown.Register("server", func(sig os.Signal) error {
return s.Shutdown()
}); err != nil {
return err
}
+ // Start the shutdown signal handler.
+ if err := shutdown.Start(); err != nil {
+ return err
+ }
errChan := make(chan error, 1)
diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go
index fcbf6fe39..86d92c028 100644
--- a/pkg/auth/auth.go
+++ b/pkg/auth/auth.go
@@ -297,7 +297,9 @@ func imageAuthToDockerAuth(authConfig types.DockerAuthConfig) dockerAPITypes.Aut
func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) {
authHeader := r.Header.Get(string(XRegistryAuthHeader))
authConfig := dockerAPITypes.AuthConfig{}
- if len(authHeader) > 0 {
+ // Accept "null" and handle it as empty value for compatibility reason with Docker.
+ // Some java docker clients pass this value, e.g. this one used in Eclipse.
+ if len(authHeader) > 0 && authHeader != "null" {
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authHeader))
if err := json.NewDecoder(authJSON).Decode(&authConfig); err != nil {
return nil, err
@@ -312,7 +314,9 @@ func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error
// The header content is a map[string]DockerAuthConfigs.
func multiAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) {
authHeader := r.Header.Get(string(XRegistryAuthHeader))
- if len(authHeader) == 0 {
+ // Accept "null" and handle it as empty value for compatibility reason with Docker.
+ // Some java docker clients pass this value, e.g. this one used in Eclipse.
+ if len(authHeader) == 0 || authHeader == "null" {
return nil, nil
}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 73e4d1d3d..40fcfbded 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -71,8 +71,10 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport,
}
// Remove removes a container from local storage. The force bool designates
-// that the container should be removed forcibly (example, even it is running). The volumes
-// bool dictates that a container's volumes should also be removed.
+// that the container should be removed forcibly (example, even it is running).
+// The volumes bool dictates that a container's volumes should also be removed.
+// The All option indicates that all containers should be removed
+// The Ignore option indicates that if a container did not exist, ignore the error
func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error {
if options == nil {
options = new(RemoveOptions)
@@ -85,9 +87,15 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error
if v := options.GetVolumes(); options.Changed("Volumes") {
params.Set("v", strconv.FormatBool(v))
}
+ if all := options.GetAll(); options.Changed("All") {
+ params.Set("all", strconv.FormatBool(all))
+ }
if force := options.GetForce(); options.Changed("Force") {
params.Set("force", strconv.FormatBool(force))
}
+ if ignore := options.GetIgnore(); options.Changed("Ignore") {
+ params.Set("ignore", strconv.FormatBool(ignore))
+ }
response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID)
if err != nil {
return err
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index 43cb58a54..24604fa83 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -122,6 +122,8 @@ type PruneOptions struct {
//go:generate go run ../generator/generator.go RemoveOptions
// RemoveOptions are optional options for removing containers
type RemoveOptions struct {
+ All *bool
+ Ignore *bool
Force *bool
Volumes *bool
}
diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go
index e21fb41f7..3ef32fa03 100644
--- a/pkg/bindings/containers/types_remove_options.go
+++ b/pkg/bindings/containers/types_remove_options.go
@@ -87,6 +87,38 @@ func (o *RemoveOptions) ToParams() (url.Values, error) {
return params, nil
}
+// WithAll
+func (o *RemoveOptions) WithAll(value bool) *RemoveOptions {
+ v := &value
+ o.All = v
+ return o
+}
+
+// GetAll
+func (o *RemoveOptions) GetAll() bool {
+ var all bool
+ if o.All == nil {
+ return all
+ }
+ return *o.All
+}
+
+// WithIgnore
+func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions {
+ v := &value
+ o.Ignore = v
+ return o
+}
+
+// GetIgnore
+func (o *RemoveOptions) GetIgnore() bool {
+ var ignore bool
+ if o.Ignore == nil {
+ return ignore
+ }
+ return *o.Ignore
+}
+
// WithForce
func (o *RemoveOptions) WithForce(value bool) *RemoveOptions {
v := &value
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index f216dd073..0248f2fa6 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -171,13 +171,13 @@ type PullOptions struct {
Username *string
// Password for authenticating against the registry.
Password *string
- // OverrideArch will overwrite the local architecture for image pulls.
- OverrideArch *string
- // OverrideOS will overwrite the local operating system (OS) for image
+ // Arch will overwrite the local architecture for image pulls.
+ Arch *string
+ // OS will overwrite the local operating system (OS) for image
// pulls.
- OverrideOS *string
- // OverrideVariant will overwrite the local variant for image pulls.
- OverrideVariant *string
+ OS *string
+ // Variant will overwrite the local variant for image pulls.
+ Variant *string
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet *bool
diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go
index 5163a6341..2bdf2b66e 100644
--- a/pkg/bindings/images/types_pull_options.go
+++ b/pkg/bindings/images/types_pull_options.go
@@ -168,52 +168,52 @@ func (o *PullOptions) GetPassword() string {
return *o.Password
}
-// WithOverrideArch
-func (o *PullOptions) WithOverrideArch(value string) *PullOptions {
+// WithArch
+func (o *PullOptions) WithArch(value string) *PullOptions {
v := &value
- o.OverrideArch = v
+ o.Arch = v
return o
}
-// GetOverrideArch
-func (o *PullOptions) GetOverrideArch() string {
- var overrideArch string
- if o.OverrideArch == nil {
- return overrideArch
+// GetArch
+func (o *PullOptions) GetArch() string {
+ var arch string
+ if o.Arch == nil {
+ return arch
}
- return *o.OverrideArch
+ return *o.Arch
}
-// WithOverrideOS
-func (o *PullOptions) WithOverrideOS(value string) *PullOptions {
+// WithOS
+func (o *PullOptions) WithOS(value string) *PullOptions {
v := &value
- o.OverrideOS = v
+ o.OS = v
return o
}
-// GetOverrideOS
-func (o *PullOptions) GetOverrideOS() string {
- var overrideOS string
- if o.OverrideOS == nil {
- return overrideOS
+// GetOS
+func (o *PullOptions) GetOS() string {
+ var oS string
+ if o.OS == nil {
+ return oS
}
- return *o.OverrideOS
+ return *o.OS
}
-// WithOverrideVariant
-func (o *PullOptions) WithOverrideVariant(value string) *PullOptions {
+// WithVariant
+func (o *PullOptions) WithVariant(value string) *PullOptions {
v := &value
- o.OverrideVariant = v
+ o.Variant = v
return o
}
-// GetOverrideVariant
-func (o *PullOptions) GetOverrideVariant() string {
- var overrideVariant string
- if o.OverrideVariant == nil {
- return overrideVariant
+// GetVariant
+func (o *PullOptions) GetVariant() string {
+ var variant string
+ if o.Variant == nil {
+ return variant
}
- return *o.OverrideVariant
+ return *o.Variant
}
// WithQuiet
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go
index b6db64b02..fec9832a0 100644
--- a/pkg/bindings/manifests/manifests.go
+++ b/pkg/bindings/manifests/manifests.go
@@ -49,6 +49,19 @@ func Create(ctx context.Context, names, images []string, options *CreateOptions)
return idr.ID, response.Process(&idr)
}
+// Exists returns true if a given maifest list exists
+func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return false, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/exists", nil, nil, name)
+ if err != nil {
+ return false, err
+ }
+ return response.IsSuccess(), nil
+}
+
// Inspect returns a manifest list for a given name.
func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) {
var list manifest.Schema2List
diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go
index 7f84d69fc..fde90a865 100644
--- a/pkg/bindings/manifests/types.go
+++ b/pkg/bindings/manifests/types.go
@@ -11,6 +11,12 @@ type CreateOptions struct {
All *bool
}
+//go:generate go run ../generator/generator.go ExistsOptions
+// ExistsOptions are optional options for checking
+// if a manifest list exists
+type ExistsOptions struct {
+}
+
//go:generate go run ../generator/generator.go AddOptions
// AddOptions are optional options for adding manifests
type AddOptions struct {
diff --git a/pkg/bindings/manifests/types_exists_options.go b/pkg/bindings/manifests/types_exists_options.go
new file mode 100644
index 000000000..fd2cd3ee9
--- /dev/null
+++ b/pkg/bindings/manifests/types_exists_options.go
@@ -0,0 +1,88 @@
+package manifests
+
+import (
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+*/
+
+// Changed
+func (o *ExistsOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *ExistsOptions) 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
+ }
+ fieldName = strings.ToLower(fieldName)
+ 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.Uint, reflect.Uint64:
+ // f.Uint() is always an uint64
+ params.Set(fieldName, strconv.FormatUint(f.Uint(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ switch typ.Kind() {
+ case reflect.String:
+ sl := f.Slice(0, f.Len())
+ s, ok := sl.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)
+ iter := f.MapRange()
+ for iter.Next() {
+ lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string)
+
+ }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ }
+ }
+ return params, nil
+}
diff --git a/pkg/bindings/volumes/types.go b/pkg/bindings/volumes/types.go
index 379174e33..3fda77ddd 100644
--- a/pkg/bindings/volumes/types.go
+++ b/pkg/bindings/volumes/types.go
@@ -30,3 +30,9 @@ type RemoveOptions struct {
// Force removes the volume even if it is being used
Force *bool
}
+
+//go:generate go run ../generator/generator.go ExistsOptions
+// ExistsOptions are optional options for checking
+// if a volume exists
+type ExistsOptions struct {
+}
diff --git a/pkg/bindings/volumes/types_exists_options.go b/pkg/bindings/volumes/types_exists_options.go
new file mode 100644
index 000000000..c66586a23
--- /dev/null
+++ b/pkg/bindings/volumes/types_exists_options.go
@@ -0,0 +1,88 @@
+package volumes
+
+import (
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+*/
+
+// Changed
+func (o *ExistsOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *ExistsOptions) 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
+ }
+ fieldName = strings.ToLower(fieldName)
+ 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.Uint, reflect.Uint64:
+ // f.Uint() is always an uint64
+ params.Set(fieldName, strconv.FormatUint(f.Uint(), 10))
+ case reflect.Slice:
+ typ := reflect.TypeOf(f.Interface()).Elem()
+ switch typ.Kind() {
+ case reflect.String:
+ sl := f.Slice(0, f.Len())
+ s, ok := sl.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)
+ iter := f.MapRange()
+ for iter.Next() {
+ lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string)
+
+ }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ }
+ }
+ return params, nil
+}
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
index fe081eb46..60fdd0a23 100644
--- a/pkg/bindings/volumes/volumes.go
+++ b/pkg/bindings/volumes/volumes.go
@@ -114,3 +114,16 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error
}
return response.Process(nil)
}
+
+// Exists returns true if a given volume exists
+func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return false, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/exists", nil, nil, nameOrID)
+ if err != nil {
+ return false, err
+ }
+ return response.IsSuccess(), nil
+}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index d8576c101..4c1bd6a7d 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -128,12 +128,11 @@ type RestartReport struct {
}
type RmOptions struct {
- All bool
- CIDFiles []string
- Force bool
- Ignore bool
- Latest bool
- Volumes bool
+ All bool
+ Force bool
+ Ignore bool
+ Latest bool
+ Volumes bool
}
type RmReport struct {
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 7b43ac961..39bda1d72 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -86,6 +86,7 @@ type ContainerEngine interface {
Unshare(ctx context.Context, args []string) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
+ VolumeExists(ctx context.Context, namesOrId string) (*BoolReport, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error)
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 935ee6f20..ee611502f 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -32,6 +32,7 @@ type ImageEngine interface {
Unmount(ctx context.Context, images []string, options ImageUnmountOptions) ([]*ImageUnmountReport, error)
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error)
+ ManifestExists(ctx context.Context, name string) (*BoolReport, error)
ManifestInspect(ctx context.Context, name string) ([]byte, error)
ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error)
ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error)
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 78a7d8aa7..ef40d5490 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -133,13 +133,13 @@ type ImagePullOptions struct {
Username string
// Password for authenticating against the registry.
Password string
- // OverrideArch will overwrite the local architecture for image pulls.
- OverrideArch string
- // OverrideOS will overwrite the local operating system (OS) for image
+ // Arch will overwrite the local architecture for image pulls.
+ Arch string
+ // OS will overwrite the local operating system (OS) for image
// pulls.
- OverrideOS string
- // OverrideVariant will overwrite the local variant for image pulls.
- OverrideVariant string
+ OS string
+ // Variant will overwrite the local variant for image pulls.
+ Variant string
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet bool
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index a8f4d44a8..48a32817d 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -264,30 +264,30 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
reports := []*entities.RmReport{}
names := namesOrIds
- for _, cidFile := range options.CIDFiles {
- content, err := ioutil.ReadFile(cidFile)
- if err != nil {
- return nil, errors.Wrap(err, "error reading CIDFile")
- }
- id := strings.Split(string(content), "\n")[0]
- names = append(names, id)
- }
-
// Attempt to remove named containers directly from storage, if container is defined in libpod
// this will fail and code will fall through to removing the container from libpod.`
tmpNames := []string{}
for _, ctr := range names {
report := entities.RmReport{Id: ctr}
- if err := ic.Libpod.RemoveStorageContainer(ctr, options.Force); err != nil {
+ report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force)
+ switch errors.Cause(report.Err) {
+ case nil:
// remove container names that we successfully deleted
- tmpNames = append(tmpNames, ctr)
- } else {
reports = append(reports, &report)
+ case define.ErrNoSuchCtr:
+ // There is still a potential this is a libpod container
+ tmpNames = append(tmpNames, ctr)
+ default:
+ if _, err := ic.Libpod.LookupContainer(ctr); errors.Cause(err) == define.ErrNoSuchCtr {
+ // remove container failed, but not a libpod container
+ reports = append(reports, &report)
+ continue
+ }
+ // attempt to remove as a libpod container
+ tmpNames = append(tmpNames, ctr)
}
}
- if len(tmpNames) < len(names) {
- names = tmpNames
- }
+ names = tmpNames
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 1288ab09b..8ca93e770 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -241,9 +241,9 @@ func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: options.CertDir,
- OSChoice: options.OverrideOS,
- ArchitectureChoice: options.OverrideArch,
- VariantChoice: options.OverrideVariant,
+ OSChoice: options.OS,
+ ArchitectureChoice: options.Arch,
+ VariantChoice: options.Variant,
DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 139032ad6..626f1f7bf 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -40,6 +40,18 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin
return imageID, err
}
+// ManifestExists checks if a manifest list with the given name exists in local storage
+func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) {
+ if image, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
+ exists, err := image.ExistsManifest()
+ if err != nil && errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
+ return nil, err
+ }
+ return &entities.BoolReport{Value: exists}, nil
+ }
+ return &entities.BoolReport{Value: false}, nil
+}
+
// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 823605052..f15aa2d14 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -153,3 +153,12 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
}
return reports, nil
}
+
+// VolumeExists check if a given volume name exists
+func (ic *ContainerEngine) VolumeExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
+ exists, err := ic.Libpod.HasVolume(nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.BoolReport{Value: exists}, nil
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 84a07f8e9..524b29553 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -173,14 +173,6 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
}
func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) {
- for _, cidFile := range opts.CIDFiles {
- content, err := ioutil.ReadFile(cidFile)
- if err != nil {
- return nil, errors.Wrap(err, "error reading CIDFile")
- }
- id := strings.Split(string(content), "\n")[0]
- namesOrIds = append(namesOrIds, id)
- }
ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds)
if err != nil {
return nil, err
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 2d686b2aa..0de756756 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -106,8 +106,8 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) {
options := new(images.PullOptions)
- options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithOverrideArch(opts.OverrideArch).WithOverrideOS(opts.OverrideOS)
- options.WithOverrideVariant(opts.OverrideVariant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy)
+ options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithArch(opts.Arch).WithOS(opts.OS)
+ options.WithVariant(opts.Variant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
if s == types.OptionalBoolTrue {
options.WithSkipTLSVerify(true)
diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go
index 22ca44165..c12ba0045 100644
--- a/pkg/domain/infra/tunnel/manifest.go
+++ b/pkg/domain/infra/tunnel/manifest.go
@@ -23,6 +23,15 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin
return imageID, err
}
+// ManifestExists checks if a manifest list with the given name exists
+func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) {
+ exists, err := manifests.Exists(ir.ClientCtx, name, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.BoolReport{Value: exists}, nil
+}
+
// ManifestInspect returns contents of manifest list with given name
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
list, err := manifests.Inspect(ir.ClientCtx, name, nil)
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index f21336828..ffd2c1d35 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -80,3 +80,14 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
options := new(volumes.ListOptions).WithFilters(opts.Filter)
return volumes.List(ic.ClientCtx, options)
}
+
+// VolumeExists checks if the given volume exists
+func (ic *ContainerEngine) VolumeExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
+ exists, err := volumes.Exists(ic.ClientCtx, nameOrID, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.BoolReport{
+ Value: exists,
+ }, nil
+}
diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go
index c686d80fc..80e1309a5 100644
--- a/pkg/rootlessport/rootlessport_linux.go
+++ b/pkg/rootlessport/rootlessport_linux.go
@@ -48,6 +48,7 @@ type Config struct {
ExitFD int
ReadyFD int
TmpDir string
+ ChildIP string
}
func init() {
@@ -227,7 +228,7 @@ outer:
// let parent expose ports
logrus.Infof("exposing ports %v", cfg.Mappings)
- if err := exposePorts(driver, cfg.Mappings); err != nil {
+ if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil {
return err
}
@@ -248,7 +249,7 @@ outer:
return nil
}
-func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error {
+func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping, childIP string) error {
ctx := context.TODO()
for _, i := range portMappings {
hostIP := i.HostIP
@@ -260,6 +261,7 @@ func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error {
ParentIP: hostIP,
ParentPort: int(i.HostPort),
ChildPort: int(i.ContainerPort),
+ ChildIP: childIP,
}
if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
return err
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 2feb1d3b2..cc3f7928c 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -257,6 +257,14 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
}
}
+ if s.LogConfiguration == nil {
+ s.LogConfiguration = &specgen.LogConfig{}
+ }
+ // set log-driver from common if not already set
+ if len(s.LogConfiguration.Driver) < 1 {
+ s.LogConfiguration.Driver = rtc.Containers.LogDriver
+ }
+
warnings, err := verifyContainerResources(s)
if err != nil {
return warnings, err
diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go
index 8901298db..de6751a17 100644
--- a/pkg/systemd/generate/common.go
+++ b/pkg/systemd/generate/common.go
@@ -30,14 +30,14 @@ func validateRestartPolicy(restart string) error {
return errors.Errorf("%s is not a valid restart policy", restart)
}
-const headerTemplate = `# {{.ServiceName}}.service
-# autogenerated by Podman {{.PodmanVersion}}
-{{- if .TimeStamp}}
-# {{.TimeStamp}}
-{{- end}}
+const headerTemplate = `# {{{{.ServiceName}}}}.service
+# autogenerated by Podman {{{{.PodmanVersion}}}}
+{{{{- if .TimeStamp}}}}
+# {{{{.TimeStamp}}}}
+{{{{- end}}}}
[Unit]
-Description=Podman {{.ServiceName}}.service
+Description=Podman {{{{.ServiceName}}}}.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go
index b64b2593c..5f52b0a77 100644
--- a/pkg/systemd/generate/containers.go
+++ b/pkg/systemd/generate/containers.go
@@ -72,22 +72,22 @@ type containerInfo struct {
}
const containerTemplate = headerTemplate + `
-{{- if .BoundToServices}}
-BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
-After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
-{{- end}}
+{{{{- if .BoundToServices}}}}
+BindsTo={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
+After={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
+{{{{- end}}}}
[Service]
-Environment={{.EnvVariable}}=%n
-Restart={{.RestartPolicy}}
-TimeoutStopSec={{.TimeoutStopSec}}
-{{- if .ExecStartPre}}
-ExecStartPre={{.ExecStartPre}}
-{{- end}}
-ExecStart={{.ExecStart}}
-ExecStop={{.ExecStop}}
-ExecStopPost={{.ExecStopPost}}
-PIDFile={{.PIDFile}}
+Environment={{{{.EnvVariable}}}}=%n
+Restart={{{{.RestartPolicy}}}}
+TimeoutStopSec={{{{.TimeoutStopSec}}}}
+{{{{- if .ExecStartPre}}}}
+ExecStartPre={{{{.ExecStartPre}}}}
+{{{{- end}}}}
+ExecStart={{{{.ExecStart}}}}
+ExecStop={{{{.ExecStop}}}}
+ExecStopPost={{{{.ExecStopPost}}}}
+PIDFile={{{{.PIDFile}}}}
Type=forking
[Install]
@@ -173,9 +173,9 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
}
info.EnvVariable = EnvVariable
- info.ExecStart = "{{.Executable}} start {{.ContainerNameOrID}}"
- info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerNameOrID}}"
- info.ExecStopPost = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerNameOrID}}"
+ info.ExecStart = "{{{{.Executable}}}} start {{{{.ContainerNameOrID}}}}"
+ info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}"
+ info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}"
// Assemble the ExecStart command when creating a new container.
//
@@ -209,8 +209,8 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
}
startCommand = append(startCommand,
"run",
- "--conmon-pidfile", "{{.PIDFile}}",
- "--cidfile", "{{.ContainerIDFile}}",
+ "--conmon-pidfile", "{{{{.PIDFile}}}}",
+ "--cidfile", "{{{{.ContainerIDFile}}}}",
"--cgroups=no-conmon",
)
// If the container is in a pod, make sure that the
@@ -281,10 +281,10 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
startCommand = append(startCommand, remainingCmd...)
startCommand = quoteArguments(startCommand)
- info.ExecStartPre = "/bin/rm -f {{.PIDFile}} {{.ContainerIDFile}}"
+ info.ExecStartPre = "/bin/rm -f {{{{.PIDFile}}}} {{{{.ContainerIDFile}}}}"
info.ExecStart = strings.Join(startCommand, " ")
- info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}"
- info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}rm --ignore -f --cidfile {{.ContainerIDFile}}"
+ info.ExecStop = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}stop --ignore --cidfile {{{{.ContainerIDFile}}}} {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}}"
+ info.ExecStopPost = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}rm --ignore -f --cidfile {{{{.ContainerIDFile}}}}"
}
info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout
@@ -307,7 +307,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
// generation. That's especially needed for embedding the PID and ID
// files in other fields which will eventually get replaced in the 2nd
// template execution.
- templ, err := template.New("container_template").Parse(containerTemplate)
+ templ, err := template.New("container_template").Delims("{{{{", "}}}}").Parse(containerTemplate)
if err != nil {
return "", errors.Wrap(err, "error parsing systemd service template")
}
@@ -318,7 +318,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
}
// Now parse the generated template (i.e., buf) and execute it.
- templ, err = template.New("container_template").Parse(buf.String())
+ templ, err = template.New("container_template").Delims("{{{{", "}}}}").Parse(buf.String())
if err != nil {
return "", errors.Wrap(err, "error parsing systemd service template")
}
diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go
index c8e65bfe3..96d95644b 100644
--- a/pkg/systemd/generate/containers_test.go
+++ b/pkg/systemd/generate/containers_test.go
@@ -329,6 +329,29 @@ Type=forking
WantedBy=multi-user.target default.target
`
+ goodNewWithJournaldTag := `# jadda-jadda.service
+# autogenerated by Podman CI
+
+[Unit]
+Description=Podman jadda-jadda.service
+Documentation=man:podman-generate-systemd(1)
+Wants=network.target
+After=network-online.target
+
+[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
+Restart=always
+TimeoutStopSec=70
+ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id
+ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test --log-driver=journald --log-opt=tag={{.Name}} awesome-image:latest
+ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10
+ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id
+PIDFile=%t/jadda-jadda.pid
+Type=forking
+
+[Install]
+WantedBy=multi-user.target default.target
+`
tests := []struct {
name string
info containerInfo
@@ -608,6 +631,22 @@ WantedBy=multi-user.target default.target
true,
false,
},
+ {"good with journald log tag (see #9034)",
+ containerInfo{
+ Executable: "/usr/bin/podman",
+ ServiceName: "jadda-jadda",
+ ContainerNameOrID: "jadda-jadda",
+ RestartPolicy: "always",
+ PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
+ StopTimeout: 10,
+ PodmanVersion: "CI",
+ CreateCommand: []string{"I'll get stripped", "create", "--name", "test", "--log-driver=journald", "--log-opt=tag={{.Name}}", "awesome-image:latest"},
+ EnvVariable: EnvVariable,
+ },
+ goodNewWithJournaldTag,
+ true,
+ false,
+ },
}
for _, tt := range tests {
test := tt
diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go
index 7678a240f..c7e3aa955 100644
--- a/pkg/systemd/generate/pods.go
+++ b/pkg/systemd/generate/pods.go
@@ -72,23 +72,23 @@ type podInfo struct {
ExecStopPost string
}
-const podTemplate = headerTemplate + `Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
-Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}}
+const podTemplate = headerTemplate + `Requires={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
+Before={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}}
[Service]
-Environment={{.EnvVariable}}=%n
-Restart={{.RestartPolicy}}
-TimeoutStopSec={{.TimeoutStopSec}}
-{{- if .ExecStartPre1}}
-ExecStartPre={{.ExecStartPre1}}
-{{- end}}
-{{- if .ExecStartPre2}}
-ExecStartPre={{.ExecStartPre2}}
-{{- end}}
-ExecStart={{.ExecStart}}
-ExecStop={{.ExecStop}}
-ExecStopPost={{.ExecStopPost}}
-PIDFile={{.PIDFile}}
+Environment={{{{.EnvVariable}}}}=%n
+Restart={{{{.RestartPolicy}}}}
+TimeoutStopSec={{{{.TimeoutStopSec}}}}
+{{{{- if .ExecStartPre1}}}}
+ExecStartPre={{{{.ExecStartPre1}}}}
+{{{{- end}}}}
+{{{{- if .ExecStartPre2}}}}
+ExecStartPre={{{{.ExecStartPre2}}}}
+{{{{- end}}}}
+ExecStart={{{{.ExecStart}}}}
+ExecStop={{{{.ExecStop}}}}
+ExecStopPost={{{{.ExecStopPost}}}}
+PIDFile={{{{.PIDFile}}}}
Type=forking
[Install]
@@ -236,9 +236,9 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
}
info.EnvVariable = EnvVariable
- info.ExecStart = "{{.Executable}} start {{.InfraNameOrID}}"
- info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.InfraNameOrID}}"
- info.ExecStopPost = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.InfraNameOrID}}"
+ info.ExecStart = "{{{{.Executable}}}} start {{{{.InfraNameOrID}}}}"
+ info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}"
+ info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}"
// Assemble the ExecStart command when creating a new pod.
//
@@ -278,8 +278,8 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
startCommand = append(startCommand, podRootArgs...)
startCommand = append(startCommand,
[]string{"pod", "create",
- "--infra-conmon-pidfile", "{{.PIDFile}}",
- "--pod-id-file", "{{.PodIDFile}}"}...)
+ "--infra-conmon-pidfile", "{{{{.PIDFile}}}}",
+ "--pod-id-file", "{{{{.PodIDFile}}}}"}...)
// Presence check for certain flags/options.
fs := pflag.NewFlagSet("args", pflag.ContinueOnError)
@@ -308,11 +308,11 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
startCommand = append(startCommand, podCreateArgs...)
startCommand = quoteArguments(startCommand)
- info.ExecStartPre1 = "/bin/rm -f {{.PIDFile}} {{.PodIDFile}}"
+ info.ExecStartPre1 = "/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}"
info.ExecStartPre2 = strings.Join(startCommand, " ")
- info.ExecStart = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod start --pod-id-file {{.PodIDFile}}"
- info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}"
- info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod rm --ignore -f --pod-id-file {{.PodIDFile}}"
+ info.ExecStart = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod start --pod-id-file {{{{.PodIDFile}}}}"
+ info.ExecStop = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod stop --ignore --pod-id-file {{{{.PodIDFile}}}} {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}}"
+ info.ExecStopPost = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod rm --ignore -f --pod-id-file {{{{.PodIDFile}}}}"
}
info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout
@@ -334,7 +334,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
// generation. That's especially needed for embedding the PID and ID
// files in other fields which will eventually get replaced in the 2nd
// template execution.
- templ, err := template.New("pod_template").Parse(podTemplate)
+ templ, err := template.New("pod_template").Delims("{{{{", "}}}}").Parse(podTemplate)
if err != nil {
return "", errors.Wrap(err, "error parsing systemd service template")
}
@@ -345,7 +345,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
}
// Now parse the generated template (i.e., buf) and execute it.
- templ, err = template.New("pod_template").Parse(buf.String())
+ templ, err = template.New("pod_template").Delims("{{{{", "}}}}").Parse(buf.String())
if err != nil {
return "", errors.Wrap(err, "error parsing systemd service template")
}
diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go
index 1c6330160..2b430226b 100644
--- a/pkg/systemd/generate/pods_test.go
+++ b/pkg/systemd/generate/pods_test.go
@@ -143,6 +143,33 @@ Type=forking
WantedBy=multi-user.target default.target
`
+ podNewLabelWithCurlyBraces := `# pod-123abc.service
+# autogenerated by Podman CI
+
+[Unit]
+Description=Podman pod-123abc.service
+Documentation=man:podman-generate-systemd(1)
+Wants=network.target
+After=network-online.target
+Requires=container-1.service container-2.service
+Before=container-1.service container-2.service
+
+[Service]
+Environment=PODMAN_SYSTEMD_UNIT=%n
+Restart=on-failure
+TimeoutStopSec=70
+ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id
+ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --label key={{someval}} --replace
+ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id
+ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10
+ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id
+PIDFile=%t/pod-123abc.pid
+Type=forking
+
+[Install]
+WantedBy=multi-user.target default.target
+`
+
tests := []struct {
name string
info podInfo
@@ -230,6 +257,22 @@ WantedBy=multi-user.target default.target
true,
false,
},
+ {"pod --new with double curly braces",
+ podInfo{
+ Executable: "/usr/bin/podman",
+ ServiceName: "pod-123abc",
+ InfraNameOrID: "jadda-jadda-infra",
+ RestartPolicy: "on-failure",
+ PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
+ StopTimeout: 10,
+ PodmanVersion: "CI",
+ RequiredServices: []string{"container-1", "container-2"},
+ CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "--label", "key={{someval}}"},
+ },
+ podNewLabelWithCurlyBraces,
+ true,
+ false,
+ },
}
for _, tt := range tests {