diff options
Diffstat (limited to 'pkg')
25 files changed, 416 insertions, 181 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 971b6aa50..d26bb50f4 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -307,6 +307,34 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error } } + portMappings, err := l.PortMappings() + if err != nil { + return nil, err + } + + ports := make([]types.Port, len(portMappings)) + for idx, portMapping := range portMappings { + ports[idx] = types.Port{ + IP: portMapping.HostIP, + PrivatePort: uint16(portMapping.ContainerPort), + PublicPort: uint16(portMapping.HostPort), + Type: portMapping.Protocol, + } + } + inspect, err := l.Inspect(false) + if err != nil { + return nil, err + } + + n, err := json.Marshal(inspect.NetworkSettings) + if err != nil { + return nil, err + } + networkSettings := types.SummaryNetworkSettings{} + if err := json.Unmarshal(n, &networkSettings); err != nil { + return nil, err + } + return &handlers.Container{Container: types.Container{ ID: l.ID(), Names: []string{fmt.Sprintf("/%s", l.Name())}, @@ -314,7 +342,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error ImageID: imageID, Command: strings.Join(l.Command(), " "), Created: l.CreatedTime().Unix(), - Ports: nil, + Ports: ports, SizeRw: sizeRW, SizeRootFs: sizeRootFs, Labels: l.Labels(), @@ -324,7 +352,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error NetworkMode string `json:",omitempty"` }{ "host"}, - NetworkSettings: nil, + NetworkSettings: &networkSettings, Mounts: nil, }, ContainerCreateConfig: types.ContainerCreateConfig{}, diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 2b84c9a25..e06f93b89 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -104,6 +104,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Squash bool `schema:"squash"` Tag []string `schema:"t"` Target string `schema:"target"` + Timestamp int64 `schema:"timestamp"` }{ Dockerfile: "Dockerfile", Registry: "docker.io", @@ -326,6 +327,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Target: query.Target, } + if _, found := r.URL.Query()["timestamp"]; found { + ts := time.Unix(query.Timestamp, 0) + buildOptions.Timestamp = &ts + } + runCtx, cancel := context.WithCancel(context.Background()) var imageID string go func() { diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index c5ee8c324..86e3887a4 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -40,7 +40,21 @@ func ListSecrets(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusOK, reports) + if utils.IsLibpodRequest(r) { + utils.WriteResponse(w, http.StatusOK, reports) + return + } + // Docker compat expects a version field that increments when the secret is updated + // We currently can't update a secret, so we default the version to 1 + compatReports := make([]entities.SecretInfoReportCompat, 0, len(reports)) + for _, report := range reports { + compatRep := entities.SecretInfoReportCompat{ + SecretInfoReport: *report, + Version: entities.SecretVersion{Index: 1}, + } + compatReports = append(compatReports, compatRep) + } + utils.WriteResponse(w, http.StatusOK, compatReports) } func InspectSecret(w http.ResponseWriter, r *http.Request) { @@ -59,7 +73,21 @@ func InspectSecret(w http.ResponseWriter, r *http.Request) { utils.SecretNotFound(w, name, errs[0]) return } - utils.WriteResponse(w, http.StatusOK, reports[0]) + if len(reports) < 1 { + utils.InternalServerError(w, err) + return + } + if utils.IsLibpodRequest(r) { + utils.WriteResponse(w, http.StatusOK, reports[0]) + return + } + // Docker compat expects a version field that increments when the secret is updated + // We currently can't update a secret, so we default the version to 1 + compatReport := entities.SecretInfoReportCompat{ + SecretInfoReport: *reports[0], + Version: entities.SecretVersion{Index: 1}, + } + utils.WriteResponse(w, http.StatusOK, compatReport) } func RemoveSecret(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go index d90a892c1..fae147440 100644 --- a/pkg/api/handlers/compat/version.go +++ b/pkg/api/handlers/compat/version.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/version" docker "github.com/docker/docker/api/types" "github.com/pkg/errors" ) @@ -35,20 +36,20 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { Name: "Podman Engine", Version: versionInfo.Version, Details: map[string]string{ - "APIVersion": utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String(), + "APIVersion": version.APIVersion[version.Libpod][version.CurrentAPI].String(), "Arch": goRuntime.GOARCH, "BuildTime": time.Unix(versionInfo.Built, 0).Format(time.RFC3339), "Experimental": "true", "GitCommit": versionInfo.GitCommit, "GoVersion": versionInfo.GoVersion, "KernelVersion": infoData.Host.Kernel, - "MinAPIVersion": utils.APIVersion[utils.LibpodTree][utils.MinimalAPIVersion].String(), + "MinAPIVersion": version.APIVersion[version.Libpod][version.MinimalAPI].String(), "Os": goRuntime.GOOS, }, }} - apiVersion := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion] - minVersion := utils.APIVersion[utils.CompatTree][utils.MinimalAPIVersion] + apiVersion := version.APIVersion[version.Compat][version.CurrentAPI] + minVersion := version.APIVersion[version.Compat][version.MinimalAPI] utils.WriteResponse(w, http.StatusOK, entities.ComponentVersion{ Version: docker.Version{ diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index b3c674788..7625f9546 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -10,49 +10,14 @@ import ( "unsafe" "github.com/blang/semver" + "github.com/containers/podman/v3/version" "github.com/gorilla/mux" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -type ( - // VersionTree determines which API endpoint tree for version - VersionTree int - // VersionLevel determines which API level, current or something from the past - VersionLevel int -) - -const ( - // LibpodTree supports Libpod endpoints - LibpodTree = VersionTree(iota) - // CompatTree supports Libpod endpoints - CompatTree - - // CurrentAPIVersion announces what is the current API level - CurrentAPIVersion = VersionLevel(iota) - // MinimalAPIVersion announces what is the oldest API level supported - MinimalAPIVersion -) - var ( - // See https://docs.docker.com/engine/api/v1.40/ - // libpod compat handlers are expected to honor docker API versions - - // APIVersion provides the current and minimal API versions for compat and libpod endpoint trees - // Note: GET|HEAD /_ping is never versioned and provides the API-Version and Libpod-API-Version headers to allow - // clients to shop for the Version they wish to support - APIVersion = map[VersionTree]map[VersionLevel]semver.Version{ - LibpodTree: { - CurrentAPIVersion: semver.MustParse("3.0.0"), - MinimalAPIVersion: semver.MustParse("3.0.0"), - }, - CompatTree: { - CurrentAPIVersion: semver.MustParse("1.40.0"), - MinimalAPIVersion: semver.MustParse("1.24.0"), - }, - } - // ErrVersionNotGiven returned when version not given by client ErrVersionNotGiven = errors.New("version not given in URL path") // ErrVersionNotSupported returned when given version is too old @@ -98,14 +63,14 @@ func SupportedVersion(r *http.Request, condition string) (semver.Version, error) // SupportedVersionWithDefaults validates that the version provided by client valid is supported by server // minimal API version <= client path version <= maximum API version focused on the endpoint tree from URL func SupportedVersionWithDefaults(r *http.Request) (semver.Version, error) { - tree := CompatTree + tree := version.Compat if IsLibpodRequest(r) { - tree = LibpodTree + tree = version.Libpod } return SupportedVersion(r, - fmt.Sprintf(">=%s <=%s", APIVersion[tree][MinimalAPIVersion].String(), - APIVersion[tree][CurrentAPIVersion].String())) + fmt.Sprintf(">=%s <=%s", version.APIVersion[tree][version.MinimalAPI].String(), + version.APIVersion[tree][version.CurrentAPI].String())) } // WriteResponse encodes the given value as JSON or string and renders it for http client diff --git a/pkg/api/handlers/utils/handler_test.go b/pkg/api/handlers/utils/handler_test.go index d9fd22b80..18a1d2678 100644 --- a/pkg/api/handlers/utils/handler_test.go +++ b/pkg/api/handlers/utils/handler_test.go @@ -7,17 +7,18 @@ import ( "net/http/httptest" "testing" + "github.com/containers/podman/v3/version" "github.com/gorilla/mux" ) func TestSupportedVersion(t *testing.T) { req, err := http.NewRequest("GET", - fmt.Sprintf("/v%s/libpod/testing/versions", APIVersion[LibpodTree][CurrentAPIVersion]), + fmt.Sprintf("/v%s/libpod/testing/versions", version.APIVersion[version.Libpod][version.CurrentAPI]), nil) if err != nil { t.Fatal(err) } - req = mux.SetURLVars(req, map[string]string{"version": APIVersion[LibpodTree][CurrentAPIVersion].String()}) + req = mux.SetURLVars(req, map[string]string{"version": version.APIVersion[version.Libpod][version.CurrentAPI].String()}) rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index e7bf94fc6..28b8706a8 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/auth" + "github.com/containers/podman/v3/version" "github.com/google/uuid" "github.com/sirupsen/logrus" ) @@ -55,10 +56,10 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { c = context.WithValue(c, "idletracker", s.idleTracker) // nolint r = r.WithContext(c) - cv := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion] + cv := version.APIVersion[version.Compat][version.CurrentAPI] w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) - lv := utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String() + lv := version.APIVersion[version.Libpod][version.CurrentAPI].String() w.Header().Set("Libpod-API-Version", lv) w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") @@ -72,5 +73,5 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { // VersionedPath prepends the version parsing code // any handler may override this default when registering URL(s) func VersionedPath(p string) string { - return "/v{version:[0-9][0-9.]*}" + p + return "/v{version:[0-9][0-9A-Za-z.-]*}" + p } diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go index 2a5cfba0b..2ac126644 100644 --- a/pkg/api/server/register_archive.go +++ b/pkg/api/server/register_archive.go @@ -91,7 +91,7 @@ func (s *APIServer) registerArchiveHandlers(r *mux.Router) error { Libpod */ - // swagger:operation POST /libpod/containers/{name}/archive libpod libpodPutArchive + // swagger:operation PUT /libpod/containers/{name}/archive libpod libpodPutArchive // --- // summary: Copy files into a container // description: Copy a tar archive of files into a container diff --git a/pkg/api/server/register_secrets.go b/pkg/api/server/register_secrets.go index 1c5f5954b..531623845 100644 --- a/pkg/api/server/register_secrets.go +++ b/pkg/api/server/register_secrets.go @@ -115,7 +115,7 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error { // parameters: // responses: // '200': - // "$ref": "#/responses/SecretListResponse" + // "$ref": "#/responses/SecretListCompatResponse" // '500': // "$ref": "#/responses/InternalError" r.Handle(VersionedPath("/secrets"), s.APIHandler(compat.ListSecrets)).Methods(http.MethodGet) @@ -158,7 +158,7 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error { // - application/json // responses: // '200': - // "$ref": "#/responses/SecretInspectResponse" + // "$ref": "#/responses/SecretInspectCompatResponse" // '404': // "$ref": "#/responses/NoSuchSecret" // '500': diff --git a/pkg/bindings/bindings.go b/pkg/bindings/bindings.go deleted file mode 100644 index 14f306910..000000000 --- a/pkg/bindings/bindings.go +++ /dev/null @@ -1,26 +0,0 @@ -// Package bindings provides golang-based access -// to the Podman REST API. Users can then interact with API endpoints -// to manage containers, images, pods, etc. -// -// This package exposes a series of methods that allow users to firstly -// create their connection with the API endpoints. Once the connection -// is established, users can then manage the Podman container runtime. -package bindings - -import ( - "github.com/blang/semver" -) - -var ( - // PTrue is a convenience variable that can be used in bindings where - // a pointer to a bool (optional parameter) is required. - pTrue = true - PTrue = &pTrue - // PFalse is a convenience variable that can be used in bindings where - // a pointer to a bool (optional parameter) is required. - pFalse = false - PFalse = &pFalse - - // APIVersion - podman will fail to run if this value is wrong - APIVersion = semver.MustParse("2.0.0") -) diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index ad16498d5..21a8e7a8b 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -14,6 +14,7 @@ import ( "github.com/blang/semver" "github.com/containers/podman/v3/pkg/terminal" + "github.com/containers/podman/v3/version" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -25,7 +26,7 @@ var ( BasePath = &url.URL{ Scheme: "http", Host: "d", - Path: "/v" + APIVersion.String() + "/libpod", + Path: "/v" + version.APIVersion[version.Libpod][version.CurrentAPI].String() + "/libpod", } ) @@ -168,15 +169,16 @@ func pingNewConnection(ctx context.Context) error { return err } - switch APIVersion.Compare(versionSrv) { + switch version.APIVersion[version.Libpod][version.MinimalAPI].Compare(versionSrv) { case -1, 0: // Server's job when Client version is equal or older return nil case 1: - return errors.Errorf("server API version is too old. Client %q server %q", APIVersion.String(), versionSrv.String()) + return errors.Errorf("server API version is too old. Client %q server %q", + version.APIVersion[version.Libpod][version.MinimalAPI].String(), versionSrv.String()) } } - return errors.Errorf("ping response was %q", response.StatusCode) + return errors.Errorf("ping response was %d", response.StatusCode) } func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 6e16461e5..27706fd2c 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -185,6 +185,12 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.Squash { params.Set("squash", "1") } + + if options.Timestamp != nil { + t := *options.Timestamp + params.Set("timestamp", strconv.FormatInt(t.Unix(), 10)) + } + var ( headers map[string]string err error diff --git a/pkg/bindings/test/attach_test.go b/pkg/bindings/test/attach_test.go index 16090e104..fbdf18d44 100644 --- a/pkg/bindings/test/attach_test.go +++ b/pkg/bindings/test/attach_test.go @@ -35,7 +35,7 @@ var _ = Describe("Podman containers attach", func() { It("can run top in container", func() { name := "TopAttachTest" - id, err := bt.RunTopContainer(&name, nil, nil) + id, err := bt.RunTopContainer(&name, nil) Expect(err).ShouldNot(HaveOccurred()) tickTock := time.NewTimer(2 * time.Second) diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 588f38930..9bac4b620 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -188,14 +188,14 @@ func (b *bindingTest) restoreImageFromCache(i testImage) { // Run a container within or without a pod // and add or append the alpine image to it -func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, podName *string) (string, error) { +func (b *bindingTest) RunTopContainer(containerName *string, podName *string) (string, error) { s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = false s.Command = []string{"/usr/bin/top"} if containerName != nil { s.Name = *containerName } - if insidePod != nil && podName != nil { + if podName != nil { s.Pod = *podName } ctr, err := containers.CreateWithSpec(b.conn, s, nil) diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index f2ab197ce..b0ddc7862 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -55,7 +55,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a running container by name", func() { // Pausing by name should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -69,7 +69,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a running container by id", func() { // Pausing by id should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -83,7 +83,7 @@ var _ = Describe("Podman containers ", func() { It("podman unpause a running container by name", func() { // Unpausing by name should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -99,7 +99,7 @@ var _ = Describe("Podman containers ", func() { It("podman unpause a running container by ID", func() { // Unpausing by ID should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Pause by name err = containers.Pause(bt.conn, name, nil) @@ -118,7 +118,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a paused container by name", func() { // Pausing a paused container by name should fail var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -131,7 +131,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a paused container by id", func() { // Pausing a paused container by id should fail var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -144,7 +144,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a stopped container by name", func() { // Pausing a stopped container by name should fail var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -157,7 +157,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a stopped container by id", func() { // Pausing a stopped container by id should fail var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -170,7 +170,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove a paused container by id without force", func() { // Removing a paused container without force should fail var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -183,7 +183,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove a paused container by id with force", func() { // Removing a paused container with force should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -194,7 +194,7 @@ var _ = Describe("Podman containers ", func() { It("podman stop a paused container by name", func() { // Stopping a paused container by name should fail var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -207,7 +207,7 @@ var _ = Describe("Podman containers ", func() { It("podman stop a paused container by id", func() { // Stopping a paused container by id should fail var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -220,7 +220,7 @@ var _ = Describe("Podman containers ", func() { It("podman stop a running container by name", func() { // Stopping a running container by name should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -234,7 +234,7 @@ var _ = Describe("Podman containers ", func() { It("podman stop a running container by ID", func() { // Stopping a running container by ID should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -256,7 +256,7 @@ var _ = Describe("Podman containers ", func() { Expect(code).To(BeNumerically("==", http.StatusNotFound)) errChan := make(chan error) - _, err = bt.RunTopContainer(&name, nil, nil) + _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) go func() { exitCode, err = containers.Wait(bt.conn, name, nil) @@ -278,7 +278,7 @@ var _ = Describe("Podman containers ", func() { running = define.ContainerStateRunning ) errChan := make(chan error) - _, err := bt.RunTopContainer(&name, nil, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) go func() { exitCode, err = containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition([]define.ContainerStatus{pause})) @@ -317,7 +317,7 @@ var _ = Describe("Podman containers ", func() { // a container that has no healthcheck should be a 409 var name = "top" - bt.RunTopContainer(&name, bindings.PFalse, nil) + bt.RunTopContainer(&name, nil) _, err = containers.RunHealthCheck(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) @@ -376,7 +376,7 @@ var _ = Describe("Podman containers ", func() { It("podman top", func() { var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // By name @@ -414,7 +414,7 @@ var _ = Describe("Podman containers ", func() { It("podman container exists in local storage by name", func() { // Container existence check by name should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) containerExists, err := containers.Exists(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -424,7 +424,7 @@ var _ = Describe("Podman containers ", func() { It("podman container exists in local storage by ID", func() { // Container existence check by ID should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) containerExists, err := containers.Exists(bt.conn, cid, nil) Expect(err).To(BeNil()) @@ -434,7 +434,7 @@ var _ = Describe("Podman containers ", func() { It("podman container exists in local storage by short ID", func() { // Container existence check by short ID should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) containerExists, err := containers.Exists(bt.conn, cid[0:12], nil) Expect(err).To(BeNil()) @@ -452,7 +452,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill a running container by name with SIGINT", func() { // Killing a running container should work var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Kill(bt.conn, name, new(containers.KillOptions).WithSignal("SIGINT")) Expect(err).To(BeNil()) @@ -463,7 +463,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill a running container by ID with SIGTERM", func() { // Killing a running container by ID should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Kill(bt.conn, cid, new(containers.KillOptions).WithSignal("SIGTERM")) Expect(err).To(BeNil()) @@ -474,7 +474,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill a running container by ID with SIGKILL", func() { // Killing a running container by ID with TERM should work var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Kill(bt.conn, cid, new(containers.KillOptions).WithSignal("SIGKILL")) Expect(err).To(BeNil()) @@ -483,7 +483,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill a running container by bogus signal", func() { //Killing a running container by bogus signal should fail var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Kill(bt.conn, cid, new(containers.KillOptions).WithSignal("foobar")) Expect(err).ToNot(BeNil()) @@ -495,9 +495,9 @@ var _ = Describe("Podman containers ", func() { // Killing latest container should work var name1 = "first" var name2 = "second" - _, err := bt.RunTopContainer(&name1, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name1, nil) Expect(err).To(BeNil()) - _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name2, nil) Expect(err).To(BeNil()) containerLatestList, err := containers.List(bt.conn, new(containers.ListOptions).WithLast(1)) Expect(err).To(BeNil()) @@ -526,7 +526,7 @@ var _ = Describe("Podman containers ", func() { It("podman prune stopped containers", func() { // Start and stop a container to enter in exited state. var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -541,7 +541,7 @@ var _ = Describe("Podman containers ", func() { It("podman prune stopped containers with filters", func() { // Start and stop a container to enter in exited state. var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -575,7 +575,7 @@ var _ = Describe("Podman containers ", func() { It("podman prune running containers", func() { // Start the container. var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Check if the container is running. @@ -598,7 +598,7 @@ var _ = Describe("Podman containers ", func() { It("podman inspect running container", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Inspecting running container should succeed _, err = containers.Inspect(bt.conn, name, nil) @@ -607,7 +607,7 @@ var _ = Describe("Podman containers ", func() { It("podman inspect stopped container", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -618,7 +618,7 @@ var _ = Describe("Podman containers ", func() { It("podman inspect running container with size", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) _, err = containers.Inspect(bt.conn, name, new(containers.InspectOptions).WithSize(true)) Expect(err).To(BeNil()) @@ -626,7 +626,7 @@ var _ = Describe("Podman containers ", func() { It("podman inspect stopped container with size", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -643,7 +643,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove running container by name", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, name, nil) @@ -654,7 +654,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove running container by ID", func() { var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, cid, nil) @@ -665,7 +665,7 @@ var _ = Describe("Podman containers ", func() { It("podman forcibly remove running container by name", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) @@ -676,7 +676,7 @@ var _ = Describe("Podman containers ", func() { It("podman forcibly remove running container by ID", func() { var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) @@ -687,7 +687,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove running container and volume by name", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) @@ -698,7 +698,7 @@ var _ = Describe("Podman containers ", func() { It("podman remove running container and volume by ID", func() { var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) @@ -709,7 +709,7 @@ var _ = Describe("Podman containers ", func() { It("podman forcibly remove running container and volume by name", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) @@ -720,7 +720,7 @@ var _ = Describe("Podman containers ", func() { It("podman forcibly remove running container and volume by ID", func() { var name = "top" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) // Removing running container should fail err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) @@ -732,9 +732,9 @@ var _ = Describe("Podman containers ", func() { It("List containers with filters", func() { var name = "top" var name2 = "top2" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) - _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name2, nil) Expect(err).To(BeNil()) s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = true @@ -753,7 +753,7 @@ var _ = Describe("Podman containers ", func() { podName := "testpod" ctrName := "testctr" bt.Podcreate(&podName) - _, err := bt.RunTopContainer(&ctrName, bindings.PTrue, &podName) + _, err := bt.RunTopContainer(&ctrName, &podName) Expect(err).To(BeNil()) lastNum := 1 diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go index 7a21be77f..c10452eaf 100644 --- a/pkg/bindings/test/exec_test.go +++ b/pkg/bindings/test/exec_test.go @@ -4,7 +4,6 @@ import ( "time" "github.com/containers/podman/v3/pkg/api/handlers" - "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/containers" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -33,7 +32,7 @@ var _ = Describe("Podman containers exec", func() { It("Podman exec create makes an exec session", func() { name := "testCtr" - cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + cid, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) execConfig := new(handlers.ExecCreateConfig) @@ -53,7 +52,7 @@ var _ = Describe("Podman containers exec", func() { It("Podman exec create with bad command fails", func() { name := "testCtr" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) execConfig := new(handlers.ExecCreateConfig) diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index db51d1e68..688bf049f 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -101,7 +101,7 @@ var _ = Describe("Podman images", func() { // Start a container with alpine image var top string = "top" - _, err = bt.RunTopContainer(&top, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&top, nil) Expect(err).To(BeNil()) // we should now have a container called "top" running containerResponse, err := containers.Inspect(bt.conn, "top", nil) diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go index 3ca4b99b3..f61e8c370 100644 --- a/pkg/bindings/test/info_test.go +++ b/pkg/bindings/test/info_test.go @@ -49,17 +49,17 @@ var _ = Describe("Podman info", func() { _, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) - idPause, err := bt.RunTopContainer(nil, nil, nil) + idPause, err := bt.RunTopContainer(nil, nil) Expect(err).To(BeNil()) err = containers.Pause(bt.conn, idPause, nil) Expect(err).To(BeNil()) - idStop, err := bt.RunTopContainer(nil, nil, nil) + idStop, err := bt.RunTopContainer(nil, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, idStop, nil) Expect(err).To(BeNil()) - _, err = bt.RunTopContainer(nil, nil, nil) + _, err = bt.RunTopContainer(nil, nil) Expect(err).To(BeNil()) info, err := system.Info(bt.conn, nil) diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 2b4eb05d3..b06ff31a2 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -63,7 +63,7 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) // Adding an alpine container to the existing pod - _, err = bt.RunTopContainer(nil, bindings.PTrue, &newpod) + _, err = bt.RunTopContainer(nil, &newpod) Expect(err).To(BeNil()) podSummary, err = pods.List(bt.conn, nil) // Verify no errors. @@ -93,7 +93,7 @@ var _ = Describe("Podman pods", func() { _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) - _, err = bt.RunTopContainer(nil, bindings.PTrue, &newpod) + _, err = bt.RunTopContainer(nil, &newpod) Expect(err).To(BeNil()) // Expected err with invalid filter params @@ -179,7 +179,7 @@ var _ = Describe("Podman pods", func() { Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Adding an alpine container to the existing pod - _, err = bt.RunTopContainer(nil, bindings.PTrue, &newpod) + _, err = bt.RunTopContainer(nil, &newpod) Expect(err).To(BeNil()) // Binding needs to be modified to inspect the pod state. diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index a68a8099c..68e9d9301 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -4,7 +4,6 @@ import ( "sync" "time" - "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/containers" "github.com/containers/podman/v3/pkg/bindings/pods" "github.com/containers/podman/v3/pkg/bindings/system" @@ -41,7 +40,7 @@ var _ = Describe("Podman system", func() { It("podman events", func() { var name = "top" - _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) filters := make(map[string][]string) @@ -72,7 +71,7 @@ var _ = Describe("Podman system", func() { Expect(err).To(BeNil()) // Start and stop a container to enter in exited state. var name = "top" - _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) @@ -98,14 +97,14 @@ var _ = Describe("Podman system", func() { // Start and stop a container to enter in exited state. var name = "top" - _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) // Start container and leave in running var name2 = "top2" - _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name2, nil) Expect(err).To(BeNil()) // Adding an unused volume @@ -132,14 +131,14 @@ var _ = Describe("Podman system", func() { // Start and stop a container to enter in exited state. var name = "top" - _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) // Start second container and leave in running var name2 = "top2" - _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name2, nil) Expect(err).To(BeNil()) // Adding an unused volume should work @@ -167,14 +166,14 @@ var _ = Describe("Podman system", func() { // Start and stop a container to enter in exited state. var name = "top" - _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) // Start second container and leave in running var name2 = "top2" - _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + _, err = bt.RunTopContainer(&name2, nil) Expect(err).To(BeNil()) // Adding an unused volume should work diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 6285680c0..77a993128 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -4,15 +4,14 @@ import ( "context" "io/ioutil" "os" - "path/filepath" + metadata "github.com/checkpoint-restore/checkpointctl/lib" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/image" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage/pkg/archive" - jsoniter "github.com/json-iterator/go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -20,21 +19,6 @@ import ( // Prefixing the checkpoint/restore related functions with 'cr' -// crImportFromJSON imports the JSON files stored in the exported -// checkpoint tarball -func crImportFromJSON(filePath string, v interface{}) error { - content, err := ioutil.ReadFile(filePath) - if err != nil { - return errors.Wrap(err, "failed to read container definition for restore") - } - json := jsoniter.ConfigCompatibleWithStandardLibrary - if err = json.Unmarshal(content, v); err != nil { - return errors.Wrapf(err, "failed to unmarshal container definition %s for restore", filePath) - } - - return nil -} - // CRImportCheckpoint it the function which imports the information // from checkpoint tarball and re-creates the container from that information func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) { @@ -48,13 +32,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt options := &archive.TarOptions{ // Here we only need the files config.dump and spec.dump ExcludePatterns: []string{ - "checkpoint", - "artifacts", - "ctr.log", - "rootfs-diff.tar", - "network.status", - "deleted.files", "volumes", + "ctr.log", + "artifacts", + metadata.RootFsDiffTar, + metadata.DeletedFilesFile, + metadata.NetworkStatusFile, + metadata.CheckpointDirectory, }, } dir, err := ioutil.TempDir("", "checkpoint") @@ -73,13 +57,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // Load spec.dump from temporary directory dumpSpec := new(spec.Spec) - if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil { + if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil { return nil, err } // Load config.dump from temporary directory config := new(libpod.ContainerConfig) - if err = crImportFromJSON(filepath.Join(dir, "config.dump"), config); err != nil { + if _, err = metadata.ReadJSONFile(config, dir, metadata.ConfigDumpFile); err != nil { return nil, err } diff --git a/pkg/checkpoint/crutils/checkpoint_restore_utils.go b/pkg/checkpoint/crutils/checkpoint_restore_utils.go new file mode 100644 index 000000000..53ff55865 --- /dev/null +++ b/pkg/checkpoint/crutils/checkpoint_restore_utils.go @@ -0,0 +1,191 @@ +package crutils + +import ( + "io" + "os" + "os/exec" + "path/filepath" + + metadata "github.com/checkpoint-restore/checkpointctl/lib" + "github.com/containers/storage/pkg/archive" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" +) + +// This file mainly exist to make the checkpoint/restore functions +// available for other users. One possible candidate would be CRI-O. + +// CRImportCheckpointWithoutConfig imports the checkpoint archive (input) +// into the directory destination without "config.dump" and "spec.dump" +func CRImportCheckpointWithoutConfig(destination, input string) error { + archiveFile, err := os.Open(input) + if err != nil { + return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input) + } + + defer archiveFile.Close() + options := &archive.TarOptions{ + ExcludePatterns: []string{ + // Import everything else besides the container config + metadata.ConfigDumpFile, + metadata.SpecDumpFile, + }, + } + if err = archive.Untar(archiveFile, destination, options); err != nil { + return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + } + + return nil +} + +// CRRemoveDeletedFiles loads the list of deleted files and if +// it exists deletes all files listed. +func CRRemoveDeletedFiles(id, baseDirectory, containerRootDirectory string) error { + deletedFiles, _, err := metadata.ReadContainerCheckpointDeletedFiles(baseDirectory) + if os.IsNotExist(errors.Unwrap(errors.Unwrap(err))) { + // No files to delete. Just return + return nil + } + + if err != nil { + return errors.Wrapf(err, "failed to read deleted files file") + } + + for _, deleteFile := range deletedFiles { + // Using RemoveAll as deletedFiles, which is generated from 'podman diff' + // lists completely deleted directories as a single entry: 'D /root'. + if err := os.RemoveAll(filepath.Join(containerRootDirectory, deleteFile)); err != nil { + return errors.Wrapf(err, "failed to delete files from container %s during restore", id) + } + } + + return nil +} + +// CRApplyRootFsDiffTar applies the tar archive found in baseDirectory with the +// root file system changes on top of containerRootDirectory +func CRApplyRootFsDiffTar(baseDirectory, containerRootDirectory string) error { + rootfsDiffPath := filepath.Join(baseDirectory, metadata.RootFsDiffTar) + if _, err := os.Stat(rootfsDiffPath); err != nil { + // Only do this if a rootfs-diff.tar actually exists + return nil + } + + rootfsDiffFile, err := os.Open(rootfsDiffPath) + if err != nil { + return errors.Wrap(err, "failed to open root file-system diff file") + } + defer rootfsDiffFile.Close() + + if err := archive.Untar(rootfsDiffFile, containerRootDirectory, nil); err != nil { + return errors.Wrapf(err, "failed to apply root file-system diff file %s", rootfsDiffPath) + } + + return nil +} + +// CRCreateRootFsDiffTar goes through the 'changes' and can create two files: +// * metadata.RootFsDiffTar will contain all new and changed files +// * metadata.DeletedFilesFile will contain a list of deleted files +// With these two files it is possible to restore the container file system to the same +// state it was during checkpointing. +// Changes to directories (owner, mode) are not handled. +func CRCreateRootFsDiffTar(changes *[]archive.Change, mountPoint, destination string) (includeFiles []string, err error) { + if len(*changes) == 0 { + return includeFiles, nil + } + + var rootfsIncludeFiles []string + var deletedFiles []string + + rootfsDiffPath := filepath.Join(destination, metadata.RootFsDiffTar) + + for _, file := range *changes { + if file.Kind == archive.ChangeAdd { + rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path) + continue + } + if file.Kind == archive.ChangeDelete { + deletedFiles = append(deletedFiles, file.Path) + continue + } + fileName, err := os.Stat(file.Path) + if err != nil { + continue + } + if !fileName.IsDir() && file.Kind == archive.ChangeModify { + rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path) + continue + } + } + + if len(rootfsIncludeFiles) > 0 { + rootfsTar, err := archive.TarWithOptions(mountPoint, &archive.TarOptions{ + Compression: archive.Uncompressed, + IncludeSourceDir: true, + IncludeFiles: rootfsIncludeFiles, + }) + if err != nil { + return includeFiles, errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath) + } + rootfsDiffFile, err := os.Create(rootfsDiffPath) + if err != nil { + return includeFiles, errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath) + } + defer rootfsDiffFile.Close() + if _, err = io.Copy(rootfsDiffFile, rootfsTar); err != nil { + return includeFiles, err + } + + includeFiles = append(includeFiles, metadata.RootFsDiffTar) + } + + if len(deletedFiles) == 0 { + return includeFiles, nil + } + + if _, err := metadata.WriteJSONFile(deletedFiles, destination, metadata.DeletedFilesFile); err != nil { + return includeFiles, nil + } + + includeFiles = append(includeFiles, metadata.DeletedFilesFile) + + return includeFiles, nil +} + +// CRCreateFileWithLabel creates an empty file and sets the corresponding ('fileLabel') +// SELinux label on the file. +// This is necessary for CRIU log files because CRIU infects the processes in +// the container with a 'parasite' and this will also try to write to the log files +// from the context of the container processes. +func CRCreateFileWithLabel(directory, fileName, fileLabel string) error { + logFileName := filepath.Join(directory, fileName) + + logFile, err := os.OpenFile(logFileName, os.O_CREATE, 0o600) + if err != nil { + return errors.Wrapf(err, "failed to create file %q", logFileName) + } + defer logFile.Close() + if err = label.SetFileLabel(logFileName, fileLabel); err != nil { + return errors.Wrapf(err, "failed to label file %q", logFileName) + } + + return nil +} + +// CRRuntimeSupportsCheckpointRestore tests if the given runtime at 'runtimePath' +// supports checkpointing. The checkpoint restore interface has no definition +// but crun implements all commands just as runc does. Whathh runc does it the +// official definition of the checkpoint/restore interface. +func CRRuntimeSupportsCheckpointRestore(runtimePath string) bool { + // Check if the runtime implements checkpointing. Currently only + // runc's and crun's checkpoint/restore implementation is supported. + cmd := exec.Command(runtimePath, "checkpoint", "--help") + if err := cmd.Start(); err != nil { + return false + } + if err := cmd.Wait(); err == nil { + return true + } + return false +} diff --git a/pkg/domain/entities/secrets.go b/pkg/domain/entities/secrets.go index 3481cbe05..8ede981da 100644 --- a/pkg/domain/entities/secrets.go +++ b/pkg/domain/entities/secrets.go @@ -42,6 +42,15 @@ type SecretInfoReport struct { Spec SecretSpec } +type SecretInfoReportCompat struct { + SecretInfoReport + Version SecretVersion +} + +type SecretVersion struct { + Index int +} + type SecretSpec struct { Name string Driver SecretDriverSpec @@ -78,6 +87,13 @@ type SwagSecretListResponse struct { Body []*SecretInfoReport } +// Secret list response +// swagger:response SecretListCompatResponse +type SwagSecretListCompatResponse struct { + // in:body + Body []*SecretInfoReportCompat +} + // Secret inspect response // swagger:response SecretInspectResponse type SwagSecretInspectResponse struct { @@ -85,6 +101,13 @@ type SwagSecretInspectResponse struct { Body SecretInfoReport } +// Secret inspect compat +// swagger:response SecretInspectCompatResponse +type SwagSecretInspectCompatResponse struct { + // in:body + Body SecretInfoReportCompat +} + // No such secret // swagger:response NoSuchSecret type SwagErrNoSuchSecret struct { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 562653403..ffd4856fe 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -583,8 +583,9 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie report.Deleted = append(report.Deleted, results.Deleted) report.Untagged = append(report.Untagged, results.Untagged...) return nil - case storage.ErrImageUnknown: - // The image must have been removed already (see #6510). + case storage.ErrImageUnknown, storage.ErrLayerUnknown: + // The image must have been removed already (see #6510) + // or the storage is corrupted (see #9617). report.Deleted = append(report.Deleted, img.ID()) report.Untagged = append(report.Untagged, img.ID()) return nil diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 23a9ce831..eb4dbc944 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -2,12 +2,14 @@ package generate import ( "context" + "path" "strings" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/image" + "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -157,8 +159,32 @@ func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { return true } +func getCGroupPermissons(unmask []string) string { + ro := "ro" + rw := "rw" + cgroup := "/sys/fs/cgroup" + + cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() + if !cgroupv2 { + return ro + } + + if unmask != nil && unmask[0] == "ALL" { + return rw + } + + for _, p := range unmask { + if path.Clean(p) == cgroup { + return rw + } + } + return ro +} + +// SpecGenToOCI returns the base configuration for the container. func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) { - cgroupPerm := "ro" + cgroupPerm := getCGroupPermissons(s.Unmask) + g, err := generate.New("linux") if err != nil { return nil, err |