aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/rootless-cni-infra/Containerfile5
-rw-r--r--contrib/rootless-cni-infra/README.md1
-rwxr-xr-xcontrib/rootless-cni-infra/rootless-cni-infra9
-rw-r--r--libpod/rootless_cni_linux.go2
-rw-r--r--pkg/api/handlers/compat/containers_attach.go6
-rw-r--r--pkg/api/handlers/compat/exec.go6
-rw-r--r--pkg/api/handlers/libpod/images.go47
-rw-r--r--pkg/api/server/idle/tracker.go96
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/register_images.go2
-rw-r--r--pkg/api/server/server.go22
-rw-r--r--pkg/domain/infra/tunnel/images.go8
-rw-r--r--test/e2e/untag_test.go1
13 files changed, 158 insertions, 121 deletions
diff --git a/contrib/rootless-cni-infra/Containerfile b/contrib/rootless-cni-infra/Containerfile
index 6bf70d644..dd80fda28 100644
--- a/contrib/rootless-cni-infra/Containerfile
+++ b/contrib/rootless-cni-infra/Containerfile
@@ -2,8 +2,7 @@ ARG GOLANG_VERSION=1.15
ARG ALPINE_VERSION=3.12
ARG CNI_VERSION=v0.8.0
ARG CNI_PLUGINS_VERSION=v0.8.7
-# Aug 20, 2020
-ARG DNSNAME_VESION=78b4da7bbfc51c27366da630e1df1c4f2e8b1b5b
+ARG DNSNAME_VESION=v1.0.0
FROM golang:${GOLANG_VERSION}-alpine${ALPINE_VERSION} AS golang-base
RUN apk add --no-cache git
@@ -34,4 +33,4 @@ COPY rootless-cni-infra /usr/local/bin
ENV CNI_PATH=/opt/cni/bin
CMD ["sleep", "infinity"]
-ENV ROOTLESS_CNI_INFRA_VERSION=2
+ENV ROOTLESS_CNI_INFRA_VERSION=3
diff --git a/contrib/rootless-cni-infra/README.md b/contrib/rootless-cni-infra/README.md
index 5aa13374b..c43b4cf49 100644
--- a/contrib/rootless-cni-infra/README.md
+++ b/contrib/rootless-cni-infra/README.md
@@ -22,3 +22,4 @@ The container images live on `quay.io/libpod/rootless-cni-infra`. The tags have
* `/run/rootless-cni-infra/${CONTAINER_ID}/pid`: PID of the `sleep infinity` process that corresponds to the allocated netns
* `/run/rootless-cni-infra/${CONTAINER_ID}/attached/${NETWORK_NAME}`: CNI result
+* `/run/rootless-cni-infra/${CONTAINER_ID}/attached-args/${NETWORK_NAME}`: CNI args
diff --git a/contrib/rootless-cni-infra/rootless-cni-infra b/contrib/rootless-cni-infra/rootless-cni-infra
index 5cb43621d..463254c7f 100755
--- a/contrib/rootless-cni-infra/rootless-cni-infra
+++ b/contrib/rootless-cni-infra/rootless-cni-infra
@@ -33,7 +33,7 @@ cmd_entrypoint_alloc() {
K8S_POD_NAME="$3"
dir="${BASE}/${ID}"
- mkdir -p "${dir}/attached"
+ mkdir -p "${dir}/attached" "${dir}/attached-args"
pid=""
if [ -f "${dir}/pid" ]; then
@@ -50,6 +50,7 @@ cmd_entrypoint_alloc() {
CNI_IFNAME="eth${nwcount}"
export CNI_ARGS CNI_IFNAME
cnitool add "${NET}" "/proc/${pid}/ns/net" >"${dir}/attached/${NET}"
+ echo "${CNI_ARGS}" >"${dir}/attached-args/${NET}"
# return the result
ns="/proc/${pid}/ns/net"
@@ -71,8 +72,12 @@ cmd_entrypoint_dealloc() {
exit 0
fi
pid=$(cat "${dir}/pid")
+ if [ -f "${dir}/attached-args/${NET}" ]; then
+ CNI_ARGS=$(cat "${dir}/attached-args/${NET}")
+ export CNI_ARGS
+ fi
cnitool del "${NET}" "/proc/${pid}/ns/net"
- rm -f "${dir}/attached/${NET}"
+ rm -f "${dir}/attached/${NET}" "${dir}/attached-args/${NET}"
nwcount=$(find "${dir}/attached" -type f | wc -l)
if [ "${nwcount}" = 0 ]; then
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 2877191e5..21e43ebd0 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -25,7 +25,7 @@ import (
// Built from ../contrib/rootless-cni-infra.
var rootlessCNIInfraImage = map[string]string{
- "amd64": "quay.io/libpod/rootless-cni-infra@sha256:e92c3a6367f8e554121b96d39af1f19f0f9ac5a32922b290112e13bc661d3a29", // 2-amd64
+ "amd64": "quay.io/libpod/rootless-cni-infra@sha256:304742d5d221211df4ec672807a5842ff11e3729c50bc424ea0cea858f69d7b7", // 3-amd64
}
const (
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index e20d48d86..4a1196c89 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -6,7 +6,7 @@ import (
"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/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -92,7 +92,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
// Perform HTTP attach.
@@ -109,7 +109,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index 1db950f85..df51293c2 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -10,7 +10,7 @@ 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/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/containers/podman/v2/pkg/specgen/generate"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@@ -174,7 +174,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan); err != nil {
@@ -186,7 +186,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index bc1bdc287..2f9c10746 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -560,24 +560,41 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
func UntagImage(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := utils.GetName(r)
- newImage, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
- return
- }
- tag := "latest"
- if len(r.Form.Get("tag")) > 0 {
- tag = r.Form.Get("tag")
- }
- if len(r.Form.Get("repo")) < 1 {
+ tags := []string{} // Note: if empty, all tags will be removed from the image.
+ repo := r.Form.Get("repo")
+ tag := r.Form.Get("tag")
+
+ // Do the parameter dance.
+ switch {
+ // If tag is set, repo must be as well.
+ case len(repo) == 0 && len(tag) > 0:
utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
return
+
+ case len(repo) == 0:
+ break
+
+ // If repo is specified, we need to add that to the tags.
+ default:
+ if len(tag) == 0 {
+ // Normalize tag to "latest" if empty.
+ tag = "latest"
+ }
+ tags = append(tags, fmt.Sprintf("%s:%s", repo, tag))
}
- repo := r.Form.Get("repo")
- tagName := fmt.Sprintf("%s:%s", repo, tag)
- if err := newImage.UntagImage(tagName); err != nil {
- utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ opts := entities.ImageUntagOptions{}
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ name := utils.GetName(r)
+ if err := imageEngine.Untag(r.Context(), name, tags, opts); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchImage {
+ utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
+ } else {
+ utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+ }
return
}
utils.WriteResponse(w, http.StatusCreated, "")
diff --git a/pkg/api/server/idle/tracker.go b/pkg/api/server/idle/tracker.go
new file mode 100644
index 000000000..1b378c492
--- /dev/null
+++ b/pkg/api/server/idle/tracker.go
@@ -0,0 +1,96 @@
+package idle
+
+import (
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+// Tracker holds the state for the server's idle tracking
+type Tracker struct {
+ // Duration is the API idle window
+ Duration time.Duration
+ hijacked int // count of active connections managed by handlers
+ managed map[net.Conn]struct{} // set of active connections managed by http package
+ mux sync.Mutex // protect managed map
+ timer *time.Timer
+ total int // total number of connections made to this server instance
+}
+
+// NewTracker creates and initializes a new Tracker object
+// For best behavior, duration should be 2x http idle connection timeout
+func NewTracker(idle time.Duration) *Tracker {
+ return &Tracker{
+ managed: make(map[net.Conn]struct{}),
+ Duration: idle,
+ timer: time.NewTimer(idle),
+ }
+}
+
+// ConnState is called on HTTP connection state changes.
+// - Once StateHijacked, StateClose is _NOT_ called on that connection
+// - There are two "idle" timeouts, the http idle connection (not to be confused with the TCP/IP idle socket timeout)
+// and the API idle window. The caller should set the http idle timeout to 2x the time provided to NewTacker() which
+// is the API idle window.
+func (t *Tracker) ConnState(conn net.Conn, state http.ConnState) {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections())
+ switch state {
+ case http.StateNew, http.StateActive:
+ // stop the API timer when the server transitions any connection to an "active" state
+ t.managed[conn] = struct{}{}
+ t.timer.Stop()
+ t.total++
+ case http.StateHijacked:
+ // hijacked connections should call Close() when finished.
+ // Note: If a handler hijack's a connection and then doesn't Close() it,
+ // the API timer will not fire and the server will _NOT_ timeout.
+ delete(t.managed, conn)
+ t.hijacked++
+ case http.StateIdle:
+ // When any connection goes into the http idle state, we know:
+ // - we have an active connection
+ // - the API timer should not be counting down (See case StateNew/StateActive)
+ break
+ case http.StateClosed:
+ oldActive := t.ActiveConnections()
+
+ // Either the server or a hijacking handler has closed the http connection to a client
+ if _, found := t.managed[conn]; found {
+ delete(t.managed, conn)
+ } else {
+ t.hijacked-- // guarded by t.mux above
+ }
+
+ // Transitioned from any "active" connection to no connections
+ if oldActive > 0 && t.ActiveConnections() == 0 {
+ t.timer.Stop() // See library source for Reset() issues and why they are not fixed
+ t.timer.Reset(t.Duration) // Restart the API window timer
+ }
+ }
+}
+
+// Close is used to update Tracker that a StateHijacked connection has been closed by handler (StateClosed)
+func (t *Tracker) Close() {
+ t.ConnState(nil, http.StateClosed)
+}
+
+// ActiveConnections returns the number of current managed or StateHijacked connections
+func (t *Tracker) ActiveConnections() int {
+ return len(t.managed) + t.hijacked
+}
+
+// TotalConnections returns total number of connections made to this instance of the service
+func (t *Tracker) TotalConnections() int {
+ return t.total
+}
+
+// Done is called when idle timer has expired
+func (t *Tracker) Done() <-chan time.Time {
+ return t.timer.C
+}
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
deleted file mode 100644
index 1ee905a99..000000000
--- a/pkg/api/server/idletracker/idletracker.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package idletracker
-
-import (
- "net"
- "net/http"
- "sync"
- "time"
-
- "github.com/sirupsen/logrus"
-)
-
-type IdleTracker struct {
- http map[net.Conn]struct{}
- hijacked int
- total int
- mux sync.Mutex
- timer *time.Timer
- Duration time.Duration
-}
-
-func NewIdleTracker(idle time.Duration) *IdleTracker {
- return &IdleTracker{
- http: make(map[net.Conn]struct{}),
- Duration: idle,
- timer: time.NewTimer(idle),
- }
-}
-
-func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- oldActive := t.ActiveConnections()
- logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, oldActive, t.TotalConnections())
- switch state {
- case http.StateNew, http.StateActive:
- t.http[conn] = struct{}{}
- // stop the timer if we transitioned from idle
- if oldActive == 0 {
- t.timer.Stop()
- }
- t.total++
- case http.StateHijacked:
- // hijacked connections are handled elsewhere
- delete(t.http, conn)
- t.hijacked++
- case http.StateIdle, http.StateClosed:
- delete(t.http, conn)
- // Restart the timer if we've become idle
- if oldActive > 0 && len(t.http) == 0 {
- t.timer.Stop()
- t.timer.Reset(t.Duration)
- }
- }
-}
-
-func (t *IdleTracker) TrackHijackedClosed() {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- t.hijacked--
-}
-
-func (t *IdleTracker) ActiveConnections() int {
- return len(t.http) + t.hijacked
-}
-
-func (t *IdleTracker) TotalConnections() int {
- return t.total
-}
-
-func (t *IdleTracker) Done() <-chan time.Time {
- return t.timer.C
-}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index b1007fe09..32e401a54 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1175,7 +1175,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// tags:
// - images
// summary: Untag an image
- // description: Untag an image
+ // description: Untag an image. If not repo and tag are specified, all tags are removed from the image.
// parameters:
// - in: path
// name: name:.*
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index e7c031234..09a9f6370 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -16,7 +16,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
@@ -26,14 +26,14 @@ import (
)
type APIServer struct {
- http.Server // The HTTP work happens here
- *schema.Decoder // Decoder for Query parameters to structs
- context.Context // Context to carry objects to handlers
- *libpod.Runtime // Where the real work happens
- net.Listener // mux for routing HTTP API calls to libpod routines
- context.CancelFunc // Stop APIServer
- idleTracker *idletracker.IdleTracker // Track connections to support idle shutdown
- pprof *http.Server // Sidecar http server for providing performance data
+ http.Server // The HTTP work happens here
+ *schema.Decoder // Decoder for Query parameters to structs
+ context.Context // Context to carry objects to handlers
+ *libpod.Runtime // Where the real work happens
+ net.Listener // mux for routing HTTP API calls to libpod routines
+ context.CancelFunc // Stop APIServer
+ idleTracker *idle.Tracker // Track connections to support idle shutdown
+ pprof *http.Server // Sidecar http server for providing performance data
}
// Number of seconds to wait for next request, if exceeded shutdown server
@@ -70,13 +70,13 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := idletracker.NewIdleTracker(duration)
+ idle := idle.NewTracker(duration)
server := APIServer{
Server: http.Server{
Handler: router,
ReadHeaderTimeout: 20 * time.Second,
- IdleTimeout: duration,
+ IdleTimeout: duration * 2,
ConnState: idle.ConnState,
ErrorLog: log.New(logrus.StandardLogger().Out, "", 0),
},
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 981884109..61ac2141c 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
- "github.com/containers/podman/v2/pkg/bindings"
images "github.com/containers/podman/v2/pkg/bindings/images"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/utils"
@@ -139,13 +138,8 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string,
}
func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
- // Remove all tags if none are provided
if len(tags) == 0 {
- newImage, err := images.GetImage(ir.ClientCxt, nameOrID, bindings.PFalse)
- if err != nil {
- return err
- }
- tags = newImage.NamesHistory
+ return images.Untag(ir.ClientCxt, nameOrID, "", "")
}
for _, newTag := range tags {
diff --git a/test/e2e/untag_test.go b/test/e2e/untag_test.go
index 7766ce634..91a933090 100644
--- a/test/e2e/untag_test.go
+++ b/test/e2e/untag_test.go
@@ -33,7 +33,6 @@ var _ = Describe("Podman untag", func() {
})
It("podman untag all", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
setup := podmanTest.PodmanNoCache([]string{"pull", ALPINE})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))