Merge pull request #42263 from AkihiroSuda/move-cgroup2-out-of-experimental-20.10

[20.10 backport] Move cgroup v2 out of experimental
diff --git a/Jenkinsfile b/Jenkinsfile
index 03a3572..68e73a3 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -939,7 +939,7 @@
                         beforeAgent true
                         expression { params.arm64 }
                     }
-                    agent { label 'arm64 && linux' }
+                    agent { label 'arm64 && ubuntu-2004' }
                     environment {
                         TEST_SKIP_INTEGRATION_CLI = '1'
                     }
diff --git a/contrib/dockerd-rootless-setuptool.sh b/contrib/dockerd-rootless-setuptool.sh
index 66f2093..4864b0a 100755
--- a/contrib/dockerd-rootless-setuptool.sh
+++ b/contrib/dockerd-rootless-setuptool.sh
@@ -23,6 +23,7 @@
 # constants
 DOCKERD_ROOTLESS_SH="dockerd-rootless.sh"
 SYSTEMD_UNIT="docker.service"
+CLI_CONTEXT="rootless"
 
 # CLI opt: --force
 OPT_FORCE=""
@@ -350,6 +351,23 @@
 	echo
 }
 
+cli_ctx_exists() {
+	name="$1"
+	"${BIN}/docker" context inspect -f "{{.Name}}" "${name}" > /dev/null 2>&1
+}
+
+cli_ctx_create() {
+	name="$1"
+	host="$2"
+	description="$3"
+	"${BIN}/docker" context create "${name}" --docker "host=${host}" --description "${description}" > /dev/null
+}
+
+cli_ctx_rm() {
+	name="$1"
+	"${BIN}/docker" context rm -f "${name}" > /dev/null
+}
+
 # CLI subcommand: "install"
 cmd_entrypoint_install() {
 	# requirements are already checked in init()
@@ -359,6 +377,14 @@
 		install_systemd
 	fi
 
+	if cli_ctx_exists "${CLI_CONTEXT}"; then
+		INFO "CLI context \"${CLI_CONTEXT}\" already exists"
+	else
+		INFO "Creating CLI context \"${CLI_CONTEXT}\""
+		cli_ctx_create "${CLI_CONTEXT}" "unix://${XDG_RUNTIME_DIR}/docker.sock" "Rootless mode"
+	fi
+
+	echo
 	INFO "Make sure the following environment variables are set (or add them to ~/.bashrc):"
 	echo
 	if [ -n "$XDG_RUNTIME_DIR_CREATED" ]; then
@@ -390,6 +416,11 @@
 		INFO "Uninstalled ${SYSTEMD_UNIT}"
 	fi
 
+	if cli_ctx_exists "${CLI_CONTEXT}"; then
+		cli_ctx_rm "${CLI_CONTEXT}"
+		INFO "Deleted CLI context \"${CLI_CONTEXT}\""
+	fi
+
 	INFO "This uninstallation tool does NOT remove Docker binaries and data."
 	INFO "To remove data, run: \`$BIN/rootlesskit rm -rf $HOME/.local/share/docker\`"
 }
diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go
index f912747..0499489 100644
--- a/daemon/graphdriver/btrfs/btrfs.go
+++ b/daemon/graphdriver/btrfs/btrfs.go
@@ -633,7 +633,14 @@
 	d.updateQuotaStatus()
 
 	if err := subvolDelete(d.subvolumesDir(), id, d.quotaEnabled); err != nil {
-		return err
+		if d.quotaEnabled {
+			return err
+		}
+		// If quota is not enabled, fallback to rmdir syscall to delete subvolumes.
+		// This would allow unprivileged user to delete their owned subvolumes
+		// in kernel >= 4.18 without user_subvol_rm_allowed mount option.
+		//
+		// From https://github.com/containers/storage/pull/508/commits/831e32b6bdcb530acc4c1cb9059d3c6dba14208c
 	}
 	if err := system.EnsureRemoveAll(dir); err != nil {
 		return err
diff --git a/integration/plugin/common/plugin_test.go b/integration/plugin/common/plugin_test.go
index 055381d..a5568cc 100644
--- a/integration/plugin/common/plugin_test.go
+++ b/integration/plugin/common/plugin_test.go
@@ -15,12 +15,17 @@
 	"strings"
 	"testing"
 
+	"github.com/containerd/containerd/images"
+	"github.com/containerd/containerd/remotes/docker"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/testutil/daemon"
 	"github.com/docker/docker/testutil/fixtures/plugin"
 	"github.com/docker/docker/testutil/registry"
 	"github.com/docker/docker/testutil/request"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
 	is "gotest.tools/v3/assert/cmp"
 	"gotest.tools/v3/skip"
 )
@@ -223,3 +228,61 @@
 		assert.NilError(t, err)
 	})
 }
+
+func TestPluginBackCompatMediaTypes(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)")
+
+	defer setupTest(t)()
+
+	reg := registry.NewV2(t)
+	defer reg.Close()
+	reg.WaitReady(t)
+
+	repo := path.Join(registry.DefaultURL, strings.ToLower(t.Name())+":latest")
+
+	client := testEnv.APIClient()
+
+	ctx := context.Background()
+	assert.NilError(t, plugin.Create(ctx, client, repo))
+
+	rdr, err := client.PluginPush(ctx, repo, "")
+	assert.NilError(t, err)
+	defer rdr.Close()
+
+	buf := &strings.Builder{}
+	assert.NilError(t, jsonmessage.DisplayJSONMessagesStream(rdr, buf, 0, false, nil), buf)
+
+	// Use custom header here because older versions of the registry do not
+	// parse the accept header correctly and does not like the accept header
+	// that the default resolver code uses. "Older registries" here would be
+	// like the one currently included in the test suite.
+	headers := http.Header{}
+	headers.Add("Accept", images.MediaTypeDockerSchema2Manifest)
+
+	resolver := docker.NewResolver(docker.ResolverOptions{
+		Headers: headers,
+	})
+	assert.NilError(t, err)
+
+	n, desc, err := resolver.Resolve(ctx, repo)
+	assert.NilError(t, err, repo)
+
+	fetcher, err := resolver.Fetcher(ctx, n)
+	assert.NilError(t, err)
+
+	rdr, err = fetcher.Fetch(ctx, desc)
+	assert.NilError(t, err)
+	defer rdr.Close()
+
+	type manifest struct {
+		MediaType string
+		v1.Manifest
+	}
+	var m manifest
+	assert.NilError(t, json.NewDecoder(rdr).Decode(&m))
+	assert.Check(t, cmp.Equal(m.MediaType, images.MediaTypeDockerSchema2Manifest))
+	assert.Check(t, cmp.Len(m.Layers, 1))
+	assert.Check(t, cmp.Equal(m.Layers[0].MediaType, images.MediaTypeDockerSchema2LayerGzip))
+}
diff --git a/integration/service/inspect_test.go b/integration/service/inspect_test.go
index 7b6d6ff..5d8f12d 100644
--- a/integration/service/inspect_test.go
+++ b/integration/service/inspect_test.go
@@ -88,7 +88,7 @@
 				Image:           "busybox:latest",
 				Labels:          map[string]string{"container-label": "container-value"},
 				Command:         []string{"/bin/top"},
-				Args:            []string{"-u", "root"},
+				Args:            []string{"-d", "5"},
 				Hostname:        "hostname",
 				Env:             []string{"envvar=envvalue"},
 				Dir:             "/work",
diff --git a/plugin/backend_linux.go b/plugin/backend_linux.go
index 7bb3c42..88ab8b3 100644
--- a/plugin/backend_linux.go
+++ b/plugin/backend_linux.go
@@ -492,7 +492,7 @@
 			return m, errors.Wrapf(err, "error fetching info for content digest %s", l)
 		}
 		m.Layers = append(m.Layers, specs.Descriptor{
-			MediaType: specs.MediaTypeImageLayerGzip, // TODO: This is assuming everything is a gzip compressed layer, but that may not be true.
+			MediaType: images.MediaTypeDockerSchema2LayerGzip, // TODO: This is assuming everything is a gzip compressed layer, but that may not be true.
 			Digest:    l,
 			Size:      info.Size,
 		})