Build standalone containerd

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
This commit is contained in:
Brad Davidson
2021-12-02 12:51:57 -08:00
committed by Brad Davidson
parent 8737e2e13f
commit 6f4217a340
200 changed files with 42313 additions and 1215 deletions

View File

@@ -182,13 +182,13 @@ func getAssetAndDir(dataDir string) (string, string) {
func extract(dataDir string) (string, error) {
// first look for global asset folder so we don't create a HOME version if not needed
_, dir := getAssetAndDir(datadir.DefaultDataDir)
if _, err := os.Stat(filepath.Join(dir, "bin", "containerd")); err == nil {
if _, err := os.Stat(filepath.Join(dir, "bin", "k3s")); err == nil {
return dir, nil
}
asset, dir := getAssetAndDir(dataDir)
// check if target content already exists
if _, err := os.Stat(filepath.Join(dir, "bin", "containerd")); err == nil {
if _, err := os.Stat(filepath.Join(dir, "bin", "k3s")); err == nil {
return dir, nil
}

26
go.mod
View File

@@ -5,17 +5,7 @@ go 1.16
replace (
github.com/Microsoft/hcsshim => github.com/Microsoft/hcsshim v0.8.20
github.com/benmoss/go-powershell => github.com/k3s-io/go-powershell v0.0.0-20201118222746-51f4c451fbd7
github.com/containerd/aufs => github.com/containerd/aufs v1.0.0
github.com/containerd/btrfs => github.com/containerd/btrfs v1.0.0
github.com/containerd/cgroups => github.com/containerd/cgroups v1.0.1
github.com/containerd/console => github.com/containerd/console v1.0.2
github.com/containerd/containerd => github.com/k3s-io/containerd v1.5.8-k3s1 // k3s-release/1.5
github.com/containerd/continuity => github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1
github.com/containerd/fifo => github.com/containerd/fifo v1.0.0
github.com/containerd/go-runc => github.com/containerd/go-runc v1.0.0
github.com/containerd/ttrpc => github.com/containerd/ttrpc v1.0.2
github.com/containerd/typeurl => github.com/containerd/typeurl v1.0.2
github.com/containerd/zfs => github.com/containerd/zfs v1.0.0
github.com/containerd/containerd => github.com/k3s-io/containerd v1.5.8-k3s2 // k3s-release/1.5
github.com/coreos/go-systemd => github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker => github.com/docker/docker v20.10.2+incompatible
@@ -25,7 +15,6 @@ replace (
github.com/juju/errors => github.com/k3s-io/nocode v0.0.0-20200630202308-cb097102c09f
github.com/kubernetes-sigs/cri-tools => github.com/k3s-io/cri-tools v1.21.0-k3s1
github.com/matryer/moq => github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009
// LOOK TO scripts/download FOR THE VERSION OF runc THAT WE ARE BUILDING/SHIPPING
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.3
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20210316141917-a8c4a9ee0f6b
github.com/rancher/k3s/pkg/data => ./pkg/data
@@ -77,9 +66,9 @@ replace (
require (
github.com/Microsoft/hcsshim v0.8.23
github.com/containerd/cgroups v1.0.1
github.com/containerd/containerd v1.5.8
github.com/containerd/fuse-overlayfs-snapshotter v1.0.3
github.com/containerd/stargz-snapshotter v0.8.0
github.com/containerd/containerd v1.6.0-beta.2.0.20211117185425-a776a27af54a
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4
github.com/containerd/stargz-snapshotter v0.10.1
github.com/coreos/go-iptables v0.5.0
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/docker/docker v20.10.10+incompatible
@@ -102,7 +91,6 @@ require (
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.11.0
// LOOK TO scripts/download FOR THE VERSION OF runc THAT WE ARE BUILDING/SHIPPING
github.com/opencontainers/runc v1.0.3
github.com/opencontainers/selinux v1.8.2
github.com/otiai10/copy v1.6.0
@@ -125,8 +113,8 @@ require (
go.etcd.io/etcd/server/v3 v3.5.0
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20211005215030-d2e5035098b3
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef
google.golang.org/grpc v1.41.0
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359
google.golang.org/grpc v1.42.0
gopkg.in/yaml.v2 v2.4.0
inet.af/tcpproxy v0.0.0-20200125044825-b6bb9b5b8252
k8s.io/api v0.22.4
@@ -136,7 +124,7 @@ require (
k8s.io/cloud-provider v0.22.4
k8s.io/component-base v0.22.4
k8s.io/controller-manager v0.22.4 // indirect
k8s.io/cri-api v0.22.4
k8s.io/cri-api v0.23.0-alpha.4
k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.9.0
k8s.io/kubectl v0.22.4

59
go.sum
View File

@@ -1,4 +1,5 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -181,12 +182,18 @@ github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5Grn
github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
github.com/containerd/console v1.0.2 h1:Pi6D+aZXM+oUw1czuKgH5IJ+y0jhYcwBJfx5/Ghn9dE=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
github.com/containerd/continuity v0.2.1 h1:/EeEo2EtN3umhbbgCveyjifoMYg0pS+nMMEemaYw634=
github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg=
github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.3 h1:eKLaaYhywg13uXYP7d1gH95b3w6SdQFuu0wxpzzhroc=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.3/go.mod h1:qRtosYylg3OWvie15wMmyaOh/n5+BCW59J0tMH5R+F0=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4 h1:UKlk0rhAq1NW5UEmu9/4f3m9v+O/0+F0as+QeK1UmCM=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4/go.mod h1:UPFCs/OiJHa4MoghjGZdPaOBcZEhATfYG1gSutAZTvE=
github.com/containerd/go-cni v1.0.2 h1:YbJAhpTevL2v6u8JC1NhCYRwf+3Vzxcc5vGnYoJ7VeE=
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
@@ -195,13 +202,15 @@ github.com/containerd/imgcrypt v1.1.1 h1:LBwiTfoUsdiEGAR1TpvxE+Gzt7469oVu87iR3mv
github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
github.com/containerd/nri v0.1.0 h1:6QioHRlThlKh2RkRTR4kIT3PKAcrLo3gIWnjkM4dQmQ=
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/stargz-snapshotter v0.8.0 h1:dpcv7te4ef8dNQMgN6zzbd4IP1puSFIXqFbqZlKFvd4=
github.com/containerd/stargz-snapshotter v0.8.0/go.mod h1:1BffdxelEfp8ct4oI7uy2mfmvMDE2o3SPe32AtFyYXk=
github.com/containerd/stargz-snapshotter/estargz v0.8.0/go.mod h1:mwIwuwb+D8FX2t45Trwi0hmWmZm5VW7zPP/rekwhWQU=
github.com/containerd/stargz-snapshotter/estargz v0.9.0 h1:PkB6BSTfOKX23erT2GkoUKkJEcXfNcyKskIViK770v8=
github.com/containerd/stargz-snapshotter v0.10.1 h1:A4nCRPWZmlYGpPNTmmktREqca1WM2IevonzQcIjCZwM=
github.com/containerd/stargz-snapshotter v0.10.1/go.mod h1:F/5gQZTyx18Kj+4VnE4Kcwg7BtESs7L/V29jamTI43k=
github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0=
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng=
github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI=
github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY=
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
@@ -260,9 +269,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v20.10.8+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v20.10.9+incompatible h1:OJ7YkwQA+k2Oi51lmCojpjiygKpi76P7bg91b2eJxYU=
github.com/docker/cli v20.10.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v20.10.10+incompatible h1:kcbwdgWbrBOH8QwQzaJmyriHwF7XIl4HT1qh0HTRys4=
github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.2+incompatible h1:vFgEHPqWBTp4pTjdLwjAA4bSo3gvIGOYwuJTlEjVBCw=
@@ -397,6 +406,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -522,20 +533,23 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc=
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek=
github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc=
github.com/hanwen/go-fuse/v2 v2.1.1-0.20210825171523-3ab5d95a30ae h1:4CB6T4YTUVvnro5ba8ju1QCbOuyGAeF3vvKlo50EJ4k=
github.com/hanwen/go-fuse/v2 v2.1.1-0.20210825171523-3ab5d95a30ae/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -593,10 +607,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k3s-io/containerd v1.5.8-k3s1 h1:1w0neoAPwO9Dounkdgi2GMCJc48iKcXgxu1vN0YUigQ=
github.com/k3s-io/containerd v1.5.8-k3s1/go.mod h1:DltFRoJAo5exp9diH/w/hyg8ZGej9AJ61qVi4ZQqShc=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1 h1:KEz2rd9IDbrQT8w6RibEYlwfTXiu0P6hQDE+6O4IJdI=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/k3s-io/containerd v1.5.8-k3s2 h1:zqiSObegytSfZnkY6ZO0NZJDT27zNNwuwxoszGvWw5s=
github.com/k3s-io/containerd v1.5.8-k3s2/go.mod h1:mPi6aT1rbxG3ilLkj0fmZDn9VpSW/PUR615wfPqRCqc=
github.com/k3s-io/cri-tools v1.21.0-k3s1 h1:MWQtAsx4HCNXenqU/B4V9eU6HMyafkd1PnW6d4HCfos=
github.com/k3s-io/cri-tools v1.21.0-k3s1/go.mod h1:Qsz54zxINPR+WVWX9Kc3CTmuDFB1dNLCNV8jE8lUbtU=
github.com/k3s-io/etcd/api/v3 v3.5.0-k3s2 h1:56a/c/Mo75uU1k3erdgWk1C7dGUL5rSpNdL2B6zftKo=
@@ -672,8 +684,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@@ -769,8 +779,9 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/symlink v0.1.0 h1:MTFZ74KtNI6qQQpuBxU+uKCim4WtOMokr03hCfJcazE=
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
@@ -857,9 +868,9 @@ github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
@@ -1016,6 +1027,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/ugorji/go v0.0.0-20170107133203-ded73eae5db7/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@@ -1423,7 +1435,6 @@ k8s.io/system-validators v1.5.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6g
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=

View File

@@ -4,9 +4,6 @@ COPY build/out/data.tar.zst /
RUN mkdir -p /image/etc/ssl/certs /image/run /image/var/run /image/tmp /image/lib/modules /image/lib/firmware && \
tar -xa -C /image -f /data.tar.zst && \
cp /etc/ssl/certs/ca-certificates.crt /image/etc/ssl/certs/ca-certificates.crt
RUN cd image/bin && \
rm -f k3s && \
ln -s k3s-server k3s
FROM scratch
ARG VERSION="dev"

View File

@@ -22,9 +22,9 @@ import (
_ "github.com/containerd/containerd/metrics/cgroups"
_ "github.com/containerd/containerd/runtime/v2"
_ "github.com/containerd/containerd/runtime/v2/runc/options"
_ "github.com/containerd/containerd/snapshots/btrfs/plugin"
_ "github.com/containerd/containerd/snapshots/native/plugin"
_ "github.com/containerd/containerd/snapshots/overlay/plugin"
_ "github.com/containerd/containerd/snapshots/btrfs/plugin"
_ "github.com/containerd/fuse-overlayfs-snapshotter/plugin"
_ "github.com/containerd/stargz-snapshotter/service/plugin"
)

View File

@@ -8,7 +8,10 @@ fi
. ./scripts/version.sh
MAX_BINARY_SIZE=61000000
# Try to keep the K3s binary under 64 megabytes.
# "64M ought to be enough for anybody"
MAX_BINARY_MB=64
MAX_BINARY_SIZE=$((MAX_BINARY_MB * 1024 * 1024))
BIN_SUFFIX="-${ARCH}"
if [ ${ARCH} = amd64 ]; then
BIN_SUFFIX=""
@@ -20,9 +23,9 @@ CMD_NAME="dist/artifacts/k3s${BIN_SUFFIX}"
SIZE=$(stat -c '%s' ${CMD_NAME})
if [ ${SIZE} -gt ${MAX_BINARY_SIZE} ]; then
echo "k3s binary ${CMD_NAME} size ${SIZE} exceeds max acceptable size of ${MAX_BINARY_SIZE} bytes"
exit 1
echo "k3s binary ${CMD_NAME} size ${SIZE} exceeds max acceptable size of ${MAX_BINARY_SIZE} bytes (${MAX_BINARY_MB} MiB)"
exit 1
fi
echo "k3s binary ${CMD_NAME} size ${SIZE} is less than max acceptable size of ${MAX_BINARY_SIZE} bytes"
echo "k3s binary ${CMD_NAME} size ${SIZE} is less than max acceptable size of ${MAX_BINARY_SIZE} bytes (${MAX_BINARY_MB} MiB)"
exit 0

View File

@@ -10,7 +10,7 @@ GO=${GO-go}
PKG="github.com/rancher/k3s"
PKG_CONTAINERD="github.com/containerd/containerd"
PKG_K3S_CONTAINERD="github.com/k3s-io/containerd"
PKG_CRICTL="github.com/kubernetes-sigs/cri-tools"
PKG_CRICTL="github.com/kubernetes-sigs/cri-tools/pkg"
PKG_K8S_BASE="k8s.io/component-base"
PKG_K8S_CLIENT="k8s.io/client-go/pkg"
@@ -35,7 +35,9 @@ VERSIONFLAGS="
-X ${VENDOR_PREFIX}${PKG_CONTAINERD}/version.Version=${VERSION_CONTAINERD}
-X ${VENDOR_PREFIX}${PKG_CONTAINERD}/version.Package=${PKG_K3S_CONTAINERD}
-X ${VENDOR_PREFIX}${PKG_CRICTL}/pkg/version.Version=${VERSION_CRICTL}
-X ${PKG_CONTAINERD}/version.Version=${VERSION_CONTAINERD}
-X ${PKG_CONTAINERD}/version.Package=${PKG_K3S_CONTAINERD}
"
LDFLAGS="
-w -s"
@@ -43,7 +45,7 @@ LDFLAGS="
STATIC="
-extldflags '-static -lm -ldl -lz -lpthread'
"
TAGS="ctrd apparmor seccomp netcgo osusergo providerless"
TAGS="apparmor seccomp netcgo osusergo providerless"
RUNC_TAGS="apparmor seccomp"
RUNC_STATIC="static"
@@ -101,22 +103,29 @@ if [ ! -x ${INSTALLBIN}/cni ]; then
fi
echo Building k3s
CGO_ENABLED=1 "${GO}" build -tags "$TAGS" -ldflags "$VERSIONFLAGS $LDFLAGS $STATIC" -o bin/containerd ./cmd/server/main.go
ln -s containerd ./bin/k3s-agent
ln -s containerd ./bin/k3s-server
ln -s containerd ./bin/k3s-etcd-snapshot
ln -s containerd ./bin/k3s-secrets-encrypt
ln -s containerd ./bin/k3s-certificate
ln -s containerd ./bin/kubectl
ln -s containerd ./bin/crictl
ln -s containerd ./bin/ctr
CGO_ENABLED=1 "${GO}" build -tags "$TAGS" -ldflags "$VERSIONFLAGS $LDFLAGS $STATIC" -o bin/k3s ./cmd/server/main.go
ln -s k3s ./bin/k3s-agent
ln -s k3s ./bin/k3s-server
ln -s k3s ./bin/k3s-etcd-snapshot
ln -s k3s ./bin/k3s-secrets-encrypt
ln -s k3s ./bin/k3s-certificate
ln -s k3s ./bin/kubectl
ln -s k3s ./bin/crictl
ln -s k3s ./bin/ctr
export GOPATH=$(pwd)/build
echo Building containerd
pushd ./build/src/github.com/containerd/containerd
TAGS="${TAGS/netcgo/netgo}"
CGO_ENABLED=1 "${GO}" build -tags "$TAGS" -ldflags "$VERSIONFLAGS $LDFLAGS $STATIC" -o bin/containerd ./cmd/containerd
CGO_ENABLED=1 "${GO}" build -tags "$TAGS" -ldflags "$VERSIONFLAGS $LDFLAGS $STATIC" -o bin/containerd-shim-runc-v2 ./cmd/containerd-shim-runc-v2
popd
cp -vf ./build/src/github.com/containerd/containerd/bin/* ./bin/
echo Building runc
rm -f ./build/src/github.com/opencontainers/runc/runc
make GOPATH=$(pwd)/build EXTRA_LDFLAGS="-w -s" BUILDTAGS="$RUNC_TAGS" -C ./build/src/github.com/opencontainers/runc $RUNC_STATIC
cp -f ./build/src/github.com/opencontainers/runc/runc ./bin/runc
echo Building containerd-shim-runc-v2
rm -f ./vendor/github.com/containerd/containerd/bin/containerd-shim-runc-v2
make -C ./vendor/github.com/containerd/containerd bin/containerd-shim-runc-v2
cp -f ./vendor/github.com/containerd/containerd/bin/containerd-shim-runc-v2 ./bin/containerd-shim-runc-v2
pushd ./build/src/github.com/opencontainers/runc
rm -f runc
make EXTRA_LDFLAGS="-w -s" BUILDTAGS="$RUNC_TAGS" $RUNC_STATIC
popd
cp -vf ./build/src/github.com/opencontainers/runc/runc ./bin/

View File

@@ -13,12 +13,14 @@ TRAEFIK_CRD_FILE=traefik-crd-${TRAEFIK_CHART_VERSION}${TRAEFIK_PACKAGE_VERSION}.
TRAEFIK_URL=https://helm.traefik.io/traefik/traefik-${TRAEFIK_CHART_VERSION}.tgz
CHARTS_DIR=build/static/charts
RUNC_DIR=build/src/github.com/opencontainers/runc
CONTAINERD_DIR=build/src/github.com/containerd/containerd
DATA_DIR=build/data
export TZ=UTC
umask 022
rm -rf ${CHARTS_DIR}
rm -rf ${RUNC_DIR}
rm -rf ${CONTAINERD_DIR}
mkdir -p ${CHARTS_DIR}
mkdir -p ${DATA_DIR}
@@ -26,6 +28,8 @@ curl --compressed -sfL https://github.com/k3s-io/k3s-root/releases/download/${VE
git clone --single-branch --branch=${VERSION_RUNC} --depth=1 https://github.com/opencontainers/runc ${RUNC_DIR}
git clone --single-branch --branch=${VERSION_CONTAINERD} --depth=1 https://github.com/k3s-io/containerd ${CONTAINERD_DIR}
setup_tmp() {
TMP_DIR=$(mktemp -d --tmpdir=${CHARTS_DIR})
cleanup() {

View File

@@ -7,9 +7,9 @@ cd $(dirname $0)/..
GO=${GO-go}
for i in crictl kubectl k3s-agent k3s-server k3s-etcd-snapshot k3s-secrets-encrypt k3s-certificate k3s ; do
for i in crictl kubectl k3s-agent k3s-server k3s-etcd-snapshot k3s-secrets-encrypt k3s-certificate; do
rm -f bin/$i
ln -s containerd bin/$i
ln -s k3s bin/$i
done
for i in bridge flannel host-local loopback portmap; do

View File

@@ -17,10 +17,10 @@
package console
import (
"errors"
"fmt"
"os"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)
@@ -103,7 +103,7 @@ func (m *master) Reset() error {
{m.err, m.errMode},
} {
if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
return errors.Wrap(err, "unable to restore console mode")
return fmt.Errorf("unable to restore console mode: %w", err)
}
}
@@ -114,7 +114,7 @@ func (m *master) Size() (WinSize, error) {
var info windows.ConsoleScreenBufferInfo
err := windows.GetConsoleScreenBufferInfo(m.out, &info)
if err != nil {
return WinSize{}, errors.Wrap(err, "unable to get console info")
return WinSize{}, fmt.Errorf("unable to get console info: %w", err)
}
winsize := WinSize{
@@ -139,7 +139,7 @@ func (m *master) DisableEcho() error {
mode |= windows.ENABLE_LINE_INPUT
if err := windows.SetConsoleMode(m.in, mode); err != nil {
return errors.Wrap(err, "unable to set console to disable echo")
return fmt.Errorf("unable to set console to disable echo: %w", err)
}
return nil
@@ -192,7 +192,7 @@ func makeInputRaw(fd windows.Handle, mode uint32) error {
}
if err := windows.SetConsoleMode(fd, mode); err != nil {
return errors.Wrap(err, "unable to set console to raw mode")
return fmt.Errorf("unable to set console to raw mode: %w", err)
}
return nil

163
vendor/github.com/containerd/console/console_zos.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
// +build zos
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
"fmt"
"os"
"golang.org/x/sys/unix"
)
// NewPty creates a new pty pair
// The master is returned as the first console and a string
// with the path to the pty slave is returned as the second
func NewPty() (Console, string, error) {
var f File
var err error
var slave string
for i := 0;; i++ {
ptyp := fmt.Sprintf("/dev/ptyp%04d", i)
f, err = os.OpenFile(ptyp, os.O_RDWR, 0600)
if err == nil {
slave = fmt.Sprintf("/dev/ttyp%04d", i)
break
}
if os.IsNotExist(err) {
return nil, "", err
}
// else probably Resource Busy
}
m, err := newMaster(f)
if err != nil {
return nil, "", err
}
return m, slave, nil
}
type master struct {
f File
original *unix.Termios
}
func (m *master) Read(b []byte) (int, error) {
return m.f.Read(b)
}
func (m *master) Write(b []byte) (int, error) {
return m.f.Write(b)
}
func (m *master) Close() error {
return m.f.Close()
}
func (m *master) Resize(ws WinSize) error {
return tcswinsz(m.f.Fd(), ws)
}
func (m *master) ResizeFrom(c Console) error {
ws, err := c.Size()
if err != nil {
return err
}
return m.Resize(ws)
}
func (m *master) Reset() error {
if m.original == nil {
return nil
}
return tcset(m.f.Fd(), m.original)
}
func (m *master) getCurrent() (unix.Termios, error) {
var termios unix.Termios
if err := tcget(m.f.Fd(), &termios); err != nil {
return unix.Termios{}, err
}
return termios, nil
}
func (m *master) SetRaw() error {
rawState, err := m.getCurrent()
if err != nil {
return err
}
rawState = cfmakeraw(rawState)
rawState.Oflag = rawState.Oflag | unix.OPOST
return tcset(m.f.Fd(), &rawState)
}
func (m *master) DisableEcho() error {
rawState, err := m.getCurrent()
if err != nil {
return err
}
rawState.Lflag = rawState.Lflag &^ unix.ECHO
return tcset(m.f.Fd(), &rawState)
}
func (m *master) Size() (WinSize, error) {
return tcgwinsz(m.f.Fd())
}
func (m *master) Fd() uintptr {
return m.f.Fd()
}
func (m *master) Name() string {
return m.f.Name()
}
// checkConsole checks if the provided file is a console
func checkConsole(f File) error {
var termios unix.Termios
if tcget(f.Fd(), &termios) != nil {
return ErrNotAConsole
}
return nil
}
func newMaster(f File) (Console, error) {
m := &master{
f: f,
}
t, err := m.getCurrent()
if err != nil {
return nil, err
}
m.original = &t
return m, nil
}
// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
// created by us acts normally. In particular, a not-very-well-known default of
// Linux unix98 ptys is that they have +onlcr by default. While this isn't a
// problem for terminal emulators, because we relay data from the terminal we
// also relay that funky line discipline.
func ClearONLCR(fd uintptr) error {
return setONLCR(fd, false)
}
// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
// created by us acts as intended for a terminal emulator.
func SetONLCR(fd uintptr) error {
return setONLCR(fd, true)
}

View File

@@ -2,7 +2,4 @@ module github.com/containerd/console
go 1.13
require (
github.com/pkg/errors v0.9.1
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
)
require golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c

View File

@@ -1,4 +1,2 @@
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -1,4 +1,4 @@
// +build darwin freebsd linux netbsd openbsd solaris
// +build darwin freebsd linux netbsd openbsd solaris zos
/*
Copyright The containerd Authors.

26
vendor/github.com/containerd/console/tc_zos.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
"golang.org/x/sys/unix"
)
const (
cmdTcGet = unix.TCGETS
cmdTcSet = unix.TCSETS
)

View File

@@ -8,13 +8,15 @@ require (
github.com/containerd/aufs v1.0.0
github.com/containerd/btrfs v1.0.0
github.com/containerd/cgroups v1.0.1
github.com/containerd/console v1.0.2
github.com/containerd/continuity v0.1.0
github.com/containerd/console v1.0.3
github.com/containerd/continuity v0.2.1
github.com/containerd/fifo v1.0.0
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4
github.com/containerd/go-cni v1.0.2
github.com/containerd/go-runc v1.0.0
github.com/containerd/imgcrypt v1.1.1
github.com/containerd/nri v0.1.0
github.com/containerd/stargz-snapshotter v0.10.1
github.com/containerd/ttrpc v1.1.0
github.com/containerd/typeurl v1.0.2
github.com/containerd/zfs v1.0.0
@@ -31,41 +33,40 @@ require (
github.com/google/go-cmp v0.5.6
github.com/google/uuid v1.2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-multierror v1.1.1
github.com/imdario/mergo v0.3.12
github.com/klauspost/compress v1.11.13
github.com/klauspost/compress v1.13.6
github.com/moby/locker v1.0.1
github.com/moby/sys/mountinfo v0.4.1
github.com/moby/sys/mountinfo v0.5.0
github.com/moby/sys/symlink v0.1.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283
github.com/opencontainers/runc v1.0.2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/selinux v1.8.2
github.com/pelletier/go-toml v1.8.1
github.com/pelletier/go-toml v1.9.4
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/procfs v0.6.0 // indirect; temporarily force v0.6.0, which was previously defined in imgcrypt as explicit version
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tchap/go-patricia v2.2.6+incompatible
github.com/urfave/cli v1.22.2
github.com/urfave/cli v1.22.4
go.etcd.io/bbolt v1.3.6
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359
google.golang.org/grpc v1.42.0
google.golang.org/protobuf v1.27.1
gotest.tools/v3 v3.0.3
k8s.io/api v0.22.0
k8s.io/apimachinery v0.22.0
k8s.io/apiserver v0.22.0
k8s.io/client-go v0.22.0
k8s.io/component-base v0.22.0
k8s.io/cri-api v0.22.0
k8s.io/api v0.22.4
k8s.io/apimachinery v0.22.4
k8s.io/apiserver v0.22.4
k8s.io/client-go v0.22.4
k8s.io/component-base v0.22.4
k8s.io/cri-api v0.23.0-alpha.4
k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a
)
// When updating replace rules, make sure to also update the rules in integration/client/go.mod
@@ -77,4 +78,5 @@ replace (
// urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
github.com/urfave/cli => github.com/urfave/cli v1.22.1
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
k8s.io/cri-api => k8s.io/cri-api v0.22.4
)

View File

@@ -1,4 +1,5 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -72,6 +73,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
@@ -90,6 +92,10 @@ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
@@ -101,13 +107,17 @@ github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTF
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2 h1:Pi6D+aZXM+oUw1czuKgH5IJ+y0jhYcwBJfx5/Ghn9dE=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
github.com/containerd/continuity v0.2.1 h1:/EeEo2EtN3umhbbgCveyjifoMYg0pS+nMMEemaYw634=
github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg=
github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4 h1:UKlk0rhAq1NW5UEmu9/4f3m9v+O/0+F0as+QeK1UmCM=
github.com/containerd/fuse-overlayfs-snapshotter v1.0.4/go.mod h1:UPFCs/OiJHa4MoghjGZdPaOBcZEhATfYG1gSutAZTvE=
github.com/containerd/go-cni v1.0.2 h1:YbJAhpTevL2v6u8JC1NhCYRwf+3Vzxcc5vGnYoJ7VeE=
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
@@ -117,6 +127,10 @@ github.com/containerd/imgcrypt v1.1.1 h1:LBwiTfoUsdiEGAR1TpvxE+Gzt7469oVu87iR3mv
github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
github.com/containerd/nri v0.1.0 h1:6QioHRlThlKh2RkRTR4kIT3PKAcrLo3gIWnjkM4dQmQ=
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/stargz-snapshotter v0.10.1 h1:A4nCRPWZmlYGpPNTmmktREqca1WM2IevonzQcIjCZwM=
github.com/containerd/stargz-snapshotter v0.10.1/go.mod h1:F/5gQZTyx18Kj+4VnE4Kcwg7BtESs7L/V29jamTI43k=
github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng=
github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI=
github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
@@ -156,11 +170,18 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/cli v20.10.10+incompatible h1:kcbwdgWbrBOH8QwQzaJmyriHwF7XIl4HT1qh0HTRys4=
github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
@@ -179,6 +200,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -211,6 +233,8 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -262,6 +286,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
@@ -283,6 +308,7 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -294,15 +320,23 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hanwen/go-fuse/v2 v2.1.1-0.20210825171523-3ab5d95a30ae h1:4CB6T4YTUVvnro5ba8ju1QCbOuyGAeF3vvKlo50EJ4k=
github.com/hanwen/go-fuse/v2 v2.1.1-0.20210825171523-3ab5d95a30ae/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -341,8 +375,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -355,6 +389,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -383,8 +419,9 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/symlink v0.1.0 h1:MTFZ74KtNI6qQQpuBxU+uKCim4WtOMokr03hCfJcazE=
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
@@ -400,7 +437,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -418,8 +454,9 @@ github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 h1:TVzvdjOalkJBNkbpPVMAr4KV9QRf2IjfxdyxwAK78Gs=
github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@@ -431,8 +468,8 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -473,6 +510,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -528,9 +567,12 @@ github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPx
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
@@ -554,8 +596,9 @@ go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvS
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
@@ -645,12 +688,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -692,6 +737,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -717,6 +763,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -725,8 +772,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
@@ -818,8 +865,9 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -829,16 +877,17 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
@@ -875,25 +924,29 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.22.0 h1:elCpMZ9UE8dLdYxr55E06TmSeji9I3KH494qH70/y+c=
k8s.io/api v0.22.0/go.mod h1:0AoXXqst47OI/L0oGKq9DG61dvGRPXs7X4/B7KyjBCU=
k8s.io/apimachinery v0.22.0 h1:CqH/BdNAzZl+sr3tc0D3VsK3u6ARVSo3GWyLmfIjbP0=
k8s.io/apimachinery v0.22.0/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apiserver v0.22.0 h1:KZh2asnRBjawLLfPOi6qiD+A2jaNt31HCnZG6AX3Qcs=
k8s.io/apiserver v0.22.0/go.mod h1:04kaIEzIQrTGJ5syLppQWvpkLJXQtJECHmae+ZGc/nc=
k8s.io/client-go v0.22.0 h1:sD6o9O6tCwUKCENw8v+HFsuAbq2jCu8cWC61/ydwA50=
k8s.io/client-go v0.22.0/go.mod h1:GUjIuXR5PiEv/RVK5OODUsm6eZk7wtSWZSaSJbpFdGg=
k8s.io/component-base v0.22.0 h1:ZTmX8hUqH9T9gc0mM42O+KDgtwTYbVTt2MwmLP0eK8A=
k8s.io/component-base v0.22.0/go.mod h1:SXj6Z+V6P6GsBhHZVbWCw9hFjUdUYnJerlhhPnYCBCg=
k8s.io/cri-api v0.22.0 h1:YECUji0xxCTCWFO/TUkrL1b44Ip6mZJbiqP6Us/+Vys=
k8s.io/cri-api v0.22.0/go.mod h1:mj5DGUtElRyErU5AZ8EM0ahxbElYsaLAMTPhLPQ40Eg=
k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs=
k8s.io/api v0.22.4 h1:UvyHW0ezB2oIgHAxlYoo6UJQObYXU7awuNarwoHEOjw=
k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk=
k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apimachinery v0.22.4 h1:9uwcvPpukBw/Ri0EUmWz+49cnFtaoiyEhQTK+xOe7Ck=
k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0=
k8s.io/apiserver v0.22.4 h1:L+220cy+94UWmyBl1kiVTklBXrBtKsbjlPV60eL2u6s=
k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E=
k8s.io/client-go v0.22.3/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow=
k8s.io/client-go v0.22.4 h1:aAQ1Wk+I3bjCNk35YWUqbaueqrIonkfDPJSPDDe8Kfg=
k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA=
k8s.io/component-base v0.22.4 h1:7qwLJnua2ppGNZrRGDQ0vhsFebI39VGbZ4zdR5ArViI=
k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A=
k8s.io/cri-api v0.22.4 h1:Do7gPZ74mDjSxT1Ux0wWIQ5MMG17uPqT/TKd2yrIwNo=
k8s.io/cri-api v0.22.4/go.mod h1:mj5DGUtElRyErU5AZ8EM0ahxbElYsaLAMTPhLPQ40Eg=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 h1:imL9YgXQ9p7xmPzHFm/vVd/cF78jad+n4wK1ABwYtMM=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -12,8 +12,11 @@ Darren Stahl <darst@microsoft.com>
Derek McGowan <derek@mcg.dev>
Derek McGowan <derek@mcgstyle.net>
Edward Pilatowicz <edward.pilatowicz@oracle.com>
Fu Wei <fuweid89@gmail.com>
Hajime Tazaki <thehajime@gmail.com>
Ian Campbell <ijc@docker.com>
Ivan Markin <sw@nogoegst.net>
Jacob Blain Christen <jacob@rancher.com>
Justin Cormack <justin.cormack@docker.com>
Justin Cummins <sul3n3t@gmail.com>
Kasper Fabæch Brandt <poizan@poizan.dk>
@@ -23,10 +26,11 @@ Michael Crosby <michael@thepasture.io>
Michael Wan <zirenwan@gmail.com>
Mike Brown <brownwm@us.ibm.com>
Niels de Vos <ndevos@redhat.com>
Phil Estes <estesp@amazon.com>
Phil Estes <estesp@gmail.com>
Phil Estes <estesp@linux.vnet.ibm.com>
Samuel Karp <me@samuelkarp.com>
Sam Whited <sam@samwhited.com>
Samuel Karp <me@samuelkarp.com>
Sebastiaan van Stijn <github@gone.nl>
Shengjing Zhu <zhsj@debian.org>
Stephen J Day <stephen.day@docker.com>

View File

@@ -1,7 +1,7 @@
# continuity
[![GoDoc](https://godoc.org/github.com/containerd/continuity?status.svg)](https://godoc.org/github.com/containerd/continuity)
[![Build Status](https://travis-ci.org/containerd/continuity.svg?branch=master)](https://travis-ci.org/containerd/continuity)
[![Build Status](https://travis-ci.org/containerd/continuity.svg?branch=main)](https://travis-ci.org/containerd/continuity)
A transport-agnostic, filesystem metadata manifest system
@@ -81,8 +81,8 @@ $ go generate ./proto
continuity is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin || freebsd || solaris
// +build linux darwin freebsd solaris
/*

View File

@@ -17,11 +17,10 @@
package devices
import (
"fmt"
"os"
"github.com/pkg/errors"
)
func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) {
return 0, 0, errors.Wrap(ErrNotSupported, "cannot get device info on windows")
return 0, 0, fmt.Errorf("cannot get device info on windows: %w", ErrNotSupported)
}

View File

@@ -1,3 +1,4 @@
//go:build freebsd
// +build freebsd
/*

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin || solaris
// +build linux darwin solaris
/*

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin || freebsd || solaris
// +build linux darwin freebsd solaris
/*

View File

@@ -1,3 +1,4 @@
//go:build go1.13
// +build go1.13
/*

View File

@@ -1,3 +1,4 @@
//go:build darwin || freebsd || solaris
// +build darwin freebsd solaris
/*

View File

@@ -17,12 +17,11 @@
package fs
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/pkg/errors"
)
var bufferPool = &sync.Pool{
@@ -92,35 +91,35 @@ func CopyDir(dst, src string, opts ...CopyDirOpt) error {
func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
stat, err := os.Stat(src)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
return fmt.Errorf("failed to stat %s: %w", src, err)
}
if !stat.IsDir() {
return errors.Errorf("source %s is not directory", src)
return fmt.Errorf("source %s is not directory", src)
}
if st, err := os.Stat(dst); err != nil {
if err := os.Mkdir(dst, stat.Mode()); err != nil {
return errors.Wrapf(err, "failed to mkdir %s", dst)
return fmt.Errorf("failed to mkdir %s: %w", dst, err)
}
} else if !st.IsDir() {
return errors.Errorf("cannot copy to non-directory: %s", dst)
return fmt.Errorf("cannot copy to non-directory: %s", dst)
} else {
if err := os.Chmod(dst, stat.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod on %s", dst)
return fmt.Errorf("failed to chmod on %s: %w", dst, err)
}
}
fis, err := ioutil.ReadDir(src)
if err != nil {
return errors.Wrapf(err, "failed to read %s", src)
return fmt.Errorf("failed to read %s: %w", src, err)
}
if err := copyFileInfo(stat, dst); err != nil {
return errors.Wrapf(err, "failed to copy file info for %s", dst)
return fmt.Errorf("failed to copy file info for %s: %w", dst, err)
}
if err := copyXAttrs(dst, src, o.xex, o.xeh); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
return fmt.Errorf("failed to copy xattrs: %w", err)
}
for _, fi := range fis {
@@ -136,37 +135,37 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
case (fi.Mode() & os.ModeType) == 0:
link, err := getLinkSource(target, fi, inodes)
if err != nil {
return errors.Wrap(err, "failed to get hardlink")
return fmt.Errorf("failed to get hardlink: %w", err)
}
if link != "" {
if err := os.Link(link, target); err != nil {
return errors.Wrap(err, "failed to create hard link")
return fmt.Errorf("failed to create hard link: %w", err)
}
} else if err := CopyFile(target, source); err != nil {
return errors.Wrap(err, "failed to copy files")
return fmt.Errorf("failed to copy files: %w", err)
}
case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
link, err := os.Readlink(source)
if err != nil {
return errors.Wrapf(err, "failed to read link: %s", source)
return fmt.Errorf("failed to read link: %s: %w", source, err)
}
if err := os.Symlink(link, target); err != nil {
return errors.Wrapf(err, "failed to create symlink: %s", target)
return fmt.Errorf("failed to create symlink: %s: %w", target, err)
}
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
if err := copyDevice(target, fi); err != nil {
return errors.Wrapf(err, "failed to create device")
return fmt.Errorf("failed to create device: %w", err)
}
default:
// TODO: Support pipes and sockets
return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
return fmt.Errorf("unsupported mode %s: %w", fi.Mode(), err)
}
if err := copyFileInfo(fi, target); err != nil {
return errors.Wrap(err, "failed to copy file info")
return fmt.Errorf("failed to copy file info: %w", err)
}
if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
return fmt.Errorf("failed to copy xattrs: %w", err)
}
}
@@ -178,12 +177,12 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
func CopyFile(target, source string) error {
src, err := os.Open(source)
if err != nil {
return errors.Wrapf(err, "failed to open source %s", source)
return fmt.Errorf("failed to open source %s: %w", source, err)
}
defer src.Close()
tgt, err := os.Create(target)
if err != nil {
return errors.Wrapf(err, "failed to open target %s", target)
return fmt.Errorf("failed to open target %s: %w", target, err)
}
defer tgt.Close()

View File

@@ -0,0 +1,43 @@
//go:build darwin
// +build darwin
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs
import (
"errors"
"os"
"syscall"
"golang.org/x/sys/unix"
)
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
}
func utimesNano(name string, atime, mtime syscall.Timespec) error {
at := unix.NsecToTimespec(atime.Nano())
mt := unix.NsecToTimespec(mtime.Nano())
utimes := [2]unix.Timespec{at, mt}
return unix.UtimesNanoAt(unix.AT_FDCWD, name, utimes[0:], unix.AT_SYMLINK_NOFOLLOW)
}

View File

@@ -1,3 +1,4 @@
//go:build freebsd
// +build freebsd
/*
@@ -19,10 +20,10 @@
package fs
import (
"errors"
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)

View File

@@ -17,12 +17,13 @@
package fs
import (
"errors"
"fmt"
"io"
"os"
"syscall"
"github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -41,13 +42,13 @@ func copyFileInfo(fi os.FileInfo, name string) error {
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
return fmt.Errorf("failed to chown %s: %w", name, err)
}
}
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
}
@@ -56,7 +57,7 @@ func copyFileInfo(fi os.FileInfo, name string) error {
unix.NsecToTimespec(syscall.TimespecToNsec(StatMtime(st))),
}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
return fmt.Errorf("failed to utime %s: %w", name, err)
}
return nil
@@ -67,7 +68,7 @@ const maxSSizeT = int64(^uint(0) >> 1)
func copyFileContent(dst, src *os.File) error {
st, err := src.Stat()
if err != nil {
return errors.Wrap(err, "unable to stat source")
return fmt.Errorf("unable to stat source: %w", err)
}
size := st.Size()
@@ -88,13 +89,16 @@ func copyFileContent(dst, src *os.File) error {
n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0)
if err != nil {
if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
return errors.Wrap(err, "copy file range failed")
return fmt.Errorf("copy file range failed: %w", err)
}
buf := bufferPool.Get().(*[]byte)
_, err = io.CopyBuffer(dst, src, *buf)
bufferPool.Put(buf)
return errors.Wrap(err, "userspace copy failed")
if err != nil {
return fmt.Errorf("userspace copy failed: %w", err)
}
return nil
}
first = false
@@ -107,7 +111,7 @@ func copyFileContent(dst, src *os.File) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)
}
@@ -119,7 +123,7 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
}
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
@@ -128,7 +132,7 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue

View File

@@ -1,4 +1,5 @@
// +build darwin openbsd solaris
//go:build openbsd || solaris
// +build openbsd solaris
/*
Copyright The containerd Authors.
@@ -19,10 +20,10 @@
package fs
import (
"errors"
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)

View File

@@ -1,3 +1,4 @@
//go:build darwin || freebsd || openbsd || solaris
// +build darwin freebsd openbsd solaris
/*
@@ -19,12 +20,12 @@
package fs
import (
"fmt"
"io"
"os"
"syscall"
"github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
)
func copyFileInfo(fi os.FileInfo, name string) error {
@@ -42,18 +43,18 @@ func copyFileInfo(fi os.FileInfo, name string) error {
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
return fmt.Errorf("failed to chown %s: %w", name, err)
}
}
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
}
if err := utimesNano(name, StatAtime(st), StatMtime(st)); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
return fmt.Errorf("failed to utime %s: %w", name, err)
}
return nil
@@ -70,7 +71,7 @@ func copyFileContent(dst, src *os.File) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)
}
@@ -82,7 +83,7 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
}
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
@@ -91,7 +92,7 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue

View File

@@ -17,15 +17,15 @@
package fs
import (
"errors"
"fmt"
"io"
"os"
"github.com/pkg/errors"
)
func copyFileInfo(fi os.FileInfo, name string) error {
if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
// TODO: copy windows specific metadata

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@@ -20,11 +21,11 @@ package fs
import (
"bytes"
"fmt"
"os"
"syscall"
"github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
)
// detectDirDiff returns diff dir options if a directory could
@@ -56,11 +57,11 @@ func compareSysStat(s1, s2 interface{}) (bool, error) {
func compareCapabilities(p1, p2 string) (bool, error) {
c1, err := sysx.LGetxattr(p1, "security.capability")
if err != nil && err != sysx.ENODATA {
return false, errors.Wrapf(err, "failed to get xattr for %s", p1)
return false, fmt.Errorf("failed to get xattr for %s: %w", p1, err)
}
c2, err := sysx.LGetxattr(p2, "security.capability")
if err != nil && err != sysx.ENODATA {
return false, errors.Wrapf(err, "failed to get xattr for %s", p2)
return false, fmt.Errorf("failed to get xattr for %s: %w", p2, err)
}
return bytes.Equal(c1, c2), nil
}

View File

@@ -1,3 +1,4 @@
//go:build linux
// +build linux
/*

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@@ -41,10 +42,8 @@ type inode struct {
func newInode(stat *syscall.Stat_t) inode {
return inode{
// Dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
dev: uint64(stat.Dev), // nolint: unconvert
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
ino: uint64(stat.Ino), // nolint: unconvert
dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
}
}

View File

@@ -1,3 +1,4 @@
//go:build windows
// +build windows
/*

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@@ -29,6 +30,5 @@ func getLinkInfo(fi os.FileInfo) (uint64, bool) {
return 0, false
}
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris
}

View File

@@ -19,11 +19,10 @@ package fs
import (
"bytes"
"context"
"errors"
"io"
"os"
"path/filepath"
"github.com/pkg/errors"
)
var (

View File

@@ -1,4 +1,5 @@
// +build darwin freebsd
//go:build darwin || freebsd || netbsd
// +build darwin freebsd netbsd
/*
Copyright The containerd Authors.
@@ -40,5 +41,5 @@ func StatMtime(st *syscall.Stat_t) syscall.Timespec {
// StatATimeAsTime returns the access time as a time.Time
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert
return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) //nolint: unconvert // int64 conversions ensure the line compiles for 32-bit systems as well.
}

View File

@@ -1,3 +1,4 @@
//go:build linux || openbsd
// +build linux openbsd
/*
@@ -40,6 +41,5 @@ func StatMtime(st *syscall.Stat_t) syscall.Timespec {
// StatATimeAsTime returns st.Atim as a time.Time
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
// The int64 conversions ensure the line compiles for 32-bit systems as well.
return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert
return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) //nolint: unconvert // int64 conversions ensure the line compiles for 32-bit systems as well.
}

View File

@@ -3,17 +3,14 @@ module github.com/containerd/continuity
go 1.13
require (
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898
// 5883e5a4b512fe2e32f915b1c66a1ddfef81cb3f is the last version to support macOS
// see https://github.com/bazil/fuse/commit/60eaf8f021ce00e5c52529cdcba1067e13c1c2c2
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512
github.com/dustin/go-humanize v1.0.0
github.com/golang/protobuf v1.2.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/golang/protobuf v1.3.5
github.com/opencontainers/go-digest v1.0.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95 // indirect
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd // indirect
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3
github.com/spf13/cobra v1.0.0
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
)

View File

@@ -1,38 +1,148 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898 h1:SC+c6A1qTFstO9qmB86mPV2IpYme/2ZoEQ0hrP+wo+Q=
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee h1:GQkkv3XSnxhAMjdq2wLfEnptEVr+2BNvmHizILHn+d4=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95 h1:j8jxLbQ0+T1DFggy6XoGvyUnrJWPR/JybflPvu5rwS4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin || freebsd || solaris
// +build linux darwin freebsd solaris
/*

View File

@@ -75,7 +75,7 @@ func MarshalText(w io.Writer, m *Manifest) error {
// BuildManifest creates the manifest for the given context
func BuildManifest(ctx Context) (*Manifest, error) {
resourcesByPath := map[string]Resource{}
hardlinks := newHardlinkManager()
hardLinks := newHardlinkManager()
if err := ctx.Walk(func(p string, fi os.FileInfo, err error) error {
if err != nil {
@@ -97,7 +97,7 @@ func BuildManifest(ctx Context) (*Manifest, error) {
}
// add to the hardlink manager
if err := hardlinks.Add(fi, resource); err == nil {
if err := hardLinks.Add(fi, resource); err == nil {
// Resource has been accepted by hardlink manager so we don't add
// it to the resourcesByPath until we merge at the end.
return nil
@@ -114,14 +114,12 @@ func BuildManifest(ctx Context) (*Manifest, error) {
}
// merge and post-process the hardlinks.
// nolint:misspell
hardlinked, err := hardlinks.Merge()
hardLinked, err := hardLinks.Merge()
if err != nil {
return nil, err
}
// nolint:misspell
for _, resource := range hardlinked {
for _, resource := range hardLinked {
resourcesByPath[resource.Path()] = resource
}

View File

@@ -1,6 +1,5 @@
// Code generated by protoc-gen-go.
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: manifest.proto
// DO NOT EDIT!
/*
Package proto is a generated protocol buffer package.
@@ -97,6 +96,83 @@ func (m *Resource) String() string { return proto1.CompactTextString(
func (*Resource) ProtoMessage() {}
func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Resource) GetPath() []string {
if m != nil {
return m.Path
}
return nil
}
func (m *Resource) GetUid() int64 {
if m != nil {
return m.Uid
}
return 0
}
func (m *Resource) GetGid() int64 {
if m != nil {
return m.Gid
}
return 0
}
func (m *Resource) GetUser() string {
if m != nil {
return m.User
}
return ""
}
func (m *Resource) GetGroup() string {
if m != nil {
return m.Group
}
return ""
}
func (m *Resource) GetMode() uint32 {
if m != nil {
return m.Mode
}
return 0
}
func (m *Resource) GetSize() uint64 {
if m != nil {
return m.Size
}
return 0
}
func (m *Resource) GetDigest() []string {
if m != nil {
return m.Digest
}
return nil
}
func (m *Resource) GetTarget() string {
if m != nil {
return m.Target
}
return ""
}
func (m *Resource) GetMajor() uint64 {
if m != nil {
return m.Major
}
return 0
}
func (m *Resource) GetMinor() uint64 {
if m != nil {
return m.Minor
}
return 0
}
func (m *Resource) GetXattr() []*XAttr {
if m != nil {
return m.Xattr
@@ -124,6 +200,20 @@ func (m *XAttr) String() string { return proto1.CompactTextString(m)
func (*XAttr) ProtoMessage() {}
func (*XAttr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *XAttr) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *XAttr) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
// ADSEntry encodes information for a Windows Alternate Data Stream.
type ADSEntry struct {
// Name specifices the stream name.
@@ -147,6 +237,27 @@ func (m *ADSEntry) String() string { return proto1.CompactTextString(
func (*ADSEntry) ProtoMessage() {}
func (*ADSEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *ADSEntry) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *ADSEntry) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *ADSEntry) GetDigest() string {
if m != nil {
return m.Digest
}
return ""
}
func init() {
proto1.RegisterType((*Manifest)(nil), "proto.Manifest")
proto1.RegisterType((*Resource)(nil), "proto.Resource")
@@ -158,7 +269,7 @@ func init() { proto1.RegisterFile("manifest.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 317 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x4f, 0x4b, 0xf3, 0x40,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0x4f, 0x4b, 0xf3, 0x40,
0x10, 0xc6, 0x49, 0x93, 0xf4, 0x4d, 0xa7, 0xed, 0xab, 0x2c, 0x52, 0xe6, 0x18, 0x73, 0x0a, 0x08,
0x15, 0xf4, 0xe0, 0xb9, 0xa2, 0x17, 0xc1, 0xcb, 0x7a, 0xf1, 0xba, 0xba, 0x6b, 0x5c, 0x21, 0xd9,
0xb0, 0xd9, 0x80, 0xfa, 0xe5, 0xfc, 0x6a, 0x32, 0xb3, 0x69, 0xd1, 0x9b, 0xa7, 0x3c, 0xcf, 0x6f,

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin || freebsd || solaris
// +build linux darwin freebsd solaris
/*

View File

@@ -1,3 +1,4 @@
//go:build darwin || freebsd || openbsd
// +build darwin freebsd openbsd
/*

View File

@@ -1,3 +1,4 @@
//go:build linux || darwin
// +build linux darwin
/*

View File

@@ -1,3 +1,4 @@
//go:build !linux && !darwin
// +build !linux,!darwin
/*

View File

@@ -13,12 +13,12 @@
# limitations under the License.
ARG FUSEOVERLAYFS_COMMIT=master
ARG ROOTLESSKIT_COMMIT=v0.14.0
ARG SHADOW_COMMIT=4.8.1
ARG ROOTLESSKIT_COMMIT=v0.14.6
ARG SHADOW_COMMIT=4.9
ARG GO_VERSION=1.16
ARG DEBIAN_VERSION=10
ARG ALPINE_VERSION=3.13
ARG GO_VERSION=1.17
ARG DEBIAN_VERSION=11
ARG ALPINE_VERSION=3.14
FROM golang:${GO_VERSION}-alpine AS containerd-fuse-overlayfs-test
COPY . /go/src/github.com/containerd/fuse-overlayfs-snapshotter

View File

@@ -5,8 +5,6 @@ Unlike `overlayfs`, `fuse-overlayfs` can be used as a non-root user on almost al
You do NOT need this `fuse-overlayfs` plugin on the following environments, because they support the real `overlayfs` for non-root users:
- [kernel >= 5.11](https://github.com/torvalds/linux/commit/459c7c565ac36ba09ffbf24231147f408fde4203)
- [Ubuntu kernel, since circa 2015](https://kernel.ubuntu.com/git/ubuntu/ubuntu-bionic.git/commit/fs/overlayfs?id=3b7da90f28fe1ed4b79ef2d994c81efbc58f1144)
- [Debian 10 kernel](https://salsa.debian.org/kernel-team/linux/blob/283390e7feb21b47779b48e0c8eb0cc409d2c815/debian/patches/debian/overlayfs-permit-mounts-in-userns.patch)
- Debian 10 needs `sudo modprobe overlay permit_mounts_in_userns=1`. Future release of Debian with kernel >= 5.11 will not need this `modprobe` hack.
fuse-overlayfs-snapshotter is a **non-core** sub-project of containerd.

View File

@@ -1,3 +1,4 @@
//go:build linux
// +build linux
/*
@@ -27,7 +28,6 @@ import (
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/pkg/errors"
)
// supportsReadonlyMultipleLowerDir checks if read-only multiple lowerdirs can be mounted with fuse-overlayfs.
@@ -57,7 +57,7 @@ func supportsReadonlyMultipleLowerDir(d string) error {
}
dest := filepath.Join(td, "merged")
if err := m.Mount(dest); err != nil {
return errors.Wrapf(err, "failed to mount fuse-overlayfs (%+v) on %s", m, dest)
return fmt.Errorf("failed to mount fuse-overlayfs (%+v) on %s: %w", m, dest, err)
}
if err := mount.UnmountAll(dest, 0); err != nil {
log.L.WithError(err).Warnf("Failed to unmount check directory %v", dest)
@@ -70,13 +70,13 @@ func supportsReadonlyMultipleLowerDir(d string) error {
// this snapshotter as a library.
func Supported(root string) error {
if _, err := exec.LookPath(fuseoverlayfsBinary); err != nil {
return errors.Wrapf(err, "%s not installed", fuseoverlayfsBinary)
return fmt.Errorf("%s not installed: %w", fuseoverlayfsBinary, err)
}
if err := os.MkdirAll(root, 0700); err != nil {
return err
}
if err := supportsReadonlyMultipleLowerDir(root); err != nil {
return errors.Wrap(err, "fuse-overlayfs not functional, make sure running with kernel >= 4.18")
return fmt.Errorf("fuse-overlayfs not functional, make sure running with kernel >= 4.18: %w", err)
}
return nil
}

View File

@@ -1,3 +1,4 @@
//go:build linux
// +build linux
/*
@@ -32,7 +33,6 @@ import (
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/snapshots/storage"
"github.com/containerd/continuity/fs"
"github.com/pkg/errors"
)
const (
@@ -186,7 +186,7 @@ func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
_, info, _, err := storage.GetInfo(ctx, key)
t.Rollback()
if err != nil {
return nil, errors.Wrap(err, "failed to get active mount")
return nil, fmt.Errorf("failed to get active mount: %w", err)
}
return o.mounts(s, info), nil
}
@@ -217,7 +217,7 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
}
if _, err = storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
return errors.Wrap(err, "failed to commit snapshot")
return fmt.Errorf("failed to commit snapshot: %w", err)
}
return t.Commit()
}
@@ -240,14 +240,14 @@ func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
_, _, err = storage.Remove(ctx, key)
if err != nil {
return errors.Wrap(err, "failed to remove")
return fmt.Errorf("failed to remove: %w", err)
}
if !o.asyncRemove {
var removals []string
removals, err = o.getCleanupDirectories(ctx, t)
if err != nil {
return errors.Wrap(err, "unable to get directories for removal")
return fmt.Errorf("unable to get directories for removal: %w", err)
}
// Remove directories after the transaction is closed, failures must not
@@ -353,7 +353,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
if path != "" {
if err1 := os.RemoveAll(path); err1 != nil {
log.G(ctx).WithError(err1).WithField("path", path).Error("failed to reclaim snapshot directory, directory may need removal")
err = errors.Wrapf(err, "failed to remove path: %v", err1)
err = fmt.Errorf("failed to remove path: %v: %w", err1, err)
}
}
}
@@ -365,7 +365,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
}
return nil, errors.Wrap(err, "failed to create prepare snapshot dir")
return nil, fmt.Errorf("failed to create prepare snapshot dir: %w", err)
}
rollback := true
defer func() {
@@ -378,13 +378,13 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
if err != nil {
return nil, errors.Wrap(err, "failed to create snapshot")
return nil, fmt.Errorf("failed to create snapshot: %w", err)
}
if len(s.ParentIDs) > 0 {
st, err := os.Stat(o.upperPath(s.ParentIDs[0]))
if err != nil {
return nil, errors.Wrap(err, "failed to stat parent")
return nil, fmt.Errorf("failed to stat parent: %w", err)
}
stat := st.Sys().(*syscall.Stat_t)
@@ -393,13 +393,13 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
}
return nil, errors.Wrap(err, "failed to chown")
return nil, fmt.Errorf("failed to chown: %w", err)
}
}
path = filepath.Join(snapshotDir, s.ID)
if err = os.Rename(td, path); err != nil {
return nil, errors.Wrap(err, "failed to rename")
return nil, fmt.Errorf("failed to rename: %w", err)
}
td = ""
@@ -407,7 +407,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
rollback = false
if err = t.Commit(); err != nil {
return nil, errors.Wrap(err, "commit failed")
return nil, fmt.Errorf("commit failed: %w", err)
}
return o.mounts(s, info), nil
@@ -416,7 +416,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string, kind snapshots.Kind) (string, error) {
td, err := ioutil.TempDir(snapshotDir, "new-")
if err != nil {
return "", errors.Wrap(err, "failed to create temp dir")
return "", fmt.Errorf("failed to create temp dir: %w", err)
}
if err := os.Mkdir(filepath.Join(td, "fs"), 0755); err != nil {

View File

@@ -3,13 +3,9 @@ module github.com/containerd/fuse-overlayfs-snapshotter
go 1.16
require (
github.com/containerd/containerd v1.5.2
github.com/containerd/containerd v1.5.8
github.com/containerd/continuity v0.1.0
github.com/coreos/go-systemd/v22 v22.1.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
github.com/coreos/go-systemd/v22 v22.3.2
github.com/sirupsen/logrus v1.8.1
google.golang.org/grpc v1.33.2
)
// replace grpc to match https://github.com/containerd/containerd/blob/v1.5.2/go.mod
replace google.golang.org/grpc => google.golang.org/grpc v1.27.1

View File

@@ -1,4 +1,5 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@@ -50,8 +51,9 @@ github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEY
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M=
github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -73,6 +75,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
@@ -81,10 +84,12 @@ github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -92,6 +97,9 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
@@ -121,12 +129,13 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX
github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
github.com/containerd/containerd v1.5.2 h1:MG/Bg1pbmMb61j3wHCFWPxESXHieiKr2xG64px/k8zQ=
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw=
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -159,8 +168,9 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI=
github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
@@ -191,8 +201,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0 h1:kq/SbG2BCKLkDKkjQf5OWwKWUKj1lgs3lFI4PxnR5lg=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -228,7 +239,9 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -262,6 +275,7 @@ github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblf
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -271,7 +285,6 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -297,8 +310,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -308,8 +322,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -351,6 +366,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -440,14 +456,17 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@@ -502,8 +521,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -601,6 +621,7 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -619,6 +640,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -667,6 +689,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -724,8 +747,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -742,6 +766,7 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -791,6 +816,7 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -819,8 +845,21 @@ google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -830,8 +869,11 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -1,3 +1,4 @@
//go:build linux
// +build linux
/*

View File

@@ -132,6 +132,7 @@ func NewDirectoryCache(directory string, config DirectoryCacheConfig) (BlobCache
}
dataCache = lrucache.New(maxEntry)
dataCache.OnEvicted = func(key string, value interface{}) {
value.(*bytes.Buffer).Reset()
bufPool.Put(value)
}
}
@@ -296,7 +297,6 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
}
b := dc.bufPool.Get().(*bytes.Buffer)
b.Reset()
memW := &writer{
WriteCloser: nopWriteCloser(io.Writer(b)),
commitFunc: func() error {
@@ -306,7 +306,7 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
}
cached, done, added := dc.cache.Add(key, b)
if !added {
dc.bufPool.Put(b) // already exists in the cache. abort it.
dc.putBuffer(b) // already exists in the cache. abort it.
}
commit := func() error {
defer done()
@@ -331,7 +331,7 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
abortFunc: func() error {
defer w.Close()
defer w.Abort()
dc.bufPool.Put(b) // abort it.
dc.putBuffer(b) // abort it.
return nil
},
}
@@ -339,6 +339,11 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
return memW, nil
}
func (dc *directoryCache) putBuffer(b *bytes.Buffer) {
b.Reset()
dc.bufPool.Put(b)
}
func (dc *directoryCache) Close() error {
dc.closedMu.Lock()
defer dc.closedMu.Unlock()

View File

@@ -118,7 +118,7 @@ func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) {
}
}
gzipCompressors := []Decompressor{new(GzipDecompressor), new(legacyGzipDecompressor)}
gzipCompressors := []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)}
decompressors := append(gzipCompressors, opts.decompressors...)
// Determine the size to fetch. Try to fetch as many bytes as possible.
@@ -184,7 +184,7 @@ func OpenFooter(sr *io.SectionReader) (tocOffset int64, footerSize int64, rErr e
return 0, 0, fmt.Errorf("error reading footer: %v", err)
}
var allErr []error
for _, d := range []Decompressor{new(GzipDecompressor), new(legacyGzipDecompressor)} {
for _, d := range []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} {
fSize := d.FooterSize()
fOffset := positive(int64(len(footer)) - fSize)
_, tocOffset, _, err := d.ParseFooter(footer[fOffset:])
@@ -279,12 +279,12 @@ func (r *Reader) initFields() error {
pdir := r.getOrCreateDir(pdirName)
ent.NumLink++ // at least one name(ent.Name) references this entry.
if ent.Type == "hardlink" {
if org, ok := r.m[cleanEntryName(ent.LinkName)]; ok {
org.NumLink++ // original entry is referenced by this ent.Name.
ent = org
} else {
return fmt.Errorf("%q is a hardlink but the linkname %q isn't found", ent.Name, ent.LinkName)
org, err := r.getSource(ent)
if err != nil {
return err
}
org.NumLink++ // original entry is referenced by this ent.Name.
ent = org
}
pdir.addChild(path.Base(name), ent)
}
@@ -303,6 +303,20 @@ func (r *Reader) initFields() error {
return nil
}
func (r *Reader) getSource(ent *TOCEntry) (_ *TOCEntry, err error) {
if ent.Type == "hardlink" {
org, ok := r.m[cleanEntryName(ent.LinkName)]
if !ok {
return nil, fmt.Errorf("%q is a hardlink but the linkname %q isn't found", ent.Name, ent.LinkName)
}
ent, err = r.getSource(org)
if err != nil {
return nil, err
}
}
return ent, nil
}
func parentDir(p string) string {
dir, _ := path.Split(p)
return strings.TrimSuffix(dir, "/")
@@ -464,7 +478,11 @@ func (r *Reader) Lookup(path string) (e *TOCEntry, ok bool) {
}
e, ok = r.m[path]
if ok && e.Type == "hardlink" {
e, ok = r.m[e.LinkName]
var err error
e, err = r.getSource(e)
if err != nil {
return nil, false
}
}
return
}

View File

@@ -38,30 +38,34 @@ import (
)
type gzipCompression struct {
*gzipCompressor
*GzipCompressor
*GzipDecompressor
}
func newGzipCompressionWithLevel(level int) Compression {
return &gzipCompression{
&gzipCompressor{level},
&GzipCompressor{level},
&GzipDecompressor{},
}
}
func NewGzipCompressorWithLevel(level int) Compressor {
return &gzipCompressor{level}
func NewGzipCompressor() *GzipCompressor {
return &GzipCompressor{gzip.BestCompression}
}
type gzipCompressor struct {
func NewGzipCompressorWithLevel(level int) *GzipCompressor {
return &GzipCompressor{level}
}
type GzipCompressor struct {
compressionLevel int
}
func (gc *gzipCompressor) Writer(w io.Writer) (io.WriteCloser, error) {
func (gc *GzipCompressor) Writer(w io.Writer) (io.WriteCloser, error) {
return gzip.NewWriterLevel(w, gc.compressionLevel)
}
func (gc *gzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (digest.Digest, error) {
func (gc *GzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (digest.Digest, error) {
tocJSON, err := json.MarshalIndent(toc, "", "\t")
if err != nil {
return "", err
@@ -155,17 +159,21 @@ func (gz *GzipDecompressor) FooterSize() int64 {
return FooterSize
}
type legacyGzipDecompressor struct{}
func (gz *GzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) {
return decompressTOCEStargz(r)
}
func (gz *legacyGzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) {
type LegacyGzipDecompressor struct{}
func (gz *LegacyGzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) {
return gzip.NewReader(r)
}
func (gz *legacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
func (gz *LegacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
return parseTOCEStargz(r)
}
func (gz *legacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) {
func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) {
if len(p) != legacyFooterSize {
return 0, 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p))
}
@@ -188,29 +196,43 @@ func (gz *legacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOff
return tocOffset, tocOffset, 0, nil
}
func (gz *legacyGzipDecompressor) FooterSize() int64 {
func (gz *LegacyGzipDecompressor) FooterSize() int64 {
return legacyFooterSize
}
func (gz *LegacyGzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) {
return decompressTOCEStargz(r)
}
func parseTOCEStargz(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
zr, err := gzip.NewReader(r)
tr, err := decompressTOCEStargz(r)
if err != nil {
return nil, "", fmt.Errorf("malformed TOC gzip header: %v", err)
}
defer zr.Close()
zr.Multistream(false)
tr := tar.NewReader(zr)
h, err := tr.Next()
if err != nil {
return nil, "", fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err)
}
if h.Name != TOCTarName {
return nil, "", fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName)
return nil, "", err
}
dgstr := digest.Canonical.Digester()
toc = new(JTOC)
if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil {
return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err)
}
if err := tr.Close(); err != nil {
return nil, "", err
}
return toc, dgstr.Digest(), nil
}
func decompressTOCEStargz(r io.Reader) (tocJSON io.ReadCloser, err error) {
zr, err := gzip.NewReader(r)
if err != nil {
return nil, fmt.Errorf("malformed TOC gzip header: %v", err)
}
zr.Multistream(false)
tr := tar.NewReader(zr)
h, err := tr.Next()
if err != nil {
return nil, fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err)
}
if h.Name != TOCTarName {
return nil, fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName)
}
return readCloser{tr, zr.Close}, nil
}

View File

@@ -17,6 +17,7 @@
package zstdchunked
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/json"
@@ -88,6 +89,25 @@ func (zz *Decompressor) FooterSize() int64 {
return FooterSize
}
func (zz *Decompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) {
decoder, err := zstd.NewReader(r)
if err != nil {
return nil, err
}
br := bufio.NewReader(decoder)
if _, err := br.Peek(1); err != nil {
return nil, err
}
return &reader{br, decoder.Close}, nil
}
type reader struct {
io.Reader
closeFunc func()
}
func (r *reader) Close() error { r.closeFunc(); return nil }
type zstdReadCloser struct{ *zstd.Decoder }
func (z *zstdReadCloser) Close() error {

View File

@@ -46,6 +46,7 @@ type Config struct {
DisableVerification bool `toml:"disable_verification"`
MaxConcurrency int64 `toml:"max_concurrency"`
NoPrometheus bool `toml:"no_prometheus"`
MetadataStore string `toml:"metadata_store" default:"memory"`
// BlobConfig is config for layer blob management.
BlobConfig `toml:"blob"`
@@ -69,13 +70,17 @@ type BlobConfig struct {
// If PrefetchChunkSize < ChunkSize prefetch bytes will be fetched as a single http GET,
// else total GET requests for prefetch = ceil(PrefetchSize / PrefetchChunkSize).
PrefetchChunkSize int64 `toml:"prefetch_chunk_size"`
MaxRetries int `toml:"max_retries"`
MinWaitMSec int `toml:"min_wait_msec"`
MaxWaitMSec int `toml:"max_wait_msec"`
}
type DirectoryCacheConfig struct {
MaxLRUCacheEntry int `toml:"max_lru_cache_entry"`
MaxCacheFds int `toml:"max_cache_fds"`
SyncAdd bool `toml:"sync_add"`
Direct bool `toml:"direct"`
Direct bool `toml:"direct" default:"true"`
}
type FuseConfig struct {

View File

@@ -53,6 +53,7 @@ import (
"github.com/containerd/stargz-snapshotter/fs/layer"
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
layermetrics "github.com/containerd/stargz-snapshotter/fs/metrics/layer"
"github.com/containerd/stargz-snapshotter/fs/remote"
"github.com/containerd/stargz-snapshotter/fs/source"
"github.com/containerd/stargz-snapshotter/snapshot"
"github.com/containerd/stargz-snapshotter/task"
@@ -73,7 +74,8 @@ const (
type Option func(*options)
type options struct {
getSources source.GetSources
getSources source.GetSources
resolveHandlers map[string]remote.Handler
}
func WithGetSources(s source.GetSources) Option {
@@ -82,6 +84,15 @@ func WithGetSources(s source.GetSources) Option {
}
}
func WithResolveHandler(name string, handler remote.Handler) Option {
return func(opts *options) {
if opts.resolveHandlers == nil {
opts.resolveHandlers = make(map[string]remote.Handler)
}
opts.resolveHandlers[name] = handler
}
}
func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.FileSystem, err error) {
var fsOpts options
for _, o := range opts {
@@ -109,7 +120,7 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F
})
}
tm := task.NewBackgroundTaskManager(maxConcurrency, 5*time.Second)
r, err := layer.NewResolver(root, tm, cfg)
r, err := layer.NewResolver(root, tm, cfg, fsOpts.resolveHandlers)
if err != nil {
return nil, errors.Wrapf(err, "failed to setup resolver")
}
@@ -177,6 +188,13 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
return fmt.Errorf("source must be passed")
}
defaultPrefetchSize := fs.prefetchSize
if psStr, ok := labels[config.TargetPrefetchSizeLabel]; ok {
if ps, err := strconv.ParseInt(psStr, 10, 64); err == nil {
defaultPrefetchSize = ps
}
}
// Resolve the target layer
var (
resultChan = make(chan layer.Layer)
@@ -188,10 +206,10 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
l, err := fs.resolver.Resolve(ctx, s.Hosts, s.Name, s.Target)
if err == nil {
resultChan <- l
fs.prefetch(ctx, l, defaultPrefetchSize, start)
return
}
rErr = errors.Wrapf(rErr, "failed to resolve layer %q from %q: %v",
s.Target.Digest, s.Name, err)
rErr = errors.Wrapf(rErr, "failed to resolve layer %q from %q: %v", s.Target.Digest, s.Name, err)
}
errChan <- rErr
}()
@@ -202,12 +220,17 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
desc := desc
go func() {
// Avoids to get canceled by client.
ctx := log.WithLogger(context.Background(),
log.G(ctx).WithField("mountpoint", mountpoint))
err := fs.resolver.Cache(ctx, preResolve.Hosts, preResolve.Name, desc)
ctx := log.WithLogger(context.Background(), log.G(ctx).WithField("mountpoint", mountpoint))
l, err := fs.resolver.Resolve(ctx, preResolve.Hosts, preResolve.Name, desc)
if err != nil {
log.G(ctx).WithError(err).Debug("failed to pre-resolve")
return
}
fs.prefetch(ctx, l, defaultPrefetchSize, start)
// Release this layer because this isn't target and we don't use it anymore here.
// However, this will remain on the resolver cache until eviction.
l.Done()
}()
}
@@ -255,7 +278,7 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
// Verification must be done. Don't mount this layer.
return fmt.Errorf("digest of TOC JSON must be passed")
}
node, err := l.RootNode()
node, err := l.RootNode(0)
if err != nil {
log.G(ctx).WithError(err).Warnf("Failed to get root node")
return errors.Wrapf(err, "failed to get root node")
@@ -263,7 +286,7 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
// Measuring duration of Mount operation for resolved layer.
digest := l.Info().Digest // get layer sha
defer commonmetrics.MeasureLatency(commonmetrics.Mount, digest, start)
defer commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.Mount, digest, start)
// Register the mountpoint layer
fs.layerMu.Lock()
@@ -271,41 +294,6 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s
fs.layerMu.Unlock()
fs.metricsController.Add(mountpoint, l)
// Prefetch this layer. We prefetch several layers in parallel. The first
// Check() for this layer waits for the prefetch completion.
if !fs.noprefetch {
prefetchSize := fs.prefetchSize
if psStr, ok := labels[config.TargetPrefetchSizeLabel]; ok {
if ps, err := strconv.ParseInt(psStr, 10, 64); err == nil {
prefetchSize = ps
}
}
go func() {
fs.backgroundTaskManager.DoPrioritizedTask()
defer fs.backgroundTaskManager.DonePrioritizedTask()
if err := l.Prefetch(prefetchSize); err != nil {
log.G(ctx).WithError(err).Warnf("failed to prefetch layer=%v", digest)
return
}
log.G(ctx).Debug("completed to prefetch")
}()
}
// Fetch whole layer aggressively in background. We use background
// reader for this so prioritized tasks(Mount, Check, etc...) can
// interrupt the reading. This can avoid disturbing prioritized tasks
// about NW traffic.
if !fs.noBackgroundFetch {
go func() {
if err := l.BackgroundFetch(); err != nil {
log.G(ctx).WithError(err).Warnf("failed to fetch whole layer=%v", digest)
return
}
commonmetrics.LogLatencyForLastOnDemandFetch(ctx, digest, start, l.Info().ReadTime) // write log record for the latency between mount start and last on demand fetch
log.G(ctx).Debug("completed to fetch all layer data in background")
}()
}
// mount the node to the specified mountpoint
// TODO: bind mount the state directory as a read-only fs on snapshotter's side
rawFS := fusefs.NewNodeFS(node, &fusefs.Options{
@@ -341,7 +329,7 @@ func (fs *filesystem) Check(ctx context.Context, mountpoint string, labels map[s
fs.backgroundTaskManager.DoPrioritizedTask()
defer fs.backgroundTaskManager.DonePrioritizedTask()
defer commonmetrics.MeasureLatency(commonmetrics.PrefetchesCompleted, digest.FromString(""), time.Now()) // measuring the time the container launch is blocked on prefetch to complete
defer commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.PrefetchesCompleted, digest.FromString(""), time.Now()) // measuring the time the container launch is blocked on prefetch to complete
ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", mountpoint))
@@ -422,6 +410,23 @@ func (fs *filesystem) Unmount(ctx context.Context, mountpoint string) error {
return syscall.Unmount(mountpoint, syscall.MNT_FORCE)
}
func (fs *filesystem) prefetch(ctx context.Context, l layer.Layer, defaultPrefetchSize int64, start time.Time) {
// Prefetch a layer. The first Check() for this layer waits for the prefetch completion.
if !fs.noprefetch {
go l.Prefetch(defaultPrefetchSize)
}
// Fetch whole layer aggressively in background.
if !fs.noBackgroundFetch {
go func() {
if err := l.BackgroundFetch(); err == nil {
// write log record for the latency between mount start and last on demand fetch
commonmetrics.LogLatencyForLastOnDemandFetch(ctx, l.Info().Digest, start, l.Info().ReadTime)
}
}()
}
}
// neighboringLayers returns layer descriptors except the `target` layer in the specified manifest.
func neighboringLayers(manifest ocispec.Manifest, target ocispec.Descriptor) (descs []ocispec.Descriptor) {
for _, desc := range manifest.Layers {

View File

@@ -37,11 +37,15 @@ import (
"github.com/containerd/containerd/reference"
"github.com/containerd/stargz-snapshotter/cache"
"github.com/containerd/stargz-snapshotter/estargz"
"github.com/containerd/stargz-snapshotter/estargz/zstdchunked"
"github.com/containerd/stargz-snapshotter/fs/config"
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
"github.com/containerd/stargz-snapshotter/fs/reader"
"github.com/containerd/stargz-snapshotter/fs/remote"
"github.com/containerd/stargz-snapshotter/fs/source"
"github.com/containerd/stargz-snapshotter/metadata"
dbmetadata "github.com/containerd/stargz-snapshotter/metadata/db"
memorymetadata "github.com/containerd/stargz-snapshotter/metadata/memory"
"github.com/containerd/stargz-snapshotter/task"
"github.com/containerd/stargz-snapshotter/util/lrucache"
"github.com/containerd/stargz-snapshotter/util/namedmutex"
@@ -50,6 +54,7 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
)
const (
@@ -58,6 +63,8 @@ const (
defaultMaxCacheFds = 10
defaultPrefetchTimeoutSec = 10
memoryCacheType = "memory"
memoryMetadataType = "memory"
dbMetadataType = "db"
)
// Layer represents a layer.
@@ -66,7 +73,7 @@ type Layer interface {
Info() Info
// RootNode returns the root node of this layer.
RootNode() (fusefs.InodeEmbedder, error)
RootNode(baseInode uint32) (fusefs.InodeEmbedder, error)
// Check checks if the layer is still connectable.
Check() error
@@ -75,14 +82,15 @@ type Layer interface {
Refresh(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) error
// Verify verifies this layer using the passed TOC Digest.
// Nop if Verify() or SkipVerify() was already called.
Verify(tocDigest digest.Digest) (err error)
// SkipVerify skips verification for this layer.
// Nop if Verify() or SkipVerify() was already called.
SkipVerify()
// Prefetch prefetches the specified size. If the layer is eStargz and contains landmark files,
// the range indicated by these files is respected.
// Calling this function before calling Verify or SkipVerify will fail.
Prefetch(prefetchSize int64) error
// ReadAt reads this layer.
@@ -93,7 +101,6 @@ type Layer interface {
// BackgroundFetch fetches the entire layer contents to the cache.
// Fetching contents is done as a background task.
// Calling this function before calling Verify or SkipVerify will fail.
BackgroundFetch() error
// Done releases the reference to this layer. The resources related to this layer will be
@@ -122,10 +129,11 @@ type Resolver struct {
backgroundTaskManager *task.BackgroundTaskManager
resolveLock *namedmutex.NamedMutex
config config.Config
newMetadataReader func(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error)
}
// NewResolver returns a new layer resolver.
func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager, cfg config.Config) (*Resolver, error) {
func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager, cfg config.Config, resolveHandlers map[string]remote.Handler) (*Resolver, error) {
resolveResultEntry := cfg.ResolveResultEntry
if resolveResultEntry == 0 {
resolveResultEntry = defaultResolveResultEntry
@@ -158,15 +166,42 @@ func NewResolver(root string, backgroundTaskManager *task.BackgroundTaskManager,
logrus.WithField("key", key).Debugf("cleaned up blob")
}
if err := os.MkdirAll(root, 0700); err != nil {
return nil, err
}
var newReader func(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error)
switch cfg.MetadataStore {
case "", memoryMetadataType:
newReader = memorymetadata.NewReader
case dbMetadataType:
bOpts := bolt.Options{
NoFreelistSync: true,
InitialMmapSize: 64 * 1024 * 1024,
FreelistType: bolt.FreelistMapType,
}
db, err := bolt.Open(filepath.Join(root, "metadata.db"), 0600, &bOpts)
if err != nil {
return nil, err
}
newReader = func(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error) {
return dbmetadata.NewReader(db, sr, opts...)
}
default:
return nil, fmt.Errorf("unknown metadata store type: %v; must be %v or %v",
cfg.MetadataStore, memoryMetadataType, dbMetadataType)
}
return &Resolver{
rootDir: root,
resolver: remote.NewResolver(cfg.BlobConfig),
resolver: remote.NewResolver(cfg.BlobConfig, resolveHandlers),
layerCache: layerCache,
blobCache: blobCache,
prefetchTimeout: prefetchTimeout,
backgroundTaskManager: backgroundTaskManager,
config: cfg,
resolveLock: new(namedmutex.NamedMutex),
newMetadataReader: newReader,
}, nil
}
@@ -192,6 +227,7 @@ func newCache(root string, cacheType string, cfg config.Config) (cache.BlobCache
}
dCache, fCache := lrucache.New(maxDataEntry), lrucache.New(maxFdEntry)
dCache.OnEvicted = func(key string, value interface{}) {
value.(*bytes.Buffer).Reset()
bufPool.Put(value)
}
fCache.OnEvicted = func(key string, value interface{}) {
@@ -218,7 +254,7 @@ func newCache(root string, cacheType string, cfg config.Config) (cache.BlobCache
}
// Resolve resolves a layer based on the passed layer blob information.
func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor, esgzOpts ...estargz.OpenOption) (_ Layer, retErr error) {
func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor, esgzOpts ...metadata.Option) (_ Layer, retErr error) {
name := refspec.String() + "/" + desc.Digest.String()
// Wait if resolving this layer is already running. The result
@@ -277,18 +313,23 @@ func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refs
return blobR.ReadAt(p, offset)
}), 0, blobR.Size())
// define telemetry hooks to measure latency metrics inside estargz package
telemetry := estargz.Telemetry{
telemetry := metadata.Telemetry{
GetFooterLatency: func(start time.Time) {
commonmetrics.MeasureLatency(commonmetrics.StargzFooterGet, desc.Digest, start)
commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.StargzFooterGet, desc.Digest, start)
},
GetTocLatency: func(start time.Time) {
commonmetrics.MeasureLatency(commonmetrics.StargzTocGet, desc.Digest, start)
commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.StargzTocGet, desc.Digest, start)
},
DeserializeTocLatency: func(start time.Time) {
commonmetrics.MeasureLatency(commonmetrics.DeserializeTocJSON, desc.Digest, start)
commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.DeserializeTocJSON, desc.Digest, start)
},
}
vr, err := reader.NewReader(sr, fsCache, desc.Digest, &telemetry, esgzOpts...)
meta, err := r.newMetadataReader(sr,
append(esgzOpts, metadata.WithTelemetry(&telemetry), metadata.WithDecompressors(new(zstdchunked.Decompressor)))...)
if err != nil {
return nil, err
}
vr, err := reader.NewReader(meta, fsCache, desc.Digest)
if err != nil {
return nil, errors.Wrap(err, "failed to read layer")
}
@@ -349,18 +390,6 @@ func (r *Resolver) resolveBlob(ctx context.Context, hosts source.RegistryHosts,
return &blobRef{cachedB.(remote.Blob), done}, nil
}
// Cache is similar to Resolve but the result isn't returned. Instead, it'll be stored in the cache.
func (r *Resolver) Cache(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) error {
l, err := r.Resolve(ctx, hosts, refspec, desc)
if err != nil {
return err
}
// Release this layer. However, this will remain on the cache until eviction.
// Until then, the client can reuse this (already pre-resolved) layer.
l.Done()
return nil
}
func newLayer(
resolver *Resolver,
desc ocispec.Descriptor,
@@ -390,15 +419,22 @@ type layer struct {
closed bool
closedMu sync.Mutex
prefetchOnce sync.Once
backgroundFetchOnce sync.Once
}
func (l *layer) Info() Info {
var readTime time.Time
if l.r != nil {
readTime = l.r.LastOnDemandReadTime()
}
return Info{
Digest: l.desc.Digest,
Size: l.blob.Size(),
FetchedSize: l.blob.FetchedSize(),
PrefetchSize: l.prefetchedSize(),
ReadTime: l.r.LastOnDemandReadTime(),
ReadTime: readTime,
}
}
@@ -427,36 +463,57 @@ func (l *layer) Verify(tocDigest digest.Digest) (err error) {
if l.isClosed() {
return fmt.Errorf("layer is already closed")
}
if l.r != nil {
return nil
}
l.r, err = l.verifiableReader.VerifyTOC(tocDigest)
return
}
func (l *layer) SkipVerify() {
if l.r != nil {
return
}
l.r = l.verifiableReader.SkipVerify()
}
func (l *layer) Prefetch(prefetchSize int64) error {
func (l *layer) Prefetch(prefetchSize int64) (err error) {
l.prefetchOnce.Do(func() {
ctx := context.Background()
l.resolver.backgroundTaskManager.DoPrioritizedTask()
defer l.resolver.backgroundTaskManager.DonePrioritizedTask()
err = l.prefetch(ctx, prefetchSize)
if err != nil {
log.G(ctx).WithError(err).Warnf("failed to prefetch layer=%v", l.desc.Digest)
return
}
log.G(ctx).Debug("completed to prefetch")
})
return
}
func (l *layer) prefetch(ctx context.Context, prefetchSize int64) error {
defer l.prefetchWaiter.done() // Notify the completion
ctx := context.Background()
// Measuring the total time to complete prefetch (use defer func() because l.Info().PrefetchSize is set later)
start := time.Now()
defer func() {
commonmetrics.WriteLatencyWithBytesLogValue(ctx, l.Info().Digest, commonmetrics.PrefetchTotal, start, commonmetrics.PrefetchSize, l.Info().PrefetchSize)
commonmetrics.WriteLatencyWithBytesLogValue(ctx, l.desc.Digest, commonmetrics.PrefetchTotal, start, commonmetrics.PrefetchSize, l.prefetchedSize())
}()
if l.isClosed() {
return fmt.Errorf("layer is already closed")
}
if l.r == nil {
return fmt.Errorf("layer hasn't been verified yet")
}
lr := l.r
if _, ok := lr.Lookup(estargz.NoPrefetchLandmark); ok {
rootID := l.verifiableReader.Metadata().RootID()
if _, _, err := l.verifiableReader.Metadata().GetChild(rootID, estargz.NoPrefetchLandmark); err == nil {
// do not prefetch this layer
return nil
} else if e, ok := lr.Lookup(estargz.PrefetchLandmark); ok {
} else if id, _, err := l.verifiableReader.Metadata().GetChild(rootID, estargz.PrefetchLandmark); err == nil {
offset, err := l.verifiableReader.Metadata().GetOffset(id)
if err != nil {
return errors.Wrapf(err, "failed to get offset of prefetch landmark")
}
// override the prefetch size with optimized value
prefetchSize = e.Offset
prefetchSize = offset
} else if prefetchSize > l.blob.Size() {
// adjust prefetch size not to exceed the whole layer size
prefetchSize = l.blob.Size()
@@ -465,7 +522,7 @@ func (l *layer) Prefetch(prefetchSize int64) error {
// Fetch the target range
downloadStart := time.Now()
err := l.blob.Cache(0, prefetchSize)
commonmetrics.WriteLatencyLogValue(ctx, l.Info().Digest, commonmetrics.PrefetchDownload, downloadStart) // time to download prefetch data
commonmetrics.WriteLatencyLogValue(ctx, l.desc.Digest, commonmetrics.PrefetchDownload, downloadStart) // time to download prefetch data
if err != nil {
return errors.Wrap(err, "failed to prefetch layer")
@@ -478,11 +535,10 @@ func (l *layer) Prefetch(prefetchSize int64) error {
// Cache uncompressed contents of the prefetched range
decompressStart := time.Now()
err = lr.Cache(reader.WithFilter(func(e *estargz.TOCEntry) bool {
return e.Offset < prefetchSize // Cache only prefetch target
err = l.verifiableReader.Cache(reader.WithFilter(func(offset int64) bool {
return offset < prefetchSize // Cache only prefetch target
}))
commonmetrics.WriteLatencyLogValue(ctx, l.Info().Digest, commonmetrics.PrefetchDecompress, decompressStart) // time to decompress prefetch data
commonmetrics.WriteLatencyLogValue(ctx, l.desc.Digest, commonmetrics.PrefetchDecompress, decompressStart) // time to decompress prefetch data
if err != nil {
return errors.Wrap(err, "failed to cache prefetched layer")
}
@@ -497,20 +553,28 @@ func (l *layer) WaitForPrefetchCompletion() error {
return l.prefetchWaiter.wait(l.resolver.prefetchTimeout)
}
func (l *layer) BackgroundFetch() error {
ctx := context.Background()
defer commonmetrics.WriteLatencyLogValue(ctx, l.Info().Digest, commonmetrics.BackgroundFetchTotal, time.Now())
func (l *layer) BackgroundFetch() (err error) {
l.backgroundFetchOnce.Do(func() {
ctx := context.Background()
err = l.backgroundFetch(ctx)
if err != nil {
log.G(ctx).WithError(err).Warnf("failed to fetch whole layer=%v", l.desc.Digest)
return
}
log.G(ctx).Debug("completed to fetch all layer data in background")
})
return
}
func (l *layer) backgroundFetch(ctx context.Context) error {
defer commonmetrics.WriteLatencyLogValue(ctx, l.desc.Digest, commonmetrics.BackgroundFetchTotal, time.Now())
if l.isClosed() {
return fmt.Errorf("layer is already closed")
}
if l.r == nil {
return fmt.Errorf("layer hasn't been verified yet")
}
lr := l.r
br := io.NewSectionReader(readerAtFunc(func(p []byte, offset int64) (retN int, retErr error) {
l.resolver.backgroundTaskManager.InvokeBackgroundTask(func(ctx context.Context) {
// Measuring the time to download background fetch data (in milliseconds)
defer commonmetrics.MeasureLatency(commonmetrics.BackgroundFetchDownload, l.Info().Digest, time.Now()) // time to download background fetch data
defer commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.BackgroundFetchDownload, l.Info().Digest, time.Now()) // time to download background fetch data
retN, retErr = l.blob.ReadAt(
p,
offset,
@@ -520,8 +584,8 @@ func (l *layer) BackgroundFetch() error {
}, 120*time.Second)
return
}), 0, l.blob.Size())
defer commonmetrics.WriteLatencyLogValue(ctx, l.Info().Digest, commonmetrics.BackgroundFetchDecompress, time.Now()) // time to decompress background fetch data (in milliseconds)
return lr.Cache(
defer commonmetrics.WriteLatencyLogValue(ctx, l.desc.Digest, commonmetrics.BackgroundFetchDecompress, time.Now()) // time to decompress background fetch data (in milliseconds)
return l.verifiableReader.Cache(
reader.WithReader(br), // Read contents in background
reader.WithCacheOpts(cache.Direct()), // Do not pollute mem cache
)
@@ -531,14 +595,14 @@ func (l *layerRef) Done() {
l.done()
}
func (l *layer) RootNode() (fusefs.InodeEmbedder, error) {
func (l *layer) RootNode(baseInode uint32) (fusefs.InodeEmbedder, error) {
if l.isClosed() {
return nil, fmt.Errorf("layer is already closed")
}
if l.r == nil {
return nil, fmt.Errorf("layer hasn't been verified yet")
}
return newNode(l.desc.Digest, l.r, l.blob)
return newNode(l.desc.Digest, l.r, l.blob, baseInode)
}
func (l *layer) ReadAt(p []byte, offset int64, opts ...remote.Option) (int, error) {

View File

@@ -26,6 +26,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
@@ -34,15 +35,17 @@ import (
"sync"
"syscall"
"time"
"unsafe"
"github.com/containerd/containerd/log"
"github.com/containerd/stargz-snapshotter/estargz"
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
"github.com/containerd/stargz-snapshotter/fs/reader"
"github.com/containerd/stargz-snapshotter/fs/remote"
"github.com/containerd/stargz-snapshotter/metadata"
fusefs "github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
digest "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -58,27 +61,70 @@ const (
var opaqueXattrs = []string{"trusted.overlay.opaque", "user.overlay.opaque"}
func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob) (fusefs.InodeEmbedder, error) {
root, ok := r.Lookup("")
if !ok {
return nil, fmt.Errorf("failed to get a TOCEntry of the root")
func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseInode uint32) (fusefs.InodeEmbedder, error) {
rootID := r.Metadata().RootID()
rootAttr, err := r.Metadata().GetAttr(rootID)
if err != nil {
return nil, err
}
ffs := &fs{
r: r,
layerDigest: layerDgst,
baseInode: baseInode,
rootID: rootID,
}
ffs.s = ffs.newState(layerDgst, blob)
return &node{
r: r,
e: root,
s: newState(layerDgst, blob),
layerSha: layerDgst,
id: rootID,
attr: rootAttr,
fs: ffs,
}, nil
}
// fs contains global metadata used by nodes
type fs struct {
r reader.Reader
s *state
layerDigest digest.Digest
baseInode uint32
rootID uint32
}
func (fs *fs) inodeOfState() uint64 {
return (uint64(fs.baseInode) << 32) | 1 // reserved
}
func (fs *fs) inodeOfStatFile() uint64 {
return (uint64(fs.baseInode) << 32) | 2 // reserved
}
func (fs *fs) inodeOfID(id uint32) (uint64, error) {
// 0 is reserved by go-fuse 1 and 2 are reserved by the state dir
if id > ^uint32(0)-3 {
return 0, fmt.Errorf("too many inodes")
}
return (uint64(fs.baseInode) << 32) | uint64(3+id), nil
}
// node is a filesystem inode abstraction.
type node struct {
fusefs.Inode
r reader.Reader
e *estargz.TOCEntry
s *state
layerSha digest.Digest
opaque bool // true if this node is an overlayfs opaque directory
fs *fs
id uint32
attr metadata.Attr
ents []fuse.DirEntry
entsCached bool
}
func (n *node) isRootNode() bool {
return n.id == n.fs.rootID
}
func (n *node) isOpaque() bool {
if _, _, err := n.fs.r.Metadata().GetChild(n.id, whiteoutOpaqueDir); err == nil {
return true
}
return false
}
var _ = (fusefs.InodeEmbedder)((*node)(nil))
@@ -86,47 +132,75 @@ var _ = (fusefs.InodeEmbedder)((*node)(nil))
var _ = (fusefs.NodeReaddirer)((*node)(nil))
func (n *node) Readdir(ctx context.Context) (fusefs.DirStream, syscall.Errno) {
// Measure how long node_readdir operation takes.
ents, errno := n.readdir()
if errno != 0 {
return nil, errno
}
return fusefs.NewListDirStream(ents), 0
}
func (n *node) readdir() ([]fuse.DirEntry, syscall.Errno) {
// Measure how long node_readdir operation takes (in microseconds).
start := time.Now() // set start time
defer commonmetrics.MeasureLatency(commonmetrics.NodeReaddir, n.layerSha, start)
defer commonmetrics.MeasureLatencyInMicroseconds(commonmetrics.NodeReaddir, n.fs.layerDigest, start)
if n.entsCached {
return n.ents, 0
}
isRoot := n.isRootNode()
var ents []fuse.DirEntry
whiteouts := map[string]*estargz.TOCEntry{}
whiteouts := map[string]uint32{}
normalEnts := map[string]bool{}
n.e.ForeachChild(func(baseName string, ent *estargz.TOCEntry) bool {
var lastErr error
if err := n.fs.r.Metadata().ForeachChild(n.id, func(name string, id uint32, mode os.FileMode) bool {
// We don't want to show prefetch landmarks in "/".
if n.e.Name == "" && (baseName == estargz.PrefetchLandmark || baseName == estargz.NoPrefetchLandmark) {
if isRoot && (name == estargz.PrefetchLandmark || name == estargz.NoPrefetchLandmark) {
return true
}
// We don't want to show whiteouts.
if strings.HasPrefix(baseName, whiteoutPrefix) {
if baseName == whiteoutOpaqueDir {
if strings.HasPrefix(name, whiteoutPrefix) {
if name == whiteoutOpaqueDir {
return true
}
// Add the overlayfs-compiant whiteout later.
whiteouts[baseName] = ent
whiteouts[name] = id
return true
}
// This is a normal entry.
normalEnts[baseName] = true
normalEnts[name] = true
ino, err := n.fs.inodeOfID(id)
if err != nil {
lastErr = err
return false
}
ents = append(ents, fuse.DirEntry{
Mode: modeOfEntry(ent),
Name: baseName,
Ino: inodeOfEnt(ent),
Mode: fileModeToSystemMode(mode),
Name: name,
Ino: ino,
})
return true
})
}); err != nil || lastErr != nil {
n.fs.s.report(fmt.Errorf("node.Readdir: err = %v; lastErr = %v", err, lastErr))
return nil, syscall.EIO
}
// Append whiteouts if no entry replaces the target entry in the lower layer.
for w, ent := range whiteouts {
for w, id := range whiteouts {
if !normalEnts[w[len(whiteoutPrefix):]] {
ino, err := n.fs.inodeOfID(id)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Readdir: err = %v; lastErr = %v", err, lastErr))
return nil, syscall.EIO
}
ents = append(ents, fuse.DirEntry{
Mode: syscall.S_IFCHR,
Name: w[len(whiteoutPrefix):],
Ino: inodeOfEnt(ent),
Ino: ino,
})
}
@@ -136,15 +210,19 @@ func (n *node) Readdir(ctx context.Context) (fusefs.DirStream, syscall.Errno) {
sort.Slice(ents, func(i, j int) bool {
return ents[i].Name < ents[j].Name
})
n.ents, n.entsCached = ents, true // cache it
return fusefs.NewListDirStream(ents), 0
return ents, 0
}
var _ = (fusefs.NodeLookuper)((*node)(nil))
func (n *node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fusefs.Inode, syscall.Errno) {
isRoot := n.isRootNode()
// We don't want to show prefetch landmarks in "/".
if n.e.Name == "" && (name == estargz.PrefetchLandmark || name == estargz.NoPrefetchLandmark) {
if isRoot && (name == estargz.PrefetchLandmark || name == estargz.NoPrefetchLandmark) {
return nil, syscall.ENOENT
}
@@ -154,63 +232,111 @@ func (n *node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fu
}
// state directory
if n.e.Name == "" && name == stateDirName {
return n.NewInode(ctx, n.s, stateToAttr(n.s, &out.Attr)), 0
if isRoot && name == stateDirName {
return n.NewInode(ctx, n.fs.s, n.fs.stateToAttr(&out.Attr)), 0
}
// lookup stargz TOCEntry
ce, ok := n.e.LookupChild(name)
if !ok {
// If the entry exists as a whiteout, show an overlayfs-styled whiteout node.
if wh, ok := n.e.LookupChild(fmt.Sprintf("%s%s", whiteoutPrefix, name)); ok {
return n.NewInode(ctx, &whiteout{
e: wh,
}, entryToWhAttr(wh, &out.Attr)), 0
// lookup on memory nodes
if cn := n.GetChild(name); cn != nil {
switch tn := cn.Operations().(type) {
case *node:
ino, err := n.fs.inodeOfID(tn.id)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Lookup: %v", err))
return nil, syscall.EIO
}
entryToAttr(ino, tn.attr, &out.Attr)
case *whiteout:
ino, err := n.fs.inodeOfID(tn.id)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Lookup: %v", err))
return nil, syscall.EIO
}
entryToAttr(ino, tn.attr, &out.Attr)
default:
n.fs.s.report(fmt.Errorf("node.Lookup: uknown node type detected"))
return nil, syscall.EIO
}
return cn, 0
}
// early return if this entry doesn't exist
if n.entsCached {
var found bool
for _, e := range n.ents {
if e.Name == name {
found = true
}
}
if !found {
return nil, syscall.ENOENT
}
}
id, ce, err := n.fs.r.Metadata().GetChild(n.id, name)
if err != nil {
// If the entry exists as a whiteout, show an overlayfs-styled whiteout node.
if whID, wh, err := n.fs.r.Metadata().GetChild(n.id, fmt.Sprintf("%s%s", whiteoutPrefix, name)); err == nil {
ino, err := n.fs.inodeOfID(whID)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Lookup: %v", err))
return nil, syscall.EIO
}
return n.NewInode(ctx, &whiteout{
id: whID,
fs: n.fs,
attr: wh,
}, entryToWhAttr(ino, wh, &out.Attr)), 0
}
n.readdir() // This code path is very expensive. Cache child entries here so that the next call don't reach here.
return nil, syscall.ENOENT
}
var opaque bool
if _, ok := ce.LookupChild(whiteoutOpaqueDir); ok {
// This entry is an opaque directory so make it recognizable for overlayfs.
opaque = true
}
ino, err := n.fs.inodeOfID(id)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Lookup: %v", err))
return nil, syscall.EIO
}
return n.NewInode(ctx, &node{
r: n.r,
e: ce,
s: n.s,
layerSha: n.layerSha,
opaque: opaque,
}, entryToAttr(ce, &out.Attr)), 0
id: id,
fs: n.fs,
attr: ce,
}, entryToAttr(ino, ce, &out.Attr)), 0
}
var _ = (fusefs.NodeOpener)((*node)(nil))
func (n *node) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
ra, err := n.r.OpenFile(n.e.Name)
ra, err := n.fs.r.OpenFile(n.id)
if err != nil {
n.s.report(fmt.Errorf("failed to open node: %v", err))
n.fs.s.report(fmt.Errorf("node.Open: %v", err))
return nil, 0, syscall.EIO
}
return &file{
n: n,
e: n.e,
ra: ra,
}, 0, 0
}, fuse.FOPEN_KEEP_CACHE, 0
}
var _ = (fusefs.NodeGetattrer)((*node)(nil))
func (n *node) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno {
entryToAttr(n.e, &out.Attr)
ino, err := n.fs.inodeOfID(n.id)
if err != nil {
n.fs.s.report(fmt.Errorf("node.Getattr: %v", err))
return syscall.EIO
}
entryToAttr(ino, n.attr, &out.Attr)
return 0
}
var _ = (fusefs.NodeGetxattrer)((*node)(nil))
func (n *node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
ent := n.attr
opq := n.isOpaque()
for _, opaqueXattr := range opaqueXattrs {
if attr == opaqueXattr && n.opaque {
if attr == opaqueXattr && opq {
// This node is an opaque directory so give overlayfs-compliant indicator.
if len(dest) < len(opaqueXattrValue) {
return uint32(len(opaqueXattrValue)), syscall.ERANGE
@@ -218,7 +344,7 @@ func (n *node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
return uint32(copy(dest, opaqueXattrValue)), 0
}
}
if v, ok := n.e.Xattrs[attr]; ok {
if v, ok := ent.Xattrs[attr]; ok {
if len(dest) < len(v) {
return uint32(len(v)), syscall.ERANGE
}
@@ -230,14 +356,16 @@ func (n *node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
var _ = (fusefs.NodeListxattrer)((*node)(nil))
func (n *node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
ent := n.attr
opq := n.isOpaque()
var attrs []byte
if n.opaque {
if opq {
// This node is an opaque directory so add overlayfs-compliant indicator.
for _, opaqueXattr := range opaqueXattrs {
attrs = append(attrs, []byte(opaqueXattr+"\x00")...)
}
}
for k := range n.e.Xattrs {
for k := range ent.Xattrs {
attrs = append(attrs, []byte(k+"\x00")...)
}
if len(dest) < len(attrs) {
@@ -249,7 +377,8 @@ func (n *node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn
var _ = (fusefs.NodeReadlinker)((*node)(nil))
func (n *node) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return []byte(n.e.LinkName), 0
ent := n.attr
return []byte(ent.LinkName), 0
}
var _ = (fusefs.NodeStatfser)((*node)(nil))
@@ -262,18 +391,17 @@ func (n *node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
// file is a file abstraction which implements file handle in go-fuse.
type file struct {
n *node
e *estargz.TOCEntry
ra io.ReaderAt
}
var _ = (fusefs.FileReader)((*file)(nil))
func (f *file) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
defer commonmetrics.MeasureLatency(commonmetrics.ReadOnDemand, f.n.layerSha, time.Now()) // measure time for on-demand file reads (in milliseconds)
defer commonmetrics.IncOperationCount(commonmetrics.OnDemandReadAccessCount, f.n.layerSha) // increment the counter for on-demand file accesses
defer commonmetrics.MeasureLatencyInMicroseconds(commonmetrics.ReadOnDemand, f.n.fs.layerDigest, time.Now()) // measure time for on-demand file reads (in microseconds)
defer commonmetrics.IncOperationCount(commonmetrics.OnDemandReadAccessCount, f.n.fs.layerDigest) // increment the counter for on-demand file accesses
n, err := f.ra.ReadAt(dest, off)
if err != nil && err != io.EOF {
f.n.s.report(fmt.Errorf("failed to read node: %v", err))
f.n.fs.s.report(fmt.Errorf("file.Read: %v", err))
return nil, syscall.EIO
}
return fuse.ReadResultData(dest[:n]), 0
@@ -282,20 +410,32 @@ func (f *file) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResul
var _ = (fusefs.FileGetattrer)((*file)(nil))
func (f *file) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
entryToAttr(f.e, &out.Attr)
ino, err := f.n.fs.inodeOfID(f.n.id)
if err != nil {
f.n.fs.s.report(fmt.Errorf("file.Getattr: %v", err))
return syscall.EIO
}
entryToAttr(ino, f.n.attr, &out.Attr)
return 0
}
// whiteout is a whiteout abstraction compliant to overlayfs.
type whiteout struct {
fusefs.Inode
e *estargz.TOCEntry
id uint32
fs *fs
attr metadata.Attr
}
var _ = (fusefs.NodeGetattrer)((*whiteout)(nil))
func (w *whiteout) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno {
entryToWhAttr(w.e, &out.Attr)
ino, err := w.fs.inodeOfID(w.id)
if err != nil {
w.fs.s.report(fmt.Errorf("whiteout.Getattr: %v", err))
return syscall.EIO
}
entryToWhAttr(ino, w.attr, &out.Attr)
return 0
}
@@ -308,7 +448,7 @@ func (w *whiteout) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errn
// newState provides new state directory node.
// It creates statFile at the same time to give it stable inode number.
func newState(layerDigest digest.Digest, blob remote.Blob) *state {
func (fs *fs) newState(layerDigest digest.Digest, blob remote.Blob) *state {
return &state{
statFile: &statFile{
name: layerDigest.String() + ".json",
@@ -317,7 +457,9 @@ func newState(layerDigest digest.Digest, blob remote.Blob) *state {
Size: blob.Size(),
},
blob: blob,
fs: fs,
},
fs: fs,
}
}
@@ -328,6 +470,7 @@ func newState(layerDigest digest.Digest, blob remote.Blob) *state {
type state struct {
fusefs.Inode
statFile *statFile
fs *fs
}
var _ = (fusefs.NodeReaddirer)((*state)(nil))
@@ -337,7 +480,7 @@ func (s *state) Readdir(ctx context.Context) (fusefs.DirStream, syscall.Errno) {
{
Mode: statFileMode,
Name: s.statFile.name,
Ino: inodeOfStatFile(s.statFile),
Ino: s.fs.inodeOfStatFile(),
},
}), 0
}
@@ -358,7 +501,7 @@ func (s *state) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*f
var _ = (fusefs.NodeGetattrer)((*state)(nil))
func (s *state) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno {
stateToAttr(s, &out.Attr)
s.fs.stateToAttr(&out.Attr)
return 0
}
@@ -392,6 +535,7 @@ type statFile struct {
blob remote.Blob
statJSON statJSON
mu sync.Mutex
fs *fs
}
var _ = (fusefs.NodeOpener)((*statFile)(nil))
@@ -430,10 +574,22 @@ func (sf *statFile) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Err
return 0
}
// logContents puts the contents of statFile in the log
// to keep that information accessible for troubleshooting.
// The entries naming is kept to be consistend with the field naming in statJSON.
func (sf *statFile) logContents() {
ctx := context.Background()
log.G(ctx).WithFields(logrus.Fields{
"digest": sf.statJSON.Digest, "size": sf.statJSON.Size,
"fetchedSize": sf.statJSON.FetchedSize, "fetchedPercent": sf.statJSON.FetchedPercent,
}).WithError(errors.New(sf.statJSON.Error)).Error("statFile error")
}
func (sf *statFile) report(err error) {
sf.mu.Lock()
defer sf.mu.Unlock()
sf.statJSON.Error = err.Error()
sf.logContents()
}
func (sf *statFile) attr(out *fuse.Attr) (fusefs.StableAttr, syscall.Errno) {
@@ -445,7 +601,7 @@ func (sf *statFile) attr(out *fuse.Attr) (fusefs.StableAttr, syscall.Errno) {
return fusefs.StableAttr{}, syscall.EIO
}
return statFileToAttr(sf, uint64(len(st)), out), 0
return sf.fs.statFileToAttr(uint64(len(st)), out), 0
}
func (sf *statFile) updateStatUnlocked() ([]byte, error) {
@@ -459,24 +615,18 @@ func (sf *statFile) updateStatUnlocked() ([]byte, error) {
return j, nil
}
// inodeOfEnt calculates the inode number which is one-to-one correspondence
// with the TOCEntry instance.
func inodeOfEnt(e *estargz.TOCEntry) uint64 {
return uint64(uintptr(unsafe.Pointer(e)))
}
// entryToAttr converts stargz's TOCEntry to go-fuse's Attr.
func entryToAttr(e *estargz.TOCEntry, out *fuse.Attr) fusefs.StableAttr {
out.Ino = inodeOfEnt(e)
// entryToAttr converts metadata.Attr to go-fuse's Attr.
func entryToAttr(ino uint64, e metadata.Attr, out *fuse.Attr) fusefs.StableAttr {
out.Ino = ino
out.Size = uint64(e.Size)
out.Blksize = blockSize
out.Blocks = out.Size / uint64(out.Blksize)
if out.Size%uint64(out.Blksize) > 0 {
out.Blocks++
}
mtime := e.ModTime()
mtime := e.ModTime
out.SetTimes(nil, &mtime, nil)
out.Mode = modeOfEntry(e)
out.Mode = fileModeToSystemMode(e.Mode)
out.Owner = fuse.Owner{Uid: uint32(e.UID), Gid: uint32(e.GID)}
out.Rdev = uint32(unix.Mkdev(uint32(e.DevMajor), uint32(e.DevMinor)))
out.Nlink = uint32(e.NumLink)
@@ -494,14 +644,13 @@ func entryToAttr(e *estargz.TOCEntry, out *fuse.Attr) fusefs.StableAttr {
}
}
// entryToWhAttr converts stargz's TOCEntry to go-fuse's Attr of whiteouts.
func entryToWhAttr(e *estargz.TOCEntry, out *fuse.Attr) fusefs.StableAttr {
fi := e.Stat()
out.Ino = inodeOfEnt(e)
// entryToWhAttr converts metadata.Attr to go-fuse's Attr of whiteouts.
func entryToWhAttr(ino uint64, e metadata.Attr, out *fuse.Attr) fusefs.StableAttr {
out.Ino = ino
out.Size = 0
out.Blksize = blockSize
out.Blocks = 0
mtime := fi.ModTime()
mtime := e.ModTime
out.SetTimes(nil, &mtime, nil)
out.Mode = syscall.S_IFCHR
out.Owner = fuse.Owner{Uid: 0, Gid: 0}
@@ -518,15 +667,9 @@ func entryToWhAttr(e *estargz.TOCEntry, out *fuse.Attr) fusefs.StableAttr {
}
}
// inodeOfState calculates the inode number which is one-to-one correspondence
// with the state directory instance which was created on mount.
func inodeOfState(s *state) uint64 {
return uint64(uintptr(unsafe.Pointer(s)))
}
// stateToAttr converts state directory to go-fuse's Attr.
func stateToAttr(s *state, out *fuse.Attr) fusefs.StableAttr {
out.Ino = inodeOfState(s)
func (fs *fs) stateToAttr(out *fuse.Attr) fusefs.StableAttr {
out.Ino = fs.inodeOfState()
out.Size = 0
out.Blksize = blockSize
out.Blocks = 0
@@ -551,15 +694,10 @@ func stateToAttr(s *state, out *fuse.Attr) fusefs.StableAttr {
}
}
// inodeOfStatFile calculates the inode number which is one-to-one correspondence
// with the stat file instance which was created on mount.
func inodeOfStatFile(s *statFile) uint64 {
return uint64(uintptr(unsafe.Pointer(s)))
}
// statFileToAttr converts stat file to go-fuse's Attr.
func statFileToAttr(sf *statFile, size uint64, out *fuse.Attr) fusefs.StableAttr {
out.Ino = inodeOfStatFile(sf)
// func statFileToAttr(id uint64, sf *statFile, size uint64, out *fuse.Attr) fusefs.StableAttr {
func (fs *fs) statFileToAttr(size uint64, out *fuse.Attr) fusefs.StableAttr {
out.Ino = fs.inodeOfStatFile()
out.Size = size
out.Blksize = blockSize
out.Blocks = out.Size / uint64(out.Blksize)
@@ -584,10 +722,7 @@ func statFileToAttr(sf *statFile, size uint64, out *fuse.Attr) fusefs.StableAttr
}
}
// modeOfEntry gets system's mode bits from TOCEntry
func modeOfEntry(e *estargz.TOCEntry) uint32 {
m := e.Stat().Mode()
func fileModeToSystemMode(m os.FileMode) uint32 {
// Permission bits
res := uint32(m & os.ModePerm)

View File

@@ -27,8 +27,11 @@ import (
)
const (
// OperationLatencyKey is the key for stargz operation latency metrics.
OperationLatencyKey = "operation_duration"
// OperationLatencyKeyMilliseconds is the key for stargz operation latency metrics in milliseconds.
OperationLatencyKeyMilliseconds = "operation_duration_milliseconds"
// OperationLatencyKeyMicroseconds is the key for stargz operation latency metrics in microseconds.
OperationLatencyKeyMicroseconds = "operation_duration_microseconds"
// OperationCountKey is the key for stargz operation count metrics.
OperationCountKey = "operation_count"
@@ -71,18 +74,32 @@ const (
)
var (
// Buckets for OperationLatency metric in milliseconds.
latencyBuckets = []float64{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384} // in milliseconds
// Buckets for OperationLatency metrics.
latencyBucketsMilliseconds = []float64{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384} // in milliseconds
latencyBucketsMicroseconds = []float64{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024} // in microseconds
// operationLatency collects operation latency numbers by operation
// type and layer digest.
operationLatency = prometheus.NewHistogramVec(
// operationLatencyMilliseconds collects operation latency numbers in milliseconds grouped by
// operation, type and layer digest.
operationLatencyMilliseconds = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: OperationLatencyKey,
Name: OperationLatencyKeyMilliseconds,
Help: "Latency in milliseconds of stargz snapshotter operations. Broken down by operation type and layer sha.",
Buckets: latencyBuckets,
Buckets: latencyBucketsMilliseconds,
},
[]string{"operation_type", "layer"},
)
// operationLatencyMicroseconds collects operation latency numbers in microseconds grouped by
// operation, type and layer digest.
operationLatencyMicroseconds = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: OperationLatencyKeyMicroseconds,
Help: "Latency in microseconds of stargz snapshotter operations. Broken down by operation type and layer sha.",
Buckets: latencyBucketsMicroseconds,
},
[]string{"operation_type", "layer"},
)
@@ -120,22 +137,39 @@ func sinceInMilliseconds(start time.Time) float64 {
return float64(time.Since(start).Nanoseconds()) / 1e6
}
// sinceInMicroseconds gets the time since the specified start in microseconds.
// The division by 1e3 is made to have the microseconds value as floating point number, since the native method
// .Microseconds() returns an integer value and you can lost a precision for sub-microsecond values.
func sinceInMicroseconds(start time.Time) float64 {
return float64(time.Since(start).Nanoseconds()) / 1e3
}
// Register registers metrics. This is always called only once.
func Register() {
register.Do(func() {
prometheus.MustRegister(operationLatency)
prometheus.MustRegister(operationLatencyMilliseconds)
prometheus.MustRegister(operationLatencyMicroseconds)
prometheus.MustRegister(operationCount)
prometheus.MustRegister(bytesCount)
})
}
// MeasureLatency wraps the labels attachment as well as calling Observe into a single method.
// MeasureLatencyInMilliseconds wraps the labels attachment as well as calling Observe into a single method.
// Right now we attach the operation and layer digest, so it's possible to see the breakdown for latency
// by operation and individual layers.
// If you want this to be layer agnostic, just pass the digest from empty string, e.g.
// layerDigest := digest.FromString("")
func MeasureLatency(operation string, layer digest.Digest, start time.Time) {
operationLatency.WithLabelValues(operation, layer.String()).Observe(sinceInMilliseconds(start))
func MeasureLatencyInMilliseconds(operation string, layer digest.Digest, start time.Time) {
operationLatencyMilliseconds.WithLabelValues(operation, layer.String()).Observe(sinceInMilliseconds(start))
}
// MeasureLatencyInMicroseconds wraps the labels attachment as well as calling Observe into a single method.
// Right now we attach the operation and layer digest, so it's possible to see the breakdown for latency
// by operation and individual layers.
// If you want this to be layer agnostic, just pass the digest from empty string, e.g.
// layerDigest := digest.FromString("")
func MeasureLatencyInMicroseconds(operation string, layer digest.Digest, start time.Time) {
operationLatencyMicroseconds.WithLabelValues(operation, layer.String()).Observe(sinceInMicroseconds(start))
}
// IncOperationCount wraps the labels attachment as well as calling Inc into a single method.

View File

@@ -29,15 +29,18 @@ import (
"crypto/sha256"
"fmt"
"io"
"path/filepath"
"io/ioutil"
"os"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/containerd/stargz-snapshotter/cache"
"github.com/containerd/stargz-snapshotter/estargz"
"github.com/containerd/stargz-snapshotter/estargz/zstdchunked"
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
"github.com/containerd/stargz-snapshotter/metadata"
"github.com/hashicorp/go-multierror"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
@@ -47,9 +50,8 @@ import (
const maxWalkDepth = 10000
type Reader interface {
OpenFile(name string) (io.ReaderAt, error)
Lookup(name string) (*estargz.TOCEntry, bool)
Cache(opts ...CacheOption) error
OpenFile(id uint32) (io.ReaderAt, error)
Metadata() metadata.Reader
Close() error
LastOnDemandReadTime() time.Time
}
@@ -57,82 +59,263 @@ type Reader interface {
// VerifiableReader produces a Reader with a given verifier.
type VerifiableReader struct {
r *reader
lastVerifyErr atomic.Value
prohibitVerifyFailure bool
prohibitVerifyFailureMu sync.RWMutex
closed bool
closedMu sync.Mutex
verifier func(uint32, string) (digest.Verifier, error)
}
func (vr *VerifiableReader) SkipVerify() Reader {
vr.r.verifier = nopTOCEntryVerifier{}
return vr.r
}
func (vr *VerifiableReader) VerifyTOC(tocDigest digest.Digest) (Reader, error) {
v, err := vr.r.r.VerifyTOC(tocDigest)
if err != nil {
return nil, err
if vr.isClosed() {
return nil, fmt.Errorf("reader is already closed")
}
vr.r.verifier = v
vr.prohibitVerifyFailureMu.Lock()
vr.prohibitVerifyFailure = true
lastVerifyErr := vr.lastVerifyErr.Load()
vr.prohibitVerifyFailureMu.Unlock()
if err := lastVerifyErr; err != nil {
return nil, errors.Wrapf(err.(error), "content error occures during caching contents")
}
if actual := vr.r.r.TOCDigest(); actual != tocDigest {
return nil, fmt.Errorf("invalid TOC JSON %q; want %q", actual, tocDigest)
}
vr.r.verify = true
return vr.r, nil
}
func (vr *VerifiableReader) Metadata() metadata.Reader {
// TODO: this shouldn't be called before verified
return vr.r.r
}
func (vr *VerifiableReader) Cache(opts ...CacheOption) (err error) {
if vr.isClosed() {
return fmt.Errorf("reader is already closed")
}
var cacheOpts cacheOptions
for _, o := range opts {
o(&cacheOpts)
}
gr := vr.r
r := gr.r
if cacheOpts.reader != nil {
r, err = r.Clone(cacheOpts.reader)
if err != nil {
return err
}
}
rootID := r.RootID()
filter := func(int64) bool {
return true
}
if cacheOpts.filter != nil {
filter = cacheOpts.filter
}
eg, egCtx := errgroup.WithContext(context.Background())
eg.Go(func() error {
return vr.cacheWithReader(egCtx,
0, eg, semaphore.NewWeighted(int64(runtime.GOMAXPROCS(0))),
rootID, r, filter, cacheOpts.cacheOpts...)
})
return eg.Wait()
}
func (vr *VerifiableReader) cacheWithReader(ctx context.Context, currentDepth int, eg *errgroup.Group, sem *semaphore.Weighted, dirID uint32, r metadata.Reader, filter func(int64) bool, opts ...cache.Option) (rErr error) {
if currentDepth > maxWalkDepth {
return fmt.Errorf("tree is too deep (depth:%d)", currentDepth)
}
gr := vr.r
rootID := r.RootID()
r.ForeachChild(dirID, func(name string, id uint32, mode os.FileMode) bool {
e, err := r.GetAttr(id)
if err != nil {
rErr = err
return false
}
if mode.IsDir() {
// Walk through all files on this stargz file.
// Ignore the entry of "./" (formated as "" by stargz lib) on root directory
// because this points to the root directory itself.
if dirID == rootID && name == "" {
return true
}
if err := vr.cacheWithReader(ctx, currentDepth+1, eg, sem, id, r, filter, opts...); err != nil {
rErr = err
return false
}
return true
} else if !mode.IsRegular() {
// Only cache regular files
return true
} else if dirID == rootID && name == estargz.TOCTarName {
// We don't need to cache TOC json file
return true
}
offset, err := r.GetOffset(id)
if err != nil {
rErr = err
return false
}
if !filter(offset) {
// This entry need to be filtered out
return true
}
fr, err := r.OpenFile(id)
if err != nil {
rErr = err
return false
}
var nr int64
for nr < e.Size {
chunkOffset, chunkSize, chunkDigestStr, ok := fr.ChunkEntryForOffset(nr)
if !ok {
break
}
nr += chunkSize
if err := sem.Acquire(ctx, 1); err != nil {
rErr = err
return false
}
eg.Go(func() (retErr error) {
defer sem.Release(1)
defer func() {
if retErr != nil {
vr.lastVerifyErr.Store(retErr)
}
}()
// Check if the target chunks exists in the cache
cacheID := genID(id, chunkOffset, chunkSize)
if r, err := gr.cache.Get(cacheID, opts...); err == nil {
return r.Close()
}
// missed cache, needs to fetch and add it to the cache
br := bufio.NewReaderSize(io.NewSectionReader(fr, chunkOffset, chunkSize), int(chunkSize))
if _, err := br.Peek(int(chunkSize)); err != nil {
return fmt.Errorf("cacheWithReader.peek: %v", err)
}
w, err := gr.cache.Add(cacheID, opts...)
if err != nil {
return err
}
defer w.Close()
v, err := vr.verifier(id, chunkDigestStr)
if err != nil {
vr.prohibitVerifyFailureMu.RLock()
if vr.prohibitVerifyFailure {
vr.prohibitVerifyFailureMu.RUnlock()
return errors.Wrapf(err, "verifier not found %q(off:%d,size:%d)", name, chunkOffset, chunkSize)
}
vr.lastVerifyErr.Store(err)
vr.prohibitVerifyFailureMu.RUnlock()
}
tee := ioutil.Discard
if v != nil {
tee = io.Writer(v) // verification is required
}
if _, err := io.CopyN(w, io.TeeReader(br, tee), chunkSize); err != nil {
w.Abort()
return errors.Wrapf(err,
"failed to cache file payload of %q (offset:%d,size:%d)",
name, chunkOffset, chunkSize)
}
if v != nil && !v.Verified() {
err := fmt.Errorf("invalid chunk %q (offset:%d,size:%d)", name, chunkOffset, chunkSize)
vr.prohibitVerifyFailureMu.RLock()
if vr.prohibitVerifyFailure {
vr.prohibitVerifyFailureMu.RUnlock()
w.Abort()
return err
}
vr.lastVerifyErr.Store(err)
vr.prohibitVerifyFailureMu.RUnlock()
}
return w.Commit()
})
}
return true
})
return
}
func (vr *VerifiableReader) Close() error {
vr.closedMu.Lock()
defer vr.closedMu.Unlock()
if vr.closed {
return nil
}
vr.closed = true
return vr.r.Close()
}
type nopTOCEntryVerifier struct{}
func (nev nopTOCEntryVerifier) Verifier(ce *estargz.TOCEntry) (digest.Verifier, error) {
return nopVerifier{}, nil
}
type nopVerifier struct{}
func (nv nopVerifier) Write(p []byte) (n int, err error) {
return len(p), nil
}
func (nv nopVerifier) Verified() bool {
return true
func (vr *VerifiableReader) isClosed() bool {
vr.closedMu.Lock()
closed := vr.closed
vr.closedMu.Unlock()
return closed
}
// NewReader creates a Reader based on the given stargz blob and cache implementation.
// It returns VerifiableReader so the caller must provide a estargz.TOCEntryVerifier
// It returns VerifiableReader so the caller must provide a metadata.ChunkVerifier
// to use for verifying file or chunk contained in this stargz blob.
func NewReader(sr *io.SectionReader, cache cache.BlobCache, layerSha digest.Digest, telemetry *estargz.Telemetry, esgzOpts ...estargz.OpenOption) (*VerifiableReader, error) {
r, err := estargz.Open(sr, append(esgzOpts, estargz.WithTelemetry(telemetry), estargz.WithDecompressors(new(zstdchunked.Decompressor)))...)
if err != nil {
return nil, errors.Wrap(err, "failed to parse stargz")
}
func NewReader(r metadata.Reader, cache cache.BlobCache, layerSha digest.Digest) (*VerifiableReader, error) {
vr := &reader{
r: r,
sr: sr,
cache: cache,
bufPool: sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
},
layerSha: layerSha,
telemetry: telemetry,
layerSha: layerSha,
verifier: digestVerifier,
}
return &VerifiableReader{vr}, nil
return &VerifiableReader{r: vr, verifier: digestVerifier}, nil
}
type reader struct {
r *estargz.Reader
sr *io.SectionReader
cache cache.BlobCache
bufPool sync.Pool
verifier estargz.TOCEntryVerifier
layerSha digest.Digest
r metadata.Reader
cache cache.BlobCache
bufPool sync.Pool
telemetry *estargz.Telemetry
layerSha digest.Digest
lastReadTime time.Time
lastReadTimeMu sync.Mutex
closed bool
closedMu sync.Mutex
verify bool
verifier func(uint32, string) (digest.Verifier, error)
}
func (gr *reader) Metadata() metadata.Reader {
return gr.r
}
func (gr *reader) setLastReadTime(lastReadTime time.Time) {
@@ -148,82 +331,36 @@ func (gr *reader) LastOnDemandReadTime() time.Time {
return t
}
func (gr *reader) OpenFile(name string) (io.ReaderAt, error) {
func (gr *reader) OpenFile(id uint32) (io.ReaderAt, error) {
if gr.isClosed() {
return nil, fmt.Errorf("reader is already closed")
}
sr, err := gr.r.OpenFile(name)
var fr metadata.File
fr, err := gr.r.OpenFile(id)
if err != nil {
return nil, err
}
e, ok := gr.r.Lookup(name)
if !ok {
return nil, fmt.Errorf("failed to get TOCEntry %q", name)
return nil, errors.Wrapf(err, "failed to open file %d", id)
}
return &file{
name: name,
digest: e.Digest,
r: gr.r,
cache: gr.cache,
ra: sr,
gr: gr,
id: id,
fr: fr,
gr: gr,
}, nil
}
func (gr *reader) Lookup(name string) (*estargz.TOCEntry, bool) {
return gr.r.Lookup(name)
}
func (gr *reader) Cache(opts ...CacheOption) (err error) {
if gr.isClosed() {
return fmt.Errorf("reader is already closed")
}
var cacheOpts cacheOptions
for _, o := range opts {
o(&cacheOpts)
}
r := gr.r
if cacheOpts.reader != nil {
if r, err = estargz.Open(cacheOpts.reader,
// TODO: apply other options used in NewReader when needed.
estargz.WithTelemetry(gr.telemetry),
estargz.WithDecompressors(new(zstdchunked.Decompressor)),
); err != nil {
return errors.Wrap(err, "failed to parse stargz")
}
}
root, ok := r.Lookup("")
if !ok {
return fmt.Errorf("failed to get a TOCEntry of the root")
}
filter := func(*estargz.TOCEntry) bool {
return true
}
if cacheOpts.filter != nil {
filter = cacheOpts.filter
}
eg, egCtx := errgroup.WithContext(context.Background())
eg.Go(func() error {
return gr.cacheWithReader(egCtx,
0, eg, semaphore.NewWeighted(int64(runtime.GOMAXPROCS(0))),
root, r, filter, cacheOpts.cacheOpts...)
})
return eg.Wait()
}
func (gr *reader) Close() error {
func (gr *reader) Close() (retErr error) {
gr.closedMu.Lock()
defer gr.closedMu.Unlock()
if gr.closed {
return nil
}
gr.closed = true
return gr.cache.Close()
if err := gr.cache.Close(); err != nil {
retErr = multierror.Append(retErr, err)
}
if err := gr.r.Close(); err != nil {
retErr = multierror.Append(retErr, err)
}
return
}
func (gr *reader) isClosed() bool {
@@ -233,115 +370,15 @@ func (gr *reader) isClosed() bool {
return closed
}
func (gr *reader) cacheWithReader(ctx context.Context, currentDepth int, eg *errgroup.Group, sem *semaphore.Weighted, dir *estargz.TOCEntry, r *estargz.Reader, filter func(*estargz.TOCEntry) bool, opts ...cache.Option) (rErr error) {
if currentDepth > maxWalkDepth {
return fmt.Errorf("TOCEntry tree is too deep (depth:%d)", currentDepth)
}
dir.ForeachChild(func(_ string, e *estargz.TOCEntry) bool {
if e.Type == "dir" {
// Walk through all files on this stargz file.
// Ignore a TOCEntry of "./" (formated as "" by stargz lib) on root directory
// because this points to the root directory itself.
if e.Name == "" && dir.Name == "" {
return true
}
// Make sure the entry is the immediate child for avoiding loop.
if filepath.Dir(filepath.Clean(e.Name)) != filepath.Clean(dir.Name) {
rErr = fmt.Errorf("invalid child path %q; must be child of %q",
e.Name, dir.Name)
return false
}
if err := gr.cacheWithReader(ctx, currentDepth+1, eg, sem, e, r, filter, opts...); err != nil {
rErr = err
return false
}
return true
} else if e.Type != "reg" {
// Only cache regular files
return true
} else if !filter(e) {
// This entry need to be filtered out
return true
} else if e.Name == estargz.TOCTarName {
// We don't need to cache TOC json file
return true
}
sr, err := r.OpenFile(e.Name)
if err != nil {
rErr = err
return false
}
var nr int64
for nr < e.Size {
ce, ok := r.ChunkEntryForOffset(e.Name, nr)
if !ok {
break
}
nr += ce.ChunkSize
if err := sem.Acquire(ctx, 1); err != nil {
rErr = err
return false
}
eg.Go(func() (retErr error) {
defer sem.Release(1)
// Check if the target chunks exists in the cache
id := genID(e.Digest, ce.ChunkOffset, ce.ChunkSize)
if r, err := gr.cache.Get(id, opts...); err == nil {
return r.Close()
}
// missed cache, needs to fetch and add it to the cache
cr := io.NewSectionReader(sr, ce.ChunkOffset, ce.ChunkSize)
v, err := gr.verifier.Verifier(ce)
if err != nil {
return errors.Wrapf(err, "verifier not found %q(off:%d,size:%d)",
e.Name, ce.ChunkOffset, ce.ChunkSize)
}
br := bufio.NewReaderSize(io.TeeReader(cr, v), int(ce.ChunkSize))
if _, err := br.Peek(int(ce.ChunkSize)); err != nil {
return fmt.Errorf("cacheWithReader.peek: %v", err)
}
w, err := gr.cache.Add(id, opts...)
if err != nil {
return err
}
defer w.Close()
if _, err := io.CopyN(w, br, ce.ChunkSize); err != nil {
w.Abort()
return errors.Wrapf(err,
"failed to cache file payload of %q (offset:%d,size:%d)",
e.Name, ce.ChunkOffset, ce.ChunkSize)
}
if !v.Verified() {
w.Abort()
return fmt.Errorf("invalid chunk %q (offset:%d,size:%d)",
e.Name, ce.ChunkOffset, ce.ChunkSize)
}
return w.Commit()
})
}
return true
})
return
func (gr *reader) putBuffer(b *bytes.Buffer) {
b.Reset()
gr.bufPool.Put(b)
}
type file struct {
name string
digest string
ra io.ReaderAt
r *estargz.Reader
cache cache.BlobCache
gr *reader
id uint32
fr metadata.File
gr *reader
}
// ReadAt reads chunks from the stargz file with trying to fetch as many chunks
@@ -349,19 +386,19 @@ type file struct {
func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
nr := 0
for nr < len(p) {
ce, ok := sf.r.ChunkEntryForOffset(sf.name, offset+int64(nr))
chunkOffset, chunkSize, chunkDigestStr, ok := sf.fr.ChunkEntryForOffset(offset + int64(nr))
if !ok {
break
}
var (
id = genID(sf.digest, ce.ChunkOffset, ce.ChunkSize)
lowerDiscard = positive(offset - ce.ChunkOffset)
upperDiscard = positive(ce.ChunkOffset + ce.ChunkSize - (offset + int64(len(p))))
expectedSize = ce.ChunkSize - upperDiscard - lowerDiscard
id = genID(sf.id, chunkOffset, chunkSize)
lowerDiscard = positive(offset - chunkOffset)
upperDiscard = positive(chunkOffset + chunkSize - (offset + int64(len(p))))
expectedSize = chunkSize - upperDiscard - lowerDiscard
)
// Check if the content exists in the cache
if r, err := sf.cache.Get(id); err == nil {
if r, err := sf.gr.cache.Get(id); err == nil {
n, err := r.ReadAt(p[nr:int64(nr)+expectedSize], lowerDiscard)
if (err == nil || err == io.EOF) && int64(n) == expectedSize {
nr += n
@@ -376,8 +413,8 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
// reads against neighboring chunks can take the data without decmpression.
if lowerDiscard == 0 && upperDiscard == 0 {
// We can directly store the result to the given buffer
ip := p[nr : int64(nr)+ce.ChunkSize]
n, err := sf.ra.ReadAt(ip, ce.ChunkOffset)
ip := p[nr : int64(nr)+chunkSize]
n, err := sf.fr.ReadAt(ip, chunkOffset)
if err != nil && err != io.EOF {
return 0, errors.Wrap(err, "failed to read data")
}
@@ -387,12 +424,12 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
sf.gr.setLastReadTime(time.Now())
// Verify this chunk
if err := sf.verify(ip, ce); err != nil {
if err := sf.verify(sf.id, ip, chunkDigestStr); err != nil {
return 0, errors.Wrap(err, "invalid chunk")
}
// Cache this chunk
if w, err := sf.cache.Add(id); err == nil {
if w, err := sf.gr.cache.Add(id); err == nil {
if cn, err := w.Write(ip); err != nil || cn != len(ip) {
w.Abort()
} else {
@@ -407,10 +444,10 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
// Use temporally buffer for aligning this chunk
b := sf.gr.bufPool.Get().(*bytes.Buffer)
b.Reset()
b.Grow(int(ce.ChunkSize))
ip := b.Bytes()[:ce.ChunkSize]
if _, err := sf.ra.ReadAt(ip, ce.ChunkOffset); err != nil && err != io.EOF {
sf.gr.bufPool.Put(b)
b.Grow(int(chunkSize))
ip := b.Bytes()[:chunkSize]
if _, err := sf.fr.ReadAt(ip, chunkOffset); err != nil && err != io.EOF {
sf.gr.putBuffer(b)
return 0, errors.Wrap(err, "failed to read data")
}
@@ -420,13 +457,13 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
sf.gr.setLastReadTime(time.Now())
// Verify this chunk
if err := sf.verify(ip, ce); err != nil {
sf.gr.bufPool.Put(b)
if err := sf.verify(sf.id, ip, chunkDigestStr); err != nil {
sf.gr.putBuffer(b)
return 0, errors.Wrap(err, "invalid chunk")
}
// Cache this chunk
if w, err := sf.cache.Add(id); err == nil {
if w, err := sf.gr.cache.Add(id); err == nil {
if cn, err := w.Write(ip); err != nil || cn != len(ip) {
w.Abort()
} else {
@@ -434,8 +471,8 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
}
w.Close()
}
n := copy(p[nr:], ip[lowerDiscard:ce.ChunkSize-upperDiscard])
sf.gr.bufPool.Put(b)
n := copy(p[nr:], ip[lowerDiscard:chunkSize-upperDiscard])
sf.gr.putBuffer(b)
if int64(n) != expectedSize {
return 0, fmt.Errorf("unexpected final data size %d; want %d", n, expectedSize)
}
@@ -447,26 +484,26 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
return nr, nil
}
func (sf *file) verify(p []byte, ce *estargz.TOCEntry) error {
v, err := sf.gr.verifier.Verifier(ce)
func (sf *file) verify(id uint32, p []byte, chunkDigestStr string) error {
if !sf.gr.verify {
return nil // verification is not required
}
v, err := sf.gr.verifier(id, chunkDigestStr)
if err != nil {
return errors.Wrapf(err, "verifier not found %q (offset:%d,size:%d)",
ce.Name, ce.ChunkOffset, ce.ChunkSize)
return errors.Wrapf(err, "invalid chunk")
}
if _, err := v.Write(p); err != nil {
return errors.Wrapf(err, "failed to verify %q (offset:%d,size:%d)",
ce.Name, ce.ChunkOffset, ce.ChunkSize)
return errors.Wrap(err, "invalid chunk: failed to write to verifier")
}
if !v.Verified() {
return fmt.Errorf("invalid chunk %q (offset:%d,size:%d)",
ce.Name, ce.ChunkOffset, ce.ChunkSize)
return fmt.Errorf("invalid chunk: not verified")
}
return nil
}
func genID(digest string, offset, size int64) string {
sum := sha256.Sum256([]byte(fmt.Sprintf("%s-%d-%d", digest, offset, size)))
func genID(id uint32, offset, size int64) string {
sum := sha256.Sum256([]byte(fmt.Sprintf("%d-%d-%d", id, offset, size)))
return fmt.Sprintf("%x", sum)
}
@@ -481,7 +518,7 @@ type CacheOption func(*cacheOptions)
type cacheOptions struct {
cacheOpts []cache.Option
filter func(*estargz.TOCEntry) bool
filter func(int64) bool
reader *io.SectionReader
}
@@ -491,7 +528,7 @@ func WithCacheOpts(cacheOpts ...cache.Option) CacheOption {
}
}
func WithFilter(filter func(*estargz.TOCEntry) bool) CacheOption {
func WithFilter(filter func(int64) bool) CacheOption {
return func(opts *cacheOptions) {
opts.filter = filter
}
@@ -502,3 +539,11 @@ func WithReader(sr *io.SectionReader) CacheOption {
opts.reader = sr
}
}
func digestVerifier(id uint32, chunkDigestStr string) (digest.Verifier, error) {
chunkDigest, err := digest.Parse(chunkDigestStr)
if err != nil {
return nil, errors.Wrap(err, "invalid chunk: no digset is recorded")
}
return chunkDigest.Verifier(), nil
}

View File

@@ -28,6 +28,8 @@ import (
"io"
"io/ioutil"
"regexp"
"sort"
"strings"
"sync"
"time"
@@ -37,6 +39,7 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/singleflight"
)
var contentRangeRegexp = regexp.MustCompile(`bytes ([0-9]+)-([0-9]+)/([0-9]+|\\*)`)
@@ -52,7 +55,7 @@ type Blob interface {
}
type blob struct {
fetcher *fetcher
fetcher fetcher
fetcherMu sync.Mutex
size int64
@@ -64,8 +67,10 @@ type blob struct {
checkInterval time.Duration
fetchTimeout time.Duration
fetchedRegionSet regionSet
fetchedRegionSetMu sync.Mutex
fetchedRegionSet regionSet
fetchedRegionSetMu sync.Mutex
fetchedRegionGroup singleflight.Group
fetchedRegionCopyMu sync.Mutex
resolver *Resolver
@@ -73,6 +78,22 @@ type blob struct {
closedMu sync.Mutex
}
func makeBlob(fetcher fetcher, size int64, chunkSize int64, prefetchChunkSize int64,
blobCache cache.BlobCache, lastCheck time.Time, checkInterval time.Duration,
r *Resolver, fetchTimeout time.Duration) *blob {
return &blob{
fetcher: fetcher,
size: size,
chunkSize: chunkSize,
prefetchChunkSize: prefetchChunkSize,
cache: blobCache,
lastCheck: lastCheck,
checkInterval: checkInterval,
resolver: r,
fetchTimeout: fetchTimeout,
}
}
func (b *blob) Close() error {
b.closedMu.Lock()
defer b.closedMu.Unlock()
@@ -96,16 +117,17 @@ func (b *blob) Refresh(ctx context.Context, hosts source.RegistryHosts, refspec
}
// refresh the fetcher
new, newSize, err := newFetcher(ctx, hosts, refspec, desc)
f, newSize, err := b.resolver.resolveFetcher(ctx, hosts, refspec, desc)
if err != nil {
return err
} else if newSize != b.size {
}
if newSize != b.size {
return fmt.Errorf("Invalid size of new blob %d; want %d", newSize, b.size)
}
// update the blob's fetcher with new one
b.fetcherMu.Lock()
b.fetcher = new
b.fetcher = f
b.fetcherMu.Unlock()
b.lastCheckMu.Lock()
b.lastCheck = time.Now()
@@ -153,7 +175,18 @@ func (b *blob) FetchedSize() int64 {
return sz
}
func (b *blob) cacheAt(offset int64, size int64, fr *fetcher, cacheOpts *options) error {
func makeSyncKey(allData map[region]io.Writer) string {
keys := make([]string, len(allData))
keysIndex := 0
for key := range allData {
keys[keysIndex] = fmt.Sprintf("[%d,%d]", key.b, key.e)
keysIndex++
}
sort.Strings(keys)
return strings.Join(keys, ",")
}
func (b *blob) cacheAt(offset int64, size int64, fr fetcher, cacheOpts *options) error {
fetchReg := region{floor(offset, b.chunkSize), ceil(offset+size-1, b.chunkSize) - 1}
discard := make(map[region]io.Writer)
@@ -275,8 +308,9 @@ func (b *blob) ReadAt(p []byte, offset int64, opts ...Option) (int, error) {
return len(p), nil
}
// fetchRange fetches all specified chunks from local cache and remote blob.
func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
// fetchRegions fetches all specified chunks from remote blob and puts it in the local cache.
// It must be called from within fetchRange and need to ensure that it is inside the singleflight `Do` operation.
func (b *blob) fetchRegions(allData map[region]io.Writer, fetched map[region]bool, opts *options) error {
if len(allData) == 0 {
return nil
}
@@ -289,14 +323,18 @@ func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
// request missed regions
var req []region
fetched := make(map[region]bool)
for reg := range allData {
req = append(req, reg)
fetched[reg] = false
}
ctx, cancel := context.WithTimeout(context.Background(), b.fetchTimeout)
fetchCtx, cancel := context.WithTimeout(context.Background(), b.fetchTimeout)
defer cancel()
mr, err := fr.fetch(ctx, req, true, opts)
if opts.ctx != nil {
fetchCtx = opts.ctx
}
mr, err := fr.fetch(fetchCtx, req, true)
if err != nil {
return err
}
@@ -366,6 +404,64 @@ func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
return nil
}
// fetchRange fetches all specified chunks from local cache and remote blob.
func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
if len(allData) == 0 {
return nil
}
// We build a key based on regions we need to fetch and pass it to singleflightGroup.Do(...)
// to block simultaneous same requests. Once the request is finished and the data is ready,
// all blocked callers will be unblocked and that same data will be returned by all blocked callers.
key := makeSyncKey(allData)
fetched := make(map[region]bool)
_, err, shared := b.fetchedRegionGroup.Do(key, func() (interface{}, error) {
return nil, b.fetchRegions(allData, fetched, opts)
})
// When unblocked try to read from cache in case if there were no errors
// If we fail reading from cache, fetch from remote registry again
if err == nil && shared {
for reg := range allData {
if _, ok := fetched[reg]; ok {
continue
}
err = b.walkChunks(reg, func(chunk region) error {
b.fetcherMu.Lock()
fr := b.fetcher
b.fetcherMu.Unlock()
// Check if the content exists in the cache
// And if exists, read from cache
r, err := b.cache.Get(fr.genID(chunk), opts.cacheOpts...)
if err != nil {
return err
}
defer r.Close()
rr := io.NewSectionReader(r, 0, chunk.size())
// Copy the target chunk
b.fetchedRegionCopyMu.Lock()
defer b.fetchedRegionCopyMu.Unlock()
if _, err := io.CopyN(allData[chunk], rr, chunk.size()); err != nil {
return err
}
return nil
})
if err != nil {
break
}
}
// if we cannot read the data from cache, do fetch again
if err != nil {
return b.fetchRange(allData, opts)
}
}
return err
}
type walkFunc func(reg region) error
// walkChunks walks chunks from begin to end in order in the specified region.

View File

@@ -28,6 +28,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math/rand"
"mime"
"mime/multipart"
"net/http"
@@ -46,18 +47,25 @@ import (
"github.com/containerd/stargz-snapshotter/fs/config"
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
"github.com/containerd/stargz-snapshotter/fs/source"
"github.com/hashicorp/go-multierror"
rhttp "github.com/hashicorp/go-retryablehttp"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
defaultChunkSize = 50000
defaultValidIntervalSec = 60
defaultFetchTimeoutSec = 300
defaultMaxRetries = 5
defaultMinWaitMSec = 30
defaultMaxWaitMSec = 300000
)
func NewResolver(cfg config.BlobConfig) *Resolver {
func NewResolver(cfg config.BlobConfig, handlers map[string]Handler) *Resolver {
if cfg.ChunkSize == 0 { // zero means "use default chunk size"
cfg.ChunkSize = defaultChunkSize
}
@@ -70,48 +78,131 @@ func NewResolver(cfg config.BlobConfig) *Resolver {
if cfg.FetchTimeoutSec == 0 {
cfg.FetchTimeoutSec = defaultFetchTimeoutSec
}
if cfg.MaxRetries == 0 {
cfg.MaxRetries = defaultMaxRetries
}
if cfg.MinWaitMSec == 0 {
cfg.MinWaitMSec = defaultMinWaitMSec
}
if cfg.MaxWaitMSec == 0 {
cfg.MaxWaitMSec = defaultMaxWaitMSec
}
return &Resolver{
blobConfig: cfg,
handlers: handlers,
}
}
type Resolver struct {
blobConfig config.BlobConfig
handlers map[string]Handler
}
type fetcher interface {
fetch(ctx context.Context, rs []region, retry bool) (multipartReadCloser, error)
check() error
genID(reg region) string
}
func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor, blobCache cache.BlobCache) (Blob, error) {
fetcher, size, err := newFetcher(ctx, hosts, refspec, desc)
f, size, err := r.resolveFetcher(ctx, hosts, refspec, desc)
if err != nil {
return nil, err
}
if r.blobConfig.ForceSingleRangeMode {
fetcher.singleRangeMode()
}
return &blob{
fetcher: fetcher,
size: size,
chunkSize: r.blobConfig.ChunkSize,
prefetchChunkSize: r.blobConfig.PrefetchChunkSize,
cache: blobCache,
lastCheck: time.Now(),
checkInterval: time.Duration(r.blobConfig.ValidInterval) * time.Second,
resolver: r,
fetchTimeout: time.Duration(r.blobConfig.FetchTimeoutSec) * time.Second,
}, nil
blobConfig := &r.blobConfig
return makeBlob(f,
size,
blobConfig.ChunkSize,
blobConfig.PrefetchChunkSize,
blobCache,
time.Now(),
time.Duration(blobConfig.ValidInterval)*time.Second,
r,
time.Duration(blobConfig.FetchTimeoutSec)*time.Second), nil
}
func newFetcher(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) (*fetcher, int64, error) {
reghosts, err := hosts(refspec)
func (r *Resolver) resolveFetcher(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) (f fetcher, size int64, err error) {
blobConfig := &r.blobConfig
fc := &fetcherConfig{
hosts: hosts,
refspec: refspec,
desc: desc,
maxRetries: blobConfig.MaxRetries,
minWaitMSec: time.Duration(blobConfig.MinWaitMSec) * time.Millisecond,
maxWaitMSec: time.Duration(blobConfig.MaxWaitMSec) * time.Millisecond,
}
var handlersErr error
for name, p := range r.handlers {
// TODO: allow to configure the selection of readers based on the hostname in refspec
r, size, err := p.Handle(ctx, desc)
if err != nil {
handlersErr = multierror.Append(handlersErr, err)
continue
}
log.G(ctx).WithField("handler name", name).WithField("ref", refspec.String).WithField("digest", desc.Digest).
Debugf("contents is provided by a handler")
return &remoteFetcher{r}, size, nil
}
log.G(ctx).WithError(handlersErr).WithField("ref", refspec.String).WithField("digest", desc.Digest).Debugf("using default handler")
hf, size, err := newHTTPFetcher(ctx, fc)
if err != nil {
return nil, 0, err
}
if blobConfig.ForceSingleRangeMode {
hf.singleRangeMode()
}
return hf, size, err
}
type fetcherConfig struct {
hosts source.RegistryHosts
refspec reference.Spec
desc ocispec.Descriptor
maxRetries int
minWaitMSec time.Duration
maxWaitMSec time.Duration
}
func jitter(duration time.Duration) time.Duration {
return time.Duration(rand.Int63n(int64(duration)) + int64(duration))
}
// backoffStrategy extends retryablehttp's DefaultBackoff to add a random jitter to avoid overwhelming the repository
// when it comes back online
// DefaultBackoff either tries to parse the 'Retry-After' header of the response; or, it uses an exponential backoff
// 2 ^ numAttempts, limited by max
func backoffStrategy(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
delayTime := rhttp.DefaultBackoff(min, max, attemptNum, resp)
return jitter(delayTime)
}
// retryStrategy extends retryablehttp's DefaultRetryPolicy to log the error and response when retrying
// DefaultRetryPolicy retries whenever err is non-nil (except for some url errors) or if returned
// status code is 429 or 5xx (except 501)
func retryStrategy(ctx context.Context, resp *http.Response, err error) (bool, error) {
retry, err2 := rhttp.DefaultRetryPolicy(ctx, resp, err)
if retry {
log.G(ctx).WithFields(logrus.Fields{
"error": err,
"response": resp,
}).Infof("Retrying request")
}
return retry, err2
}
func newHTTPFetcher(ctx context.Context, fc *fetcherConfig) (*httpFetcher, int64, error) {
reghosts, err := fc.hosts(fc.refspec)
if err != nil {
return nil, 0, err
}
desc := fc.desc
if desc.Digest.String() == "" {
return nil, 0, fmt.Errorf("Digest is mandatory in layer descriptor")
}
digest := desc.Digest
pullScope, err := repositoryScope(refspec, false)
pullScope, err := repositoryScope(fc.refspec, false)
if err != nil {
return nil, 0, err
}
@@ -121,13 +212,22 @@ func newFetcher(ctx context.Context, hosts source.RegistryHosts, refspec referen
for _, host := range reghosts {
if host.Host == "" || strings.Contains(host.Host, "/") {
rErr = errors.Wrapf(rErr, "invalid destination (host %q, ref:%q, digest:%q)",
host.Host, refspec, digest)
host.Host, fc.refspec, digest)
continue // Try another
}
// Prepare transport with authorization functionality
tr := host.Client.Transport
if rt, ok := tr.(*rhttp.RoundTripper); ok {
rt.Client.RetryMax = fc.maxRetries
rt.Client.RetryWaitMin = fc.minWaitMSec
rt.Client.RetryWaitMax = fc.maxWaitMSec
rt.Client.Backoff = backoffStrategy
rt.Client.CheckRetry = retryStrategy
}
timeout := host.Client.Timeout
if host.Authorizer != nil {
tr = &transport{
@@ -141,12 +241,12 @@ func newFetcher(ctx context.Context, hosts source.RegistryHosts, refspec referen
blobURL := fmt.Sprintf("%s://%s/%s/blobs/%s",
host.Scheme,
path.Join(host.Host, host.Path),
strings.TrimPrefix(refspec.Locator, refspec.Hostname()+"/"),
strings.TrimPrefix(fc.refspec.Locator, fc.refspec.Hostname()+"/"),
digest)
url, err := redirect(ctx, blobURL, tr, timeout)
if err != nil {
rErr = errors.Wrapf(rErr, "failed to redirect (host %q, ref:%q, digest:%q): %v",
host.Host, refspec, digest, err)
host.Host, fc.refspec, digest, err)
continue // Try another
}
@@ -154,15 +254,15 @@ func newFetcher(ctx context.Context, hosts source.RegistryHosts, refspec referen
// TODO: we should try to use the Size field in the descriptor here.
start := time.Now() // start time before getting layer header
size, err := getSize(ctx, url, tr, timeout)
commonmetrics.MeasureLatency(commonmetrics.StargzHeaderGet, digest, start) // time to get layer header
commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.StargzHeaderGet, digest, start) // time to get layer header
if err != nil {
rErr = errors.Wrapf(rErr, "failed to get size (host %q, ref:%q, digest:%q): %v",
host.Host, refspec, digest, err)
host.Host, fc.refspec, digest, err)
continue // Try another
}
// Hit one destination
return &fetcher{
return &httpFetcher{
url: url,
tr: tr,
blobURL: blobURL,
@@ -302,7 +402,7 @@ func getSize(ctx context.Context, url string, tr http.RoundTripper, timeout time
headStatusCode, res.StatusCode)
}
type fetcher struct {
type httpFetcher struct {
url string
urlMu sync.Mutex
tr http.RoundTripper
@@ -318,7 +418,7 @@ type multipartReadCloser interface {
Close() error
}
func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *options) (multipartReadCloser, error) {
func (f *httpFetcher) fetch(ctx context.Context, rs []region, retry bool) (multipartReadCloser, error) {
if len(rs) == 0 {
return nil, fmt.Errorf("no request queried")
}
@@ -328,13 +428,6 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
singleRangeMode = f.isSingleRangeMode()
)
if opts.ctx != nil {
ctx = opts.ctx
}
if opts.tr != nil {
tr = opts.tr
}
// squash requesting chunks for reducing the total size of request header
// (servers generally have limits for the size of headers)
// TODO: when our request has too many ranges, we need to divide it into
@@ -368,7 +461,7 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
// Recording the roundtrip latency for remote registry GET operation.
start := time.Now()
res, err := tr.RoundTrip(req) // NOT DefaultClient; don't want redirects
commonmetrics.MeasureLatency(commonmetrics.RemoteRegistryGet, f.digest, start)
commonmetrics.MeasureLatencyInMilliseconds(commonmetrics.RemoteRegistryGet, f.digest, start)
if err != nil {
return nil, err
}
@@ -378,7 +471,7 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
if err != nil {
return nil, errors.Wrapf(err, "failed to parse Content-Length")
}
return singlePartReader(region{0, size - 1}, res.Body), nil
return newSinglePartReader(region{0, size - 1}, res.Body), nil
} else if res.StatusCode == http.StatusPartialContent {
mediaType, params, err := mime.ParseMediaType(res.Header.Get("Content-Type"))
if err != nil {
@@ -386,7 +479,7 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
}
if strings.HasPrefix(mediaType, "multipart/") {
// We are getting a set of chunks as a multipart body.
return multiPartReader(res.Body, params["boundary"]), nil
return newMultiPartReader(res.Body, params["boundary"]), nil
}
// We are getting single range
@@ -394,7 +487,7 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
if err != nil {
return nil, errors.Wrapf(err, "failed to parse Content-Range")
}
return singlePartReader(reg, res.Body), nil
return newSinglePartReader(reg, res.Body), nil
} else if retry && res.StatusCode == http.StatusForbidden {
log.G(ctx).Infof("Received status code: %v. Refreshing URL and retrying...", res.Status)
@@ -402,19 +495,19 @@ func (f *fetcher) fetch(ctx context.Context, rs []region, retry bool, opts *opti
if err := f.refreshURL(ctx); err != nil {
return nil, errors.Wrapf(err, "failed to refresh URL on %v", res.Status)
}
return f.fetch(ctx, rs, false, opts)
return f.fetch(ctx, rs, false)
} else if retry && res.StatusCode == http.StatusBadRequest && !singleRangeMode {
log.G(ctx).Infof("Received status code: %v. Setting single range mode and retrying...", res.Status)
// gcr.io (https://storage.googleapis.com) returns 400 on multi-range request (2020 #81)
f.singleRangeMode() // fallbacks to singe range request mode
return f.fetch(ctx, rs, false, opts) // retries with the single range mode
f.singleRangeMode() // fallbacks to singe range request mode
return f.fetch(ctx, rs, false) // retries with the single range mode
}
return nil, fmt.Errorf("unexpected status code: %v", res.Status)
}
func (f *fetcher) check() error {
func (f *httpFetcher) check() error {
ctx := context.Background()
if f.timeout > 0 {
var cancel context.CancelFunc
@@ -457,7 +550,7 @@ func (f *fetcher) check() error {
return fmt.Errorf("unexpected status code %v", res.StatusCode)
}
func (f *fetcher) refreshURL(ctx context.Context) error {
func (f *httpFetcher) refreshURL(ctx context.Context) error {
newURL, err := redirect(ctx, f.blobURL, f.tr, f.timeout)
if err != nil {
return err
@@ -468,25 +561,25 @@ func (f *fetcher) refreshURL(ctx context.Context) error {
return nil
}
func (f *fetcher) genID(reg region) string {
func (f *httpFetcher) genID(reg region) string {
sum := sha256.Sum256([]byte(fmt.Sprintf("%s-%d-%d", f.blobURL, reg.b, reg.e)))
return fmt.Sprintf("%x", sum)
}
func (f *fetcher) singleRangeMode() {
func (f *httpFetcher) singleRangeMode() {
f.singleRangeMu.Lock()
f.singleRange = true
f.singleRangeMu.Unlock()
}
func (f *fetcher) isSingleRangeMode() bool {
func (f *httpFetcher) isSingleRangeMode() bool {
f.singleRangeMu.Lock()
r := f.singleRange
f.singleRangeMu.Unlock()
return r
}
func singlePartReader(reg region, rc io.ReadCloser) multipartReadCloser {
func newSinglePartReader(reg region, rc io.ReadCloser) multipartReadCloser {
return &singlepartReader{
r: rc,
Closer: rc,
@@ -509,7 +602,7 @@ func (sr *singlepartReader) Next() (region, io.Reader, error) {
return region{}, nil, io.EOF
}
func multiPartReader(rc io.ReadCloser, boundary string) multipartReadCloser {
func newMultiPartReader(rc io.ReadCloser, boundary string) multipartReadCloser {
return &multipartReader{
m: multipart.NewReader(rc, boundary),
Closer: rc,
@@ -558,7 +651,6 @@ type Option func(*options)
type options struct {
ctx context.Context
tr http.RoundTripper
cacheOpts []cache.Option
}
@@ -568,12 +660,6 @@ func WithContext(ctx context.Context) Option {
}
}
func WithRoundTripper(tr http.RoundTripper) Option {
return func(opts *options) {
opts.tr = tr
}
}
func WithCacheOpts(cacheOpts ...cache.Option) Option {
return func(opts *options) {
opts.cacheOpts = cacheOpts
@@ -597,3 +683,38 @@ func repositoryScope(refspec reference.Spec, push bool) (string, error) {
}
return s, nil
}
type remoteFetcher struct {
r Fetcher
}
func (r *remoteFetcher) fetch(ctx context.Context, rs []region, retry bool) (multipartReadCloser, error) {
var s regionSet
for _, reg := range rs {
s.add(reg)
}
reg := superRegion(s.rs)
rc, err := r.r.Fetch(ctx, reg.b, reg.size())
if err != nil {
return nil, err
}
return newSinglePartReader(reg, rc), nil
}
func (r *remoteFetcher) check() error {
return r.r.Check()
}
func (r *remoteFetcher) genID(reg region) string {
return r.r.GenID(reg.b, reg.size())
}
type Handler interface {
Handle(ctx context.Context, desc ocispec.Descriptor) (fetcher Fetcher, size int64, err error)
}
type Fetcher interface {
Fetch(ctx context.Context, off int64, size int64) (io.ReadCloser, error)
Check() error
GenID(off int64, size int64) string
}

View File

@@ -70,6 +70,14 @@ const (
// targetImageLayersLabel is a label which contains layer digests contained in
// the target image.
targetImageLayersLabel = "containerd.io/snapshot/remote/stargz.layers"
// targetImageURLsLabelPrefix is a label prefix which constructs a map from the layer index to
// urls of the layer descriptor.
targetImageURLsLabelPrefix = "containerd.io/snapshot/remote/urls."
// targetURsLLabel is a label which contains layer URL. This is only used to pass URL from containerd
// to snapshotter.
targetURLsLabel = "containerd.io/snapshot/remote/urls"
)
// FromDefaultLabels returns a function for converting snapshot labels to
@@ -94,31 +102,38 @@ func FromDefaultLabels(hosts RegistryHosts) GetSources {
return nil, err
}
var layersDgst []digest.Digest
var neighboringLayers []ocispec.Descriptor
if l, ok := labels[targetImageLayersLabel]; ok {
layersStr := strings.Split(l, ",")
for _, l := range layersStr {
for i, l := range layersStr {
d, err := digest.Parse(l)
if err != nil {
return nil, err
}
if d.String() != target.String() {
layersDgst = append(layersDgst, d)
desc := ocispec.Descriptor{Digest: d}
if urls, ok := labels[targetImageURLsLabelPrefix+fmt.Sprintf("%d", i)]; ok {
desc.URLs = strings.Split(urls, ",")
}
neighboringLayers = append(neighboringLayers, desc)
}
}
}
var layers []ocispec.Descriptor
for _, dgst := range append([]digest.Digest{target}, layersDgst...) {
layers = append(layers, ocispec.Descriptor{Digest: dgst})
targetDesc := ocispec.Descriptor{
Digest: target,
Annotations: labels,
}
if targetURLs, ok := labels[targetURLsLabel]; ok {
targetDesc.URLs = append(targetDesc.URLs, strings.Split(targetURLs, ",")...)
}
return []Source{
{
Hosts: hosts,
Name: refspec,
Target: ocispec.Descriptor{Digest: target},
Manifest: ocispec.Manifest{Layers: layers},
Target: targetDesc,
Manifest: ocispec.Manifest{Layers: append([]ocispec.Descriptor{targetDesc}, neighboringLayers...)},
},
}, nil
}
@@ -146,7 +161,7 @@ func AppendDefaultLabelsHandlerWrapper(ref string, prefetchSize int64) func(f im
c.Annotations[targetRefLabel] = ref
c.Annotations[targetDigestLabel] = c.Digest.String()
var layers string
for _, l := range children[i:] {
for i, l := range children[i:] {
if images.IsLayerType(l.MediaType) {
ls := fmt.Sprintf("%s,", l.Digest.String())
// This avoids the label hits the size limitation.
@@ -155,10 +170,17 @@ func AppendDefaultLabelsHandlerWrapper(ref string, prefetchSize int64) func(f im
break
}
layers += ls
// Store URLs of the neighbouring layer as well.
urlsKey := targetImageURLsLabelPrefix + fmt.Sprintf("%d", i)
c.Annotations[urlsKey] = appendWithValidation(urlsKey, l.URLs)
}
}
c.Annotations[targetImageLayersLabel] = strings.TrimSuffix(layers, ",")
c.Annotations[config.TargetPrefetchSizeLabel] = fmt.Sprintf("%d", prefetchSize)
// store URL in annotation to let containerd to pass it to the snapshotter
c.Annotations[targetURLsLabel] = appendWithValidation(targetURLsLabel, c.URLs)
}
}
}
@@ -166,3 +188,15 @@ func AppendDefaultLabelsHandlerWrapper(ref string, prefetchSize int64) func(f im
})
}
}
func appendWithValidation(key string, values []string) string {
var v string
for _, u := range values {
s := fmt.Sprintf("%s,", u)
if err := labels.Validate(key, v+s); err != nil {
break
}
v += s
}
return strings.TrimSuffix(v, ",")
}

View File

@@ -0,0 +1,476 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package db
import (
"encoding/binary"
"fmt"
"os"
"sort"
"github.com/containerd/stargz-snapshotter/metadata"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
// Metadata package stores filesystem metadata in the following schema.
//
// - filesystems
// - *filesystem id* : bucket for each filesystem keyed by a unique string.
// - nodes
// - *node id* : bucket for each node keyed by a uniqe uint64.
// - size : <varint> : size of the regular node.
// - modtime : <varint> : modification time of the node.
// - linkName : <string> : link target of symlink
// - mode : <uvarint> : permission and mode bits (os.FileMode).
// - uid : <varint> : uid of the owner.
// - gid : <varint> : gid of the owner.
// - devMajor : <varint> : the major device number for device
// - devMinor : <varint> : the minor device number for device
// - xattrKey : <string> : key of the first extended attribute.
// - xattrValue : <string> : value of the first extended attribute
// - xattrsExtra : 2nd and the following extended attribute.
// - *key* : <string> : map of key to value string
// - numLink : <varint> : the number of links pointing to this node.
// - metadata
// - *node id* : bucket for each node keyed by a uniqe uint64.
// - childName : <string> : base name of the first child
// - childID : <node id> : id of the first child
// - childrenExtra : 2nd and following child nodes of directory.
// - *basename* : <node id> : map of basename string to the child node id
// - chunk : <encoded> : information of the first chunkn
// - chunksExtra : 2nd and following chunks (this is rarely used so we can avoid the cost of creating the bucket)
// - *offset* : <encoded> : keyed by gzip header offset (varint) in the estargz file to the chunk.
// - nextOffset : <varint> : the offset of the next node with a non-zero offset.
var (
bucketKeyFilesystems = []byte("filesystems")
bucketKeyNodes = []byte("nodes")
bucketKeySize = []byte("size")
bucketKeyModTime = []byte("modtime")
bucketKeyLinkName = []byte("linkName")
bucketKeyMode = []byte("mode")
bucketKeyUID = []byte("uid")
bucketKeyGID = []byte("gid")
bucketKeyDevMajor = []byte("devMajor")
bucketKeyDevMinor = []byte("devMinor")
bucketKeyXattrKey = []byte("xattrKey")
bucketKeyXattrValue = []byte("xattrValue")
bucketKeyXattrsExtra = []byte("xattrsExtra")
bucketKeyNumLink = []byte("numLink")
bucketKeyMetadata = []byte("metadata")
bucketKeyChildName = []byte("childName")
bucketKeyChildID = []byte("childID")
bucketKeyChildrenExtra = []byte("childrenExtra")
bucketKeyChunk = []byte("chunk")
bucketKeyChunksExtra = []byte("chunksExtra")
bucketKeyNextOffset = []byte("nextOffset")
)
type childEntry struct {
base string
id uint32
}
type chunkEntry struct {
offset int64
chunkOffset int64
chunkSize int64
chunkDigest string
}
type metadataEntry struct {
children map[string]childEntry
chunks []chunkEntry
nextOffset int64
}
func getNodes(tx *bolt.Tx, fsID string) (*bolt.Bucket, error) {
filesystems := tx.Bucket(bucketKeyFilesystems)
if filesystems == nil {
return nil, fmt.Errorf("fs %q not found: no fs is registered", fsID)
}
lbkt := filesystems.Bucket([]byte(fsID))
if lbkt == nil {
return nil, fmt.Errorf("fs bucket for %q not found", fsID)
}
nodes := lbkt.Bucket(bucketKeyNodes)
if nodes == nil {
return nil, fmt.Errorf("nodes bucket for %q not found", fsID)
}
return nodes, nil
}
func getMetadata(tx *bolt.Tx, fsID string) (*bolt.Bucket, error) {
filesystems := tx.Bucket(bucketKeyFilesystems)
if filesystems == nil {
return nil, fmt.Errorf("fs %q not found: no fs is registered", fsID)
}
lbkt := filesystems.Bucket([]byte(fsID))
if lbkt == nil {
return nil, fmt.Errorf("fs bucket for %q not found", fsID)
}
md := lbkt.Bucket(bucketKeyMetadata)
if md == nil {
return nil, fmt.Errorf("metadata bucket for fs %q not found", fsID)
}
return md, nil
}
func getNodeBucketByID(nodes *bolt.Bucket, id uint32) (*bolt.Bucket, error) {
b := nodes.Bucket(encodeID(id))
if b == nil {
return nil, fmt.Errorf("node bucket for %d not found", id)
}
return b, nil
}
func getMetadataBucketByID(md *bolt.Bucket, id uint32) (*bolt.Bucket, error) {
b := md.Bucket(encodeID(id))
if b == nil {
return nil, fmt.Errorf("metadata bucket for %d not found", id)
}
return b, nil
}
func writeAttr(b *bolt.Bucket, attr *metadata.Attr) error {
for _, v := range []struct {
key []byte
val int64
}{
{bucketKeySize, attr.Size},
{bucketKeyUID, int64(attr.UID)},
{bucketKeyGID, int64(attr.GID)},
{bucketKeyDevMajor, int64(attr.DevMajor)},
{bucketKeyDevMinor, int64(attr.DevMinor)},
{bucketKeyNumLink, int64(attr.NumLink - 1)}, // numLink = 0 means num link = 1 in DB
} {
if v.val != 0 {
val, err := encodeInt(v.val)
if err != nil {
return err
}
if err := b.Put(v.key, val); err != nil {
return err
}
}
}
if !attr.ModTime.IsZero() {
te, err := attr.ModTime.GobEncode()
if err != nil {
return err
}
if err := b.Put(bucketKeyModTime, te); err != nil {
return err
}
}
if len(attr.LinkName) > 0 {
if err := b.Put(bucketKeyLinkName, []byte(attr.LinkName)); err != nil {
return err
}
}
if attr.Mode != 0 {
val, err := encodeUint(uint64(attr.Mode))
if err != nil {
return err
}
if err := b.Put(bucketKeyMode, val); err != nil {
return err
}
}
if len(attr.Xattrs) > 0 {
var firstK string
var firstV []byte
for k, v := range attr.Xattrs {
firstK, firstV = k, v
break
}
if err := b.Put(bucketKeyXattrKey, []byte(firstK)); err != nil {
return err
}
if err := b.Put(bucketKeyXattrValue, firstV); err != nil {
return err
}
var xbkt *bolt.Bucket
for k, v := range attr.Xattrs {
if k == firstK || len(v) == 0 {
continue
}
if xbkt == nil {
if xbkt := b.Bucket(bucketKeyXattrsExtra); xbkt != nil {
// Reset
if err := b.DeleteBucket(bucketKeyXattrsExtra); err != nil {
return err
}
}
var err error
xbkt, err = b.CreateBucket(bucketKeyXattrsExtra)
if err != nil {
return err
}
}
if err := xbkt.Put([]byte(k), v); err != nil {
return errors.Wrapf(err, "failed to set xattr %q=%q", k, string(v))
}
}
}
return nil
}
func readAttr(b *bolt.Bucket, attr *metadata.Attr) error {
return b.ForEach(func(k, v []byte) error {
switch string(k) {
case string(bucketKeySize):
attr.Size, _ = binary.Varint(v)
case string(bucketKeyModTime):
if err := (&attr.ModTime).GobDecode(v); err != nil {
return err
}
case string(bucketKeyLinkName):
attr.LinkName = string(v)
case string(bucketKeyMode):
mode, _ := binary.Uvarint(v)
attr.Mode = os.FileMode(uint32(mode))
case string(bucketKeyUID):
i, _ := binary.Varint(v)
attr.UID = int(i)
case string(bucketKeyGID):
i, _ := binary.Varint(v)
attr.GID = int(i)
case string(bucketKeyDevMajor):
i, _ := binary.Varint(v)
attr.DevMajor = int(i)
case string(bucketKeyDevMinor):
i, _ := binary.Varint(v)
attr.DevMinor = int(i)
case string(bucketKeyNumLink):
i, _ := binary.Varint(v)
attr.NumLink = int(i) + 1 // numLink = 0 means num link = 1 in DB
case string(bucketKeyXattrKey):
if attr.Xattrs == nil {
attr.Xattrs = make(map[string][]byte)
}
attr.Xattrs[string(v)] = b.Get(bucketKeyXattrValue)
case string(bucketKeyXattrsExtra):
if err := b.Bucket(k).ForEach(func(k, v []byte) error {
if attr.Xattrs == nil {
attr.Xattrs = make(map[string][]byte)
}
attr.Xattrs[string(k)] = v
return nil
}); err != nil {
return err
}
}
return nil
})
}
func readNumLink(b *bolt.Bucket) int {
// numLink = 0 means num link = 1 in BD
numLink, _ := binary.Varint(b.Get(bucketKeyNumLink))
return int(numLink) + 1
}
func readChunks(b *bolt.Bucket, size int64) (chunks []chunkEntry, err error) {
if chunk := b.Get(bucketKeyChunk); len(chunk) > 0 {
e, err := decodeChunkEntry(chunk)
if err != nil {
return nil, err
}
chunks = append(chunks, e)
}
if chbkt := b.Bucket(bucketKeyChunksExtra); chbkt != nil {
if err := chbkt.ForEach(func(_, v []byte) error {
e, err := decodeChunkEntry(v)
if err != nil {
return err
}
chunks = append(chunks, e)
return nil
}); err != nil {
return nil, err
}
sort.Slice(chunks, func(i, j int) bool {
return chunks[i].chunkOffset < chunks[j].chunkOffset
})
}
nextOffset := size
for i := len(chunks) - 1; i >= 0; i-- {
chunks[i].chunkSize = nextOffset - chunks[i].chunkOffset
nextOffset = chunks[i].chunkOffset
}
return
}
func readChild(md *bolt.Bucket, base string) (uint32, error) {
if base == string(md.Get(bucketKeyChildName)) {
return decodeID(md.Get(bucketKeyChildID)), nil
}
cbkt := md.Bucket(bucketKeyChildrenExtra)
if cbkt == nil {
return 0, fmt.Errorf("extra children not found")
}
eid := cbkt.Get([]byte(base))
if len(eid) == 0 {
return 0, fmt.Errorf("children %q not found", base)
}
return decodeID(eid), nil
}
func writeMetadataEntry(md *bolt.Bucket, m *metadataEntry) error {
if len(m.children) > 0 {
var firstChildName string
var firstChild childEntry
for name, child := range m.children {
firstChildName, firstChild = name, child
break
}
if err := md.Put(bucketKeyChildID, encodeID(firstChild.id)); err != nil {
return errors.Wrapf(err, "failed to put id of first child %q", firstChildName)
}
if err := md.Put(bucketKeyChildName, []byte(firstChildName)); err != nil {
return errors.Wrapf(err, "failed to put name first child %q", firstChildName)
}
if len(m.children) > 1 {
var cbkt *bolt.Bucket
for k, c := range m.children {
if k == firstChildName {
continue
}
if cbkt == nil {
if cbkt := md.Bucket(bucketKeyChildrenExtra); cbkt != nil {
// Reset
if err := md.DeleteBucket(bucketKeyChildrenExtra); err != nil {
return err
}
}
var err error
cbkt, err = md.CreateBucket(bucketKeyChildrenExtra)
if err != nil {
return err
}
}
if err := cbkt.Put([]byte(c.base), encodeID(c.id)); err != nil {
return errors.Wrapf(err, "failed to add child ID %q", c.id)
}
}
}
}
if len(m.chunks) > 0 {
first := m.chunks[0]
if err := md.Put(bucketKeyChunk, encodeChunkEntry(first)); err != nil {
return errors.Wrapf(err, "failed to set chunk %q", first.offset)
}
var cbkt *bolt.Bucket
for _, e := range m.chunks[1:] {
if cbkt == nil {
if cbkt := md.Bucket(bucketKeyChunksExtra); cbkt != nil {
// Reset
if err := md.DeleteBucket(bucketKeyChunksExtra); err != nil {
return err
}
}
var err error
cbkt, err = md.CreateBucket(bucketKeyChunksExtra)
if err != nil {
return err
}
}
eoff, err := encodeInt(e.offset)
if err != nil {
return err
}
if err := cbkt.Put(eoff, encodeChunkEntry(e)); err != nil {
return err
}
}
}
if m.nextOffset > 0 {
if err := putInt(md, bucketKeyNextOffset, m.nextOffset); err != nil {
return errors.Wrapf(err, "failed to set next offset value %d", m.nextOffset)
}
}
return nil
}
func encodeChunkEntry(e chunkEntry) []byte {
eb := make([]byte, 16+len([]byte(e.chunkDigest)))
binary.BigEndian.PutUint64(eb[0:8], uint64(e.chunkOffset))
binary.BigEndian.PutUint64(eb[8:16], uint64(e.offset))
copy(eb[16:], []byte(e.chunkDigest))
return eb
}
func decodeChunkEntry(d []byte) (e chunkEntry, _ error) {
if len(d) < 16 {
return e, fmt.Errorf("mulformed chunk entry (len:%d)", len(d))
}
e.chunkOffset = int64(binary.BigEndian.Uint64(d[0:8]))
e.offset = int64(binary.BigEndian.Uint64(d[8:16]))
if len(d) > 16 {
e.chunkDigest = string(d[16:])
}
return e, nil
}
func putInt(b *bolt.Bucket, k []byte, v int64) error {
i, err := encodeInt(v)
if err != nil {
return err
}
return b.Put(k, i)
}
func encodeID(id uint32) []byte {
b := [4]byte{}
binary.BigEndian.PutUint32(b[:], id)
return b[:]
}
func decodeID(b []byte) uint32 {
return binary.BigEndian.Uint32(b)
}
func encodeInt(i int64) ([]byte, error) {
var (
buf [binary.MaxVarintLen64]byte
iEncoded = buf[:]
)
iEncoded = iEncoded[:binary.PutVarint(iEncoded, i)]
if len(iEncoded) == 0 {
return nil, fmt.Errorf("failed encoding integer = %v", i)
}
return iEncoded, nil
}
func encodeUint(i uint64) ([]byte, error) {
var (
buf [binary.MaxVarintLen64]byte
iEncoded = buf[:]
)
iEncoded = iEncoded[:binary.PutUvarint(iEncoded, i)]
if len(iEncoded) == 0 {
return nil, fmt.Errorf("failed encoding integer = %v", i)
}
return iEncoded, nil
}

View File

@@ -0,0 +1,985 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package db
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path"
"path/filepath"
"sort"
"strings"
"sync"
"time"
"github.com/containerd/stargz-snapshotter/estargz"
"github.com/containerd/stargz-snapshotter/metadata"
"github.com/goccy/go-json"
"github.com/hashicorp/go-multierror"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/rs/xid"
bolt "go.etcd.io/bbolt"
"golang.org/x/sync/errgroup"
)
// reader stores filesystem metadata parsed from eStargz to metadata DB
// and provides methods to read them.
type reader struct {
db *bolt.DB
fsID string
rootID uint32
tocDigest digest.Digest
sr *io.SectionReader
curID uint32
curIDMu sync.Mutex
initG *errgroup.Group
decompressor metadata.Decompressor
}
func (r *reader) nextID() (uint32, error) {
r.curIDMu.Lock()
defer r.curIDMu.Unlock()
if r.curID == math.MaxUint32 {
return 0, fmt.Errorf("sequence id too large")
}
r.curID++
return r.curID, nil
}
// NewReader parses an eStargz and stores filesystem metadata to
// the provided DB.
func NewReader(db *bolt.DB, sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error) {
var rOpts metadata.Options
for _, o := range opts {
if err := o(&rOpts); err != nil {
return nil, errors.Wrapf(err, "failed to apply option")
}
}
gzipCompressors := []metadata.Decompressor{new(estargz.GzipDecompressor), new(estargz.LegacyGzipDecompressor)}
decompressors := append(gzipCompressors, rOpts.Decompressors...)
// Determine the size to fetch. Try to fetch as many bytes as possible.
fetchSize := maxFooterSize(sr.Size(), decompressors...)
if maybeTocOffset := rOpts.TOCOffset; maybeTocOffset > fetchSize {
if maybeTocOffset > sr.Size() {
return nil, fmt.Errorf("blob size %d is smaller than the toc offset", sr.Size())
}
fetchSize = sr.Size() - maybeTocOffset
}
start := time.Now() // before getting layer footer
footer := make([]byte, fetchSize)
if _, err := sr.ReadAt(footer, sr.Size()-fetchSize); err != nil {
return nil, fmt.Errorf("error reading footer: %v", err)
}
if rOpts.Telemetry != nil && rOpts.Telemetry.GetFooterLatency != nil {
rOpts.Telemetry.GetFooterLatency(start)
}
var allErr error
var tocR io.ReadCloser
var decompressor metadata.Decompressor
for _, d := range decompressors {
fSize := d.FooterSize()
fOffset := positive(int64(len(footer)) - fSize)
maybeTocBytes := footer[:fOffset]
_, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:])
if err != nil {
allErr = multierror.Append(allErr, err)
continue
}
if tocSize <= 0 {
tocSize = sr.Size() - tocOffset - fSize
}
if tocSize < int64(len(maybeTocBytes)) {
maybeTocBytes = maybeTocBytes[:tocSize]
}
tocR, err = decompressTOC(d, sr, tocOffset, tocSize, maybeTocBytes, rOpts)
if err != nil {
allErr = multierror.Append(allErr, err)
continue
}
decompressor = d
break
}
if tocR == nil {
if allErr == nil {
return nil, fmt.Errorf("failed to get the reader of TOC: unknown")
}
return nil, errors.Wrapf(allErr, "failed to get the reader of TOC")
}
defer tocR.Close()
r := &reader{sr: sr, db: db, initG: new(errgroup.Group), decompressor: decompressor}
if err := r.init(tocR, rOpts); err != nil {
return nil, errors.Wrapf(err, "failed to initialize matadata")
}
return r, nil
}
func maxFooterSize(blobSize int64, decompressors ...metadata.Decompressor) (res int64) {
for _, d := range decompressors {
if s := d.FooterSize(); res < s && s <= blobSize {
res = s
}
}
return
}
func decompressTOC(d metadata.Decompressor, sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte, opts metadata.Options) (io.ReadCloser, error) {
if len(tocBytes) > 0 {
start := time.Now() // before getting TOC
tocR, err := d.DecompressTOC(bytes.NewReader(tocBytes))
if err == nil {
if opts.Telemetry != nil && opts.Telemetry.GetTocLatency != nil {
opts.Telemetry.GetTocLatency(start)
}
return tocR, nil
}
}
start := time.Now() // before getting TOC
tocBytes = make([]byte, tocSize)
if _, err := sr.ReadAt(tocBytes, tocOff); err != nil {
return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocBytes), err)
}
r, err := d.DecompressTOC(bytes.NewReader(tocBytes))
if err != nil {
return nil, err
}
if opts.Telemetry != nil && opts.Telemetry.GetTocLatency != nil {
opts.Telemetry.GetTocLatency(start)
}
return r, nil
}
// RootID returns ID of the root node.
func (r *reader) RootID() uint32 {
return r.rootID
}
func (r *reader) TOCDigest() digest.Digest {
return r.tocDigest
}
// Clone returns a new reader identical to the current reader
// but uses the provided section reader for retrieving file paylaods.
func (r *reader) Clone(sr *io.SectionReader) (metadata.Reader, error) {
if err := r.waitInit(); err != nil {
return nil, err
}
return &reader{
db: r.db,
fsID: r.fsID,
rootID: r.rootID,
sr: sr,
initG: new(errgroup.Group),
decompressor: r.decompressor,
}, nil
}
func (r *reader) init(decompressedR io.Reader, rOpts metadata.Options) (retErr error) {
start := time.Now() // before parsing TOC JSON
// Initialize root node
var ok bool
for i := 0; i < 100; i++ {
fsID := xid.New().String()
if err := r.initRootNode(fsID); err != nil {
if errors.Is(err, bolt.ErrBucketExists) {
continue // try with another id
}
return errors.Wrapf(err, "failed to initialize root node %q", fsID)
}
ok = true
break
}
if !ok {
return fmt.Errorf("failed to get a unique id for metadata reader")
}
f, err := ioutil.TempFile("", "")
if err != nil {
return err
}
closeFunc := func() (closeErr error) {
name := f.Name()
if err := f.Close(); err != nil {
closeErr = multierror.Append(closeErr, err)
}
if err := os.Remove(name); err != nil {
closeErr = multierror.Append(closeErr, err)
}
return
}
defer func() {
if retErr != nil {
if err := closeFunc(); err != nil {
retErr = multierror.Append(retErr, err)
}
}
}()
if err := f.Chmod(0600); err != nil {
return err
}
dgstr := digest.Canonical.Digester()
if _, err := io.Copy(f, io.TeeReader(decompressedR, dgstr.Hash())); err != nil {
return errors.Wrapf(err, "failed to read TOC")
}
r.tocDigest = dgstr.Digest()
// Initialize file metadata in background. All operations refer to these metadata must wait
// until this initialization ends.
r.initG.Go(func() error {
defer closeFunc()
if _, err := f.Seek(0, io.SeekStart); err != nil {
return err
}
if err := r.initNodes(f); err != nil {
return err
}
if rOpts.Telemetry != nil && rOpts.Telemetry.DeserializeTocLatency != nil {
rOpts.Telemetry.DeserializeTocLatency(start)
}
return nil
})
return nil
}
func (r *reader) initRootNode(fsID string) error {
return r.db.Batch(func(tx *bolt.Tx) (err error) {
filesystems, err := tx.CreateBucketIfNotExists(bucketKeyFilesystems)
if err != nil {
return err
}
lbkt, err := filesystems.CreateBucket([]byte(fsID))
if err != nil {
return err
}
r.fsID = fsID
if _, err := lbkt.CreateBucket(bucketKeyMetadata); err != nil {
return err
}
nodes, err := lbkt.CreateBucket(bucketKeyNodes)
if err != nil {
return err
}
rootID, err := r.nextID()
if err != nil {
return err
}
rootBucket, err := nodes.CreateBucket(encodeID(rootID))
if err != nil {
return err
}
if err := writeAttr(rootBucket, &metadata.Attr{
Mode: os.ModeDir | 0755,
NumLink: 2, // The directory itself(.) and the parent link to this directory.
}); err != nil {
return err
}
r.rootID = rootID
return err
})
}
func (r *reader) initNodes(tr io.Reader) error {
dec := json.NewDecoder(tr)
for {
t, err := dec.Token()
if err != nil {
return errors.Wrapf(err, "failed to get JSON token")
}
if ele, ok := t.(string); ok {
if ele == "version" {
continue
}
if ele == "entries" {
continue
}
}
if de, ok := t.(json.Delim); ok {
if de.String() == "[" {
break
}
}
}
md := make(map[uint32]*metadataEntry)
if err := r.db.Batch(func(tx *bolt.Tx) (err error) {
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return err
}
nodes.FillPercent = 1.0 // we only do sequential write to this bucket
var wantNextOffsetID uint32
var lastEntBucketID uint32
var lastEntSize int64
var attr metadata.Attr
var ent estargz.TOCEntry
for dec.More() {
resetEnt(&ent)
if err := dec.Decode(&ent); err != nil {
return err
}
ent.Name = cleanEntryName(ent.Name)
if ent.Type == "chunk" {
if lastEntBucketID == 0 {
return fmt.Errorf("chunk entry must not be the topmost")
}
if ent.ChunkSize == 0 { // last chunk in this file
ent.ChunkSize = lastEntSize - ent.ChunkOffset
}
}
if ent.ChunkSize == 0 && ent.Size != 0 {
ent.ChunkSize = ent.Size
}
if ent.Type != "chunk" {
var id uint32
var b *bolt.Bucket
if ent.Type == "hardlink" {
id, err = getIDByName(md, ent.LinkName, r.rootID)
if err != nil {
return errors.Wrapf(err, "%q is a hardlink but cannot get link destination %q", ent.Name, ent.LinkName)
}
b, err = getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "cannot get hardlink destination %q ==> %q (%d)", ent.Name, ent.LinkName, id)
}
numLink, _ := binary.Varint(b.Get(bucketKeyNumLink))
if err := putInt(b, bucketKeyNumLink, numLink+1); err != nil {
return errors.Wrapf(err, "cannot put NumLink of %q ==> %q", ent.Name, ent.LinkName)
}
} else {
// Write node bucket
var found bool
if ent.Type == "dir" {
// Check if this directory is already created, if so overwrite it.
id, err = getIDByName(md, ent.Name, r.rootID)
if err == nil {
b, err = getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get directory bucket %d", id)
}
found = true
ent.NumLink = readNumLink(b)
}
}
if !found {
// No existing node. Create a new one.
id, err = r.nextID()
if err != nil {
return err
}
b, err = nodes.CreateBucket(encodeID(id))
if err != nil {
return err
}
ent.NumLink = 1 // at least the parent dir references this directory.
if ent.Type == "dir" {
ent.NumLink++ // at least "." references this directory.
}
}
if err := writeAttr(b, attrFromTOCEntry(&ent, &attr)); err != nil {
return errors.Wrapf(err, "failed to set attr to %d(%q)", id, ent.Name)
}
}
pdirName := parentDir(ent.Name)
pid, pb, err := r.getOrCreateDir(nodes, md, pdirName, r.rootID)
if err != nil {
return errors.Wrapf(err, "failed to create parent directory %q of %q", pdirName, ent.Name)
}
if err := setChild(md, pb, pid, path.Base(ent.Name), id, ent.Type == "dir"); err != nil {
return err
}
if ent.Offset > 0 && wantNextOffsetID > 0 {
if md[wantNextOffsetID] == nil {
md[wantNextOffsetID] = &metadataEntry{}
}
md[wantNextOffsetID].nextOffset = ent.Offset
}
if ent.Type == "reg" && ent.Size > 0 {
wantNextOffsetID = id
}
lastEntSize = ent.Size
lastEntBucketID = id
}
if (ent.Type == "reg" && ent.Size > 0) || (ent.Type == "chunk" && ent.ChunkSize > 0) {
if md[lastEntBucketID] == nil {
md[lastEntBucketID] = &metadataEntry{}
}
ce := chunkEntry{ent.Offset, ent.ChunkOffset, ent.ChunkSize, ent.ChunkDigest}
md[lastEntBucketID].chunks = append(md[lastEntBucketID].chunks, ce)
}
}
if wantNextOffsetID > 0 {
if md[wantNextOffsetID] == nil {
md[wantNextOffsetID] = &metadataEntry{}
}
md[wantNextOffsetID].nextOffset = r.sr.Size()
}
return nil
}); err != nil {
return err
}
addendum := make([]struct {
id []byte
md *metadataEntry
}, len(md))
i := 0
for id, d := range md {
addendum[i].id, addendum[i].md = encodeID(id), d
i++
}
sort.Slice(addendum, func(i, j int) bool {
return bytes.Compare(addendum[i].id, addendum[j].id) < 0
})
if err := r.db.Batch(func(tx *bolt.Tx) (err error) {
meta, err := getMetadata(tx, r.fsID)
if err != nil {
return err
}
meta.FillPercent = 1.0 // we only do sequential write to this bucket
for _, m := range addendum {
md, err := meta.CreateBucket(m.id)
if err != nil {
return err
}
if err := writeMetadataEntry(md, m.md); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
return nil
}
func (r *reader) getOrCreateDir(nodes *bolt.Bucket, md map[uint32]*metadataEntry, d string, rootID uint32) (id uint32, b *bolt.Bucket, err error) {
id, err = getIDByName(md, d, rootID)
if err != nil {
id, err = r.nextID()
if err != nil {
return 0, nil, err
}
b, err = nodes.CreateBucket(encodeID(id))
if err != nil {
return 0, nil, err
}
attr := &metadata.Attr{
Mode: os.ModeDir | 0755,
NumLink: 2, // The directory itself(.) and the parent link to this directory.
}
if err := writeAttr(b, attr); err != nil {
return 0, nil, err
}
if d != "" {
pid, pb, err := r.getOrCreateDir(nodes, md, parentDir(d), rootID)
if err != nil {
return 0, nil, err
}
if err := setChild(md, pb, pid, path.Base(d), id, true); err != nil {
return 0, nil, err
}
}
} else {
b, err = getNodeBucketByID(nodes, id)
if err != nil {
return 0, nil, errors.Wrapf(err, "failed to get dir bucket %d", id)
}
}
return id, b, nil
}
func (r *reader) waitInit() error {
// TODO: add timeout
return errors.Wrapf(r.initG.Wait(), "initialization failed")
}
func (r *reader) view(fn func(tx *bolt.Tx) error) error {
if err := r.waitInit(); err != nil {
return err
}
return r.db.View(func(tx *bolt.Tx) error {
return fn(tx)
})
}
func (r *reader) update(fn func(tx *bolt.Tx) error) error {
if err := r.waitInit(); err != nil {
return err
}
return r.db.Batch(func(tx *bolt.Tx) error {
return fn(tx)
})
}
// Close closes this reader. This removes underlying filesystem metadata as well.
func (r *reader) Close() error {
return r.update(func(tx *bolt.Tx) (err error) {
filesystems := tx.Bucket(bucketKeyFilesystems)
if filesystems == nil {
return nil
}
return filesystems.DeleteBucket([]byte(r.fsID))
})
}
// GetOffset returns an offset of a node.
func (r *reader) GetOffset(id uint32) (offset int64, _ error) {
if err := r.view(func(tx *bolt.Tx) error {
metadataEntries, err := getMetadata(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "metadata bucket of %q not found for searching offset of %d", r.fsID, id)
}
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return err
}
b, err := getNodeBucketByID(nodes, id)
if err != nil {
return err
}
size, _ := binary.Varint(b.Get(bucketKeySize))
if md, err := getMetadataBucketByID(metadataEntries, id); err == nil {
chunks, err := readChunks(md, size)
if err != nil {
return err
}
if len(chunks) > 0 {
offset = chunks[0].offset
}
}
return nil
}); err != nil {
return 0, err
}
return
}
// GetAttr returns file attribute of specified node.
func (r *reader) GetAttr(id uint32) (attr metadata.Attr, _ error) {
if r.rootID == id { // no need to wait for root dir
if err := r.db.View(func(tx *bolt.Tx) error {
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for sarching attr %d", r.fsID, id)
}
b, err := getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get attr bucket %d", id)
}
return readAttr(b, &attr)
}); err != nil {
return metadata.Attr{}, err
}
return attr, nil
}
if err := r.view(func(tx *bolt.Tx) error {
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for sarching attr %d", r.fsID, id)
}
b, err := getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get attr bucket %d", id)
}
return readAttr(b, &attr)
}); err != nil {
return metadata.Attr{}, err
}
return
}
// GetChild returns a child node that has the specified base name.
func (r *reader) GetChild(pid uint32, base string) (id uint32, attr metadata.Attr, _ error) {
if err := r.view(func(tx *bolt.Tx) error {
metadataEntries, err := getMetadata(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "metadata bucket of %q not found for getting child of %d", r.fsID, pid)
}
md, err := getMetadataBucketByID(metadataEntries, pid)
if err != nil {
return errors.Wrapf(err, "failed to get parent metadata %d", pid)
}
id, err = readChild(md, base)
if err != nil {
return errors.Wrapf(err, "failed to read child %q of %d", base, pid)
}
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for getting child of %d", r.fsID, pid)
}
child, err := getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get child bucket %d", id)
}
return readAttr(child, &attr)
}); err != nil {
return 0, metadata.Attr{}, err
}
return
}
// ForeachChild calls the specified callback function for each child node.
// When the callback returns non-nil error, this stops the iteration.
func (r *reader) ForeachChild(id uint32, f func(name string, id uint32, mode os.FileMode) bool) error {
type childInfo struct {
id uint32
mode os.FileMode
}
children := make(map[string]childInfo)
if err := r.view(func(tx *bolt.Tx) error {
metadataEntries, err := getMetadata(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for getting child of %d", r.fsID, id)
}
md, err := getMetadataBucketByID(metadataEntries, id)
if err != nil {
return nil // no child
}
var nodes *bolt.Bucket
firstName := md.Get(bucketKeyChildName)
if len(firstName) != 0 {
firstID := decodeID(md.Get(bucketKeyChildID))
if nodes == nil {
nodes, err = getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for getting children of %d", r.fsID, id)
}
}
firstChild, err := getNodeBucketByID(nodes, firstID)
if err != nil {
return errors.Wrapf(err, "failed to get first child bucket %d", firstID)
}
mode, _ := binary.Uvarint(firstChild.Get(bucketKeyMode))
children[string(firstName)] = childInfo{firstID, os.FileMode(uint32(mode))}
}
cbkt := md.Bucket(bucketKeyChildrenExtra)
if cbkt == nil {
return nil // no child
}
if nodes == nil {
nodes, err = getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for getting children of %d", r.fsID, id)
}
}
return cbkt.ForEach(func(k, v []byte) error {
id := decodeID(v)
child, err := getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get child bucket %d", id)
}
mode, _ := binary.Uvarint(child.Get(bucketKeyMode))
children[string(k)] = childInfo{id, os.FileMode(uint32(mode))}
return nil
})
}); err != nil {
return err
}
for k, e := range children {
if !f(k, e.id, e.mode) {
break
}
}
return nil
}
// OpenFile returns a section reader of the specified node.
func (r *reader) OpenFile(id uint32) (metadata.File, error) {
var chunks []chunkEntry
var size int64
var nextOffset int64
if err := r.view(func(tx *bolt.Tx) error {
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "nodes bucket of %q not found for opening %d", r.fsID, id)
}
b, err := getNodeBucketByID(nodes, id)
if err != nil {
return errors.Wrapf(err, "failed to get file bucket %d", id)
}
size, _ = binary.Varint(b.Get(bucketKeySize))
m, _ := binary.Uvarint(b.Get(bucketKeyMode))
if !os.FileMode(uint32(m)).IsRegular() {
return fmt.Errorf("%q is not a regular file", id)
}
metadataEntries, err := getMetadata(tx, r.fsID)
if err != nil {
return errors.Wrapf(err, "metadata bucket of %q not found for opening %d", r.fsID, id)
}
if md, err := getMetadataBucketByID(metadataEntries, id); err == nil {
chunks, err = readChunks(md, size)
if err != nil {
return errors.Wrapf(err, "failed to get chunks")
}
nextOffset, _ = binary.Varint(md.Get(bucketKeyNextOffset))
}
return nil
}); err != nil {
return nil, err
}
fr := &fileReader{
r: r,
size: size,
ents: chunks,
nextOffset: nextOffset,
}
return &file{io.NewSectionReader(fr, 0, size), chunks}, nil
}
type file struct {
io.ReaderAt
ents []chunkEntry
}
func (fr *file) ChunkEntryForOffset(offset int64) (off int64, size int64, dgst string, ok bool) {
i := sort.Search(len(fr.ents), func(i int) bool {
e := fr.ents[i]
return e.chunkOffset >= offset || (offset > e.chunkOffset && offset < e.chunkOffset+e.chunkSize)
})
if i == len(fr.ents) {
return 0, 0, "", false
}
ci := fr.ents[i]
return ci.chunkOffset, ci.chunkSize, ci.chunkDigest, true
}
type fileReader struct {
r *reader
size int64
ents []chunkEntry
nextOffset int64
}
// ReadAt reads file payload of this file.
func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) {
if off >= fr.size {
return 0, io.EOF
}
if off < 0 {
return 0, errors.New("invalid offset")
}
var ent chunkEntry
switch len(fr.ents) {
case 0:
return 0, errors.New("no chunk is registered")
case 1:
ent = fr.ents[0]
if ent.chunkOffset > off {
return 0, fmt.Errorf("no chunk coveres offset %d", off)
}
default:
i := sort.Search(len(fr.ents), func(i int) bool {
return fr.ents[i].chunkOffset > off
})
if i == 0 {
return 0, fmt.Errorf("no chunk coveres offset %d", off)
}
ent = fr.ents[i-1]
}
compressedBytesRemain := fr.nextOffset - ent.offset
bufSize := int(2 << 20)
if bufSize > int(compressedBytesRemain) {
bufSize = int(compressedBytesRemain)
}
br := bufio.NewReaderSize(io.NewSectionReader(fr.r.sr, ent.offset, compressedBytesRemain), bufSize)
if _, err := br.Peek(bufSize); err != nil {
return 0, fmt.Errorf("failed to peek read file payload: %v", err)
}
dr, err := fr.r.decompressor.Reader(br)
if err != nil {
return 0, fmt.Errorf("fileReader.ReadAt.decompressor.Reader: %v", err)
}
defer dr.Close()
base := off - ent.chunkOffset
if n, err := io.CopyN(ioutil.Discard, dr, base); n != base || err != nil {
return 0, fmt.Errorf("discard of %d bytes = %v, %v", base, n, err)
}
return io.ReadFull(dr, p)
}
// TODO: share it with memory pkg
func attrFromTOCEntry(src *estargz.TOCEntry, dst *metadata.Attr) *metadata.Attr {
dst.Size = src.Size
dst.ModTime, _ = time.Parse(time.RFC3339, src.ModTime3339)
dst.LinkName = src.LinkName
dst.Mode = src.Stat().Mode()
dst.UID = src.UID
dst.GID = src.GID
dst.DevMajor = src.DevMajor
dst.DevMinor = src.DevMinor
dst.Xattrs = src.Xattrs
dst.NumLink = src.NumLink
return dst
}
func getIDByName(md map[uint32]*metadataEntry, name string, rootID uint32) (uint32, error) {
name = cleanEntryName(name)
if name == "" {
return rootID, nil
}
dir, base := filepath.Split(name)
pid, err := getIDByName(md, dir, rootID)
if err != nil {
return 0, err
}
if md[pid] == nil {
return 0, fmt.Errorf("not found metadata of %d", pid)
}
if md[pid].children == nil {
return 0, fmt.Errorf("not found children of %q", pid)
}
c, ok := md[pid].children[base]
if !ok {
return 0, fmt.Errorf("not found child %q in %d", base, pid)
}
return c.id, nil
}
func setChild(md map[uint32]*metadataEntry, pb *bolt.Bucket, pid uint32, base string, id uint32, isDir bool) error {
if md[pid] == nil {
md[pid] = &metadataEntry{}
}
if md[pid].children == nil {
md[pid].children = make(map[string]childEntry)
}
md[pid].children[base] = childEntry{base, id}
if isDir {
numLink, _ := binary.Varint(pb.Get(bucketKeyNumLink))
if err := putInt(pb, bucketKeyNumLink, numLink+1); err != nil {
return errors.Wrapf(err, "cannot add numlink for children")
}
}
return nil
}
func parentDir(p string) string {
dir, _ := path.Split(p)
return strings.TrimSuffix(dir, "/")
}
func cleanEntryName(name string) string {
// Use path.Clean to consistently deal with path separators across platforms.
return strings.TrimPrefix(path.Clean("/"+name), "/")
}
func resetEnt(ent *estargz.TOCEntry) {
ent.Name = ""
ent.Type = ""
ent.Size = 0
ent.ModTime3339 = ""
ent.LinkName = ""
ent.Mode = 0
ent.UID = 0
ent.GID = 0
ent.Uname = ""
ent.Gname = ""
ent.Offset = 0
ent.DevMajor = 0
ent.DevMinor = 0
ent.NumLink = 0
ent.Xattrs = nil
ent.Digest = ""
ent.ChunkOffset = 0
ent.ChunkSize = 0
ent.ChunkDigest = ""
}
func positive(n int64) int64 {
if n < 0 {
return 0
}
return n
}
func (r *reader) NumOfNodes() (i int, _ error) {
if err := r.view(func(tx *bolt.Tx) error {
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return err
}
return nodes.ForEach(func(k, v []byte) error {
b := nodes.Bucket(k)
if b == nil {
return fmt.Errorf("entry bucket for %q not found", string(k))
}
var attr metadata.Attr
if err := readAttr(b, &attr); err != nil {
return err
}
i++
return nil
})
}); err != nil {
return 0, err
}
return
}
func (r *reader) NumOfChunks(id uint32) (i int, _ error) {
if err := r.view(func(tx *bolt.Tx) error {
metadataEntries, err := getMetadata(tx, r.fsID)
if err != nil {
return err
}
md, err := getMetadataBucketByID(metadataEntries, id)
if err != nil {
return err
}
nodes, err := getNodes(tx, r.fsID)
if err != nil {
return err
}
b, err := getNodeBucketByID(nodes, id)
if err != nil {
return err
}
size, _ := binary.Varint(b.Get(bucketKeySize))
chunks, err := readChunks(md, size)
if err != nil {
return err
}
i = len(chunks)
return nil
}); err != nil {
return 0, err
}
return
}

View File

@@ -0,0 +1,281 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package memory
import (
"fmt"
"io"
"math"
"os"
"sync"
"time"
"github.com/containerd/stargz-snapshotter/estargz"
"github.com/containerd/stargz-snapshotter/metadata"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
type reader struct {
r *estargz.Reader
rootID uint32
idMap map[uint32]*estargz.TOCEntry
idOfEntry map[*estargz.TOCEntry]uint32
mu sync.Mutex
curID uint32
curIDMu sync.Mutex
opts *metadata.Options
}
func (r *reader) nextID() (uint32, error) {
r.curIDMu.Lock()
defer r.curIDMu.Unlock()
if r.curID == math.MaxUint32 {
return 0, fmt.Errorf("sequence id too large")
}
r.curID++
return r.curID, nil
}
func NewReader(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error) {
var rOpts metadata.Options
for _, o := range opts {
if err := o(&rOpts); err != nil {
return nil, errors.Wrapf(err, "failed to apply option")
}
}
telemetry := &estargz.Telemetry{}
if rOpts.Telemetry != nil {
telemetry.GetFooterLatency = estargz.MeasureLatencyHook(rOpts.Telemetry.GetFooterLatency)
telemetry.GetTocLatency = estargz.MeasureLatencyHook(rOpts.Telemetry.GetTocLatency)
telemetry.DeserializeTocLatency = estargz.MeasureLatencyHook(rOpts.Telemetry.DeserializeTocLatency)
}
var decompressors []estargz.Decompressor
for _, d := range rOpts.Decompressors {
decompressors = append(decompressors, d)
}
er, err := estargz.Open(sr,
estargz.WithTOCOffset(rOpts.TOCOffset),
estargz.WithTelemetry(telemetry),
estargz.WithDecompressors(decompressors...),
)
if err != nil {
return nil, err
}
root, ok := er.Lookup("")
if !ok {
return nil, fmt.Errorf("failed to get root node")
}
r := &reader{r: er, idMap: make(map[uint32]*estargz.TOCEntry), idOfEntry: make(map[*estargz.TOCEntry]uint32), opts: &rOpts}
rootID, err := r.initID(root)
if err != nil {
return nil, err
}
r.rootID = rootID
return r, nil
}
func (r *reader) initID(e *estargz.TOCEntry) (id uint32, err error) {
var ok bool
r.mu.Lock()
id, ok = r.idOfEntry[e]
if !ok {
id, err = r.nextID()
if err != nil {
return 0, err
}
r.idMap[id] = e
r.idOfEntry[e] = id
}
r.mu.Unlock()
e.ForeachChild(func(_ string, ent *estargz.TOCEntry) bool {
if ent.Type == "hardlink" {
var ok bool
ent, ok = r.r.Lookup(ent.Name)
if !ok {
return false
}
}
_, err = r.initID(ent)
return err == nil
})
return id, err
}
func (r *reader) RootID() uint32 {
return r.rootID
}
func (r *reader) TOCDigest() digest.Digest {
return r.r.TOCDigest()
}
func (r *reader) GetOffset(id uint32) (offset int64, err error) {
r.mu.Lock()
defer r.mu.Unlock()
e, ok := r.idMap[id]
if !ok {
return 0, fmt.Errorf("entry %d not found", id)
}
return e.Offset, nil
}
func (r *reader) GetAttr(id uint32) (attr metadata.Attr, err error) {
r.mu.Lock()
e, ok := r.idMap[id]
r.mu.Unlock()
if !ok {
err = fmt.Errorf("entry %d not found", id)
return
}
// TODO: zero copy
attrFromTOCEntry(e, &attr)
return
}
func (r *reader) GetChild(pid uint32, base string) (id uint32, attr metadata.Attr, err error) {
r.mu.Lock()
e, ok := r.idMap[pid]
r.mu.Unlock()
if !ok {
err = fmt.Errorf("parent entry %d not found", pid)
return
}
child, ok := e.LookupChild(base)
if !ok {
err = fmt.Errorf("child %q of entry %d not found", base, pid)
return
}
if child.Type == "hardlink" {
child, ok = r.r.Lookup(child.Name)
if !ok {
err = fmt.Errorf("child %q ()hardlink of entry %d not found", base, pid)
return
}
}
cid, ok := r.idOfEntry[child]
if !ok {
err = fmt.Errorf("id of entry %q not found", base)
return
}
// TODO: zero copy
attrFromTOCEntry(child, &attr)
return cid, attr, nil
}
func (r *reader) ForeachChild(id uint32, f func(name string, id uint32, mode os.FileMode) bool) error {
r.mu.Lock()
e, ok := r.idMap[id]
r.mu.Unlock()
if !ok {
return fmt.Errorf("parent entry %d not found", id)
}
var err error
e.ForeachChild(func(baseName string, ent *estargz.TOCEntry) bool {
if ent.Type == "hardlink" {
var ok bool
ent, ok = r.r.Lookup(ent.Name)
if !ok {
return false
}
}
r.mu.Lock()
id, ok := r.idOfEntry[ent]
r.mu.Unlock()
if !ok {
err = fmt.Errorf("id of child entry %q not found", baseName)
return false
}
return f(baseName, id, ent.Stat().Mode())
})
return err
}
func (r *reader) OpenFile(id uint32) (metadata.File, error) {
r.mu.Lock()
e, ok := r.idMap[id]
r.mu.Unlock()
if !ok {
return nil, fmt.Errorf("entry %d not found", id)
}
sr, err := r.r.OpenFile(e.Name)
if err != nil {
return nil, err
}
return &file{r, e, sr}, nil
}
func (r *reader) Clone(sr *io.SectionReader) (metadata.Reader, error) {
return NewReader(sr,
metadata.WithTOCOffset(r.opts.TOCOffset),
metadata.WithTelemetry(r.opts.Telemetry),
metadata.WithDecompressors(r.opts.Decompressors...),
)
}
func (r *reader) Close() error {
return nil
}
type file struct {
r *reader
e *estargz.TOCEntry
sr *io.SectionReader
}
func (r *file) ChunkEntryForOffset(offset int64) (off int64, size int64, dgst string, ok bool) {
e, ok := r.r.r.ChunkEntryForOffset(r.e.Name, offset)
if !ok {
return 0, 0, "", false
}
dgst = e.Digest
if e.ChunkDigest != "" {
// NOTE* "reg" also can contain ChunkDigest (e.g. when "reg" is the first entry of
// chunked file)
dgst = e.ChunkDigest
}
return e.ChunkOffset, e.ChunkSize, dgst, true
}
func (r *file) ReadAt(p []byte, off int64) (n int, err error) {
return r.sr.ReadAt(p, off)
}
func (r *reader) NumOfNodes() (i int, _ error) {
return len(r.idMap), nil
}
// TODO: share it with db pkg
func attrFromTOCEntry(src *estargz.TOCEntry, dst *metadata.Attr) *metadata.Attr {
dst.Size = src.Size
dst.ModTime, _ = time.Parse(time.RFC3339, src.ModTime3339)
dst.LinkName = src.LinkName
dst.Mode = src.Stat().Mode()
dst.UID = src.UID
dst.GID = src.GID
dst.DevMajor = src.DevMajor
dst.DevMinor = src.DevMinor
dst.Xattrs = src.Xattrs
dst.NumLink = src.NumLink
return dst
}

View File

@@ -0,0 +1,131 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metadata
import (
"io"
"os"
"time"
"github.com/containerd/stargz-snapshotter/estargz"
digest "github.com/opencontainers/go-digest"
)
// Attr reprensents the attributes of a node.
type Attr struct {
// Size, for regular files, is the logical size of the file.
Size int64
// ModTime is the modification time of the node.
ModTime time.Time
// LinkName, for symlinks, is the link target.
LinkName string
// Mode is the permission and mode bits.
Mode os.FileMode
// UID is the user ID of the owner.
UID int
// GID is the group ID of the owner.
GID int
// DevMajor is the major device number for device.
DevMajor int
// DevMinor is the major device number for device.
DevMinor int
// Xattrs are the extended attribute for the node.
Xattrs map[string][]byte
// NumLink is the number of names pointing to this node.
NumLink int
}
// TODO: docs
type Reader interface {
RootID() uint32
TOCDigest() digest.Digest
GetOffset(id uint32) (offset int64, err error)
GetAttr(id uint32) (attr Attr, err error)
GetChild(pid uint32, base string) (id uint32, attr Attr, err error)
ForeachChild(id uint32, f func(name string, id uint32, mode os.FileMode) bool) error
OpenFile(id uint32) (File, error)
Clone(sr *io.SectionReader) (Reader, error)
Close() error
}
type File interface {
ChunkEntryForOffset(offset int64) (off int64, size int64, dgst string, ok bool)
ReadAt(p []byte, off int64) (n int, err error)
}
type Decompressor interface {
estargz.Decompressor
// DecompressTOC decompresses the passed blob and returns a reader of TOC JSON.
DecompressTOC(io.Reader) (tocJSON io.ReadCloser, err error)
}
type Options struct {
TOCOffset int64
Telemetry *Telemetry
Decompressors []Decompressor
}
// Option is an option to configure the behaviour of reader.
type Option func(o *Options) error
// WithTOCOffset option specifies the offset of TOC
func WithTOCOffset(tocOffset int64) Option {
return func(o *Options) error {
o.TOCOffset = tocOffset
return nil
}
}
// WithTelemetry option specifies the telemetry hooks
func WithTelemetry(telemetry *Telemetry) Option {
return func(o *Options) error {
o.Telemetry = telemetry
return nil
}
}
// WithDecompressors option specifies decompressors to use.
// Default is gzip-based decompressor.
func WithDecompressors(decompressors ...Decompressor) Option {
return func(o *Options) error {
o.Decompressors = decompressors
return nil
}
}
// A func which takes start time and records the diff
type MeasureLatencyHook func(time.Time)
// A struct which defines telemetry hooks. By implementing these hooks you should be able to record
// the latency metrics of the respective steps of estargz open operation.
type Telemetry struct {
GetFooterLatency MeasureLatencyHook // measure time to get stargz footer (in milliseconds)
GetTocLatency MeasureLatencyHook // measure time to GET TOC JSON (in milliseconds)
DeserializeTocLatency MeasureLatencyHook // measure time to deserialize TOC JSON (in milliseconds)
}

View File

@@ -0,0 +1,659 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metadata
import (
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
"time"
"github.com/containerd/stargz-snapshotter/estargz"
"github.com/containerd/stargz-snapshotter/estargz/zstdchunked"
"github.com/containerd/stargz-snapshotter/util/testutil"
"github.com/hashicorp/go-multierror"
"github.com/klauspost/compress/zstd"
)
var allowedPrefix = [4]string{"", "./", "/", "../"}
type compression interface {
estargz.Compressor
Decompressor
}
var srcCompressions = map[string]compression{
"zstd-fastest": zstdCompressionWithLevel(zstd.SpeedFastest),
"zstd-default": zstdCompressionWithLevel(zstd.SpeedDefault),
"zstd-bettercompression": zstdCompressionWithLevel(zstd.SpeedBetterCompression),
"gzip-nocompression": gzipCompressionWithLevel(gzip.NoCompression),
"gzip-bestspeed": gzipCompressionWithLevel(gzip.BestSpeed),
"gzip-bestcompression": gzipCompressionWithLevel(gzip.BestCompression),
"gzip-defaultcompression": gzipCompressionWithLevel(gzip.DefaultCompression),
"gzip-huffmanonly": gzipCompressionWithLevel(gzip.HuffmanOnly),
}
type zstdCompression struct {
*zstdchunked.Compressor
*zstdchunked.Decompressor
}
func zstdCompressionWithLevel(compressionLevel zstd.EncoderLevel) compression {
return &zstdCompression{&zstdchunked.Compressor{CompressionLevel: compressionLevel}, &zstdchunked.Decompressor{}}
}
type gzipCompression struct {
*estargz.GzipCompressor
*estargz.GzipDecompressor
}
func gzipCompressionWithLevel(compressionLevel int) compression {
return gzipCompression{estargz.NewGzipCompressorWithLevel(compressionLevel), &estargz.GzipDecompressor{}}
}
type ReaderFactory func(sr *io.SectionReader, opts ...Option) (r TestableReader, done func() error, err error)
type TestableReader interface {
Reader
NumOfNodes() (i int, _ error)
}
// TestReader tests Reader returns correct file metadata.
func TestReader(t *testing.T, factory ReaderFactory) {
sampleTime := time.Now().Truncate(time.Second)
sampleText := "qwer" + "tyui" + "opas" + "dfgh" + "jk"
tests := []struct {
name string
chunkSize int
in []testutil.TarEntry
want []check
}{
{
name: "empty",
in: []testutil.TarEntry{},
want: []check{
numOfNodes(2), // root dir + prefetch landmark
},
},
{
name: "files",
in: []testutil.TarEntry{
testutil.File("foo", "foofoo", testutil.WithFileMode(0644|os.ModeSetuid)),
testutil.Dir("bar/"),
testutil.File("bar/baz.txt", "bazbazbaz", testutil.WithFileOwner(1000, 1000)),
testutil.File("xxx.txt", "xxxxx", testutil.WithFileModTime(sampleTime)),
testutil.File("y.txt", "", testutil.WithFileXattrs(map[string]string{"testkey": "testval"})),
},
want: []check{
numOfNodes(7), // root dir + prefetch landmark + 1 dir + 4 files
hasFile("foo", "foofoo", 6),
hasMode("foo", 0644|os.ModeSetuid),
hasFile("bar/baz.txt", "bazbazbaz", 9),
hasOwner("bar/baz.txt", 1000, 1000),
hasFile("xxx.txt", "xxxxx", 5),
hasModTime("xxx.txt", sampleTime),
hasFile("y.txt", "", 0),
hasXattrs("y.txt", map[string]string{"testkey": "testval"}),
},
},
{
name: "dirs",
in: []testutil.TarEntry{
testutil.Dir("foo/", testutil.WithDirMode(os.ModeDir|0600|os.ModeSticky)),
testutil.Dir("foo/bar/", testutil.WithDirOwner(1000, 1000)),
testutil.File("foo/bar/baz.txt", "testtest"),
testutil.File("foo/bar/xxxx", "x"),
testutil.File("foo/bar/yyy", "yyy"),
testutil.Dir("foo/a/", testutil.WithDirModTime(sampleTime)),
testutil.Dir("foo/a/1/", testutil.WithDirXattrs(map[string]string{"testkey": "testval"})),
testutil.File("foo/a/1/2", "1111111111"),
},
want: []check{
numOfNodes(10), // root dir + prefetch landmark + 4 dirs + 4 files
hasDirChildren("foo", "bar", "a"),
hasDirChildren("foo/bar", "baz.txt", "xxxx", "yyy"),
hasDirChildren("foo/a", "1"),
hasDirChildren("foo/a/1", "2"),
hasMode("foo", os.ModeDir|0600|os.ModeSticky),
hasOwner("foo/bar", 1000, 1000),
hasModTime("foo/a", sampleTime),
hasXattrs("foo/a/1", map[string]string{"testkey": "testval"}),
hasFile("foo/bar/baz.txt", "testtest", 8),
hasFile("foo/bar/xxxx", "x", 1),
hasFile("foo/bar/yyy", "yyy", 3),
hasFile("foo/a/1/2", "1111111111", 10),
},
},
{
name: "hardlinks",
in: []testutil.TarEntry{
testutil.File("foo", "foofoo", testutil.WithFileOwner(1000, 1000)),
testutil.Dir("bar/"),
testutil.Link("bar/foolink", "foo"),
testutil.Link("bar/foolink2", "bar/foolink"),
testutil.Dir("bar/1/"),
testutil.File("bar/1/baz.txt", "testtest"),
testutil.Link("barlink", "bar/1/baz.txt"),
testutil.Symlink("foosym", "bar/foolink2"),
},
want: []check{
numOfNodes(7), // root dir + prefetch landmark + 2 dirs + 1 flie(linked) + 1 file(linked) + 1 symlink
hasFile("foo", "foofoo", 6),
hasOwner("foo", 1000, 1000),
hasFile("bar/foolink", "foofoo", 6),
hasOwner("bar/foolink", 1000, 1000),
hasFile("bar/foolink2", "foofoo", 6),
hasOwner("bar/foolink2", 1000, 1000),
hasFile("bar/1/baz.txt", "testtest", 8),
hasFile("barlink", "testtest", 8),
hasDirChildren("bar", "foolink", "foolink2", "1"),
hasDirChildren("bar/1", "baz.txt"),
sameNodes("foo", "bar/foolink", "bar/foolink2"),
sameNodes("bar/1/baz.txt", "barlink"),
linkName("foosym", "bar/foolink2"),
hasNumLink("foo", 3), // parent dir + 2 links
hasNumLink("barlink", 2), // parent dir + 1 link
hasNumLink("bar", 3), // parent + "." + child's ".."
},
},
{
name: "various files",
in: []testutil.TarEntry{
testutil.Dir("bar/"),
testutil.File("bar/../bar///////////////////foo", ""),
testutil.Chardev("bar/cdev", 10, 11),
testutil.Blockdev("bar/bdev", 100, 101),
testutil.Fifo("bar/fifo"),
},
want: []check{
numOfNodes(7), // root dir + prefetch landmark + 1 file + 1 dir + 1 cdev + 1 bdev + 1 fifo
hasFile("bar/foo", "", 0),
hasChardev("bar/cdev", 10, 11),
hasBlockdev("bar/bdev", 100, 101),
hasFifo("bar/fifo"),
},
},
{
name: "chunks",
chunkSize: 4,
in: []testutil.TarEntry{
testutil.Dir("foo/"),
testutil.File("foo/small", sampleText[:2]),
testutil.File("foo/large", sampleText),
},
want: []check{
numOfNodes(5), // root dir + prefetch landmark + 1 dir + 2 files
numOfChunks("foo/large", 1+(len(sampleText)/4)),
hasFileContentsOffset("foo/small", 0, sampleText[:2]),
hasFileContentsOffset("foo/large", 0, sampleText[0:]),
hasFileContentsOffset("foo/large", 1, sampleText[1:]),
hasFileContentsOffset("foo/large", 2, sampleText[2:]),
hasFileContentsOffset("foo/large", 3, sampleText[3:]),
hasFileContentsOffset("foo/large", 4, sampleText[4:]),
hasFileContentsOffset("foo/large", 5, sampleText[5:]),
hasFileContentsOffset("foo/large", 6, sampleText[6:]),
hasFileContentsOffset("foo/large", 7, sampleText[7:]),
hasFileContentsOffset("foo/large", 8, sampleText[8:]),
hasFileContentsOffset("foo/large", 9, sampleText[9:]),
hasFileContentsOffset("foo/large", 10, sampleText[10:]),
hasFileContentsOffset("foo/large", 11, sampleText[11:]),
hasFileContentsOffset("foo/large", 12, sampleText[12:]),
hasFileContentsOffset("foo/large", int64(len(sampleText)-1), ""),
},
},
}
for _, tt := range tests {
for _, prefix := range allowedPrefix {
prefix := prefix
for srcCompresionName, srcCompression := range srcCompressions {
srcCompression := srcCompression
t.Run(tt.name+"-"+srcCompresionName, func(t *testing.T) {
opts := []testutil.BuildEStargzOption{
testutil.WithBuildTarOptions(testutil.WithPrefix(prefix)),
testutil.WithEStargzOptions(estargz.WithCompression(srcCompression)),
}
if tt.chunkSize > 0 {
opts = append(opts, testutil.WithEStargzOptions(estargz.WithChunkSize(tt.chunkSize)))
}
esgz, _, err := testutil.BuildEStargz(tt.in, opts...)
if err != nil {
t.Fatalf("failed to build sample eStargz: %v", err)
}
telemetry, checkCalled := newCalledTelemetry()
r, done, err := factory(esgz,
WithDecompressors(new(zstdchunked.Decompressor)), WithTelemetry(telemetry))
if err != nil {
t.Fatalf("failed to create new reader: %v", err)
}
defer done()
t.Logf("vvvvv Node tree vvvvv")
t.Logf("[%d] ROOT", r.RootID())
dumpNodes(t, r, r.RootID(), 1)
t.Logf("^^^^^^^^^^^^^^^^^^^^^")
for _, want := range tt.want {
want(t, r)
}
if err := checkCalled(); err != nil {
t.Errorf("telemetry failure: %v", err)
}
})
}
}
}
}
func newCalledTelemetry() (telemetry *Telemetry, check func() error) {
var getFooterLatencyCalled bool
var getTocLatencyCalled bool
var deserializeTocLatencyCalled bool
return &Telemetry{
func(time.Time) { getFooterLatencyCalled = true },
func(time.Time) { getTocLatencyCalled = true },
func(time.Time) { deserializeTocLatencyCalled = true },
}, func() error {
var allErr error
if !getFooterLatencyCalled {
allErr = multierror.Append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called"))
}
if !getTocLatencyCalled {
allErr = multierror.Append(allErr, fmt.Errorf("metrics GetTocLatency isn't called"))
}
if !deserializeTocLatencyCalled {
allErr = multierror.Append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called"))
}
return allErr
}
}
func dumpNodes(t *testing.T, r TestableReader, id uint32, level int) {
if err := r.ForeachChild(id, func(name string, id uint32, mode os.FileMode) bool {
ind := ""
for i := 0; i < level; i++ {
ind += " "
}
t.Logf("%v+- [%d] %q : %v", ind, id, name, mode)
dumpNodes(t, r, id, level+1)
return true
}); err != nil {
t.Errorf("failed to dump nodes %v", err)
}
}
type check func(*testing.T, TestableReader)
func numOfNodes(want int) check {
return func(t *testing.T, r TestableReader) {
i, err := r.NumOfNodes()
if err != nil {
t.Errorf("num of nodes: %v", err)
}
if want != i {
t.Errorf("unexpected num of nodes %d; want %d", i, want)
}
}
}
func numOfChunks(name string, num int) check {
return func(t *testing.T, r TestableReader) {
nr, ok := r.(interface {
NumOfChunks(id uint32) (i int, _ error)
})
if !ok {
return // skip
}
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
return
}
i, err := nr.NumOfChunks(id)
if err != nil {
t.Errorf("failed to get num of chunks of %q: %v", name, err)
return
}
if i != num {
t.Errorf("unexpected num of chunk of %q : %d want %d", name, i, num)
}
}
}
func sameNodes(n string, nodes ...string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, n)
if err != nil {
t.Errorf("failed to lookup %q: %v", n, err)
return
}
for _, en := range nodes {
eid, err := lookup(r, en)
if err != nil {
t.Errorf("failed to lookup %q: %v", en, err)
return
}
if eid != id {
t.Errorf("unexpected ID of %q: %d want %d", en, eid, id)
}
}
}
}
func linkName(name string, linkName string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("failed to get attr of %q: %v", name, err)
return
}
if attr.Mode&os.ModeSymlink == 0 {
t.Errorf("%q is not a symlink: %v", name, attr.Mode)
return
}
if attr.LinkName != linkName {
t.Errorf("unexpected link name of %q : %q want %q", name, attr.LinkName, linkName)
return
}
}
}
func hasNumLink(name string, numLink int) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("failed to get attr of %q: %v", name, err)
return
}
if attr.NumLink != numLink {
t.Errorf("unexpected numLink of %q: %d want %d", name, attr.NumLink, numLink)
return
}
}
}
func hasDirChildren(name string, children ...string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("failed to get attr of %q: %v", name, err)
return
}
if !attr.Mode.IsDir() {
t.Errorf("%q is not directory: %v", name, attr.Mode)
return
}
found := map[string]struct{}{}
if err := r.ForeachChild(id, func(name string, id uint32, mode os.FileMode) bool {
found[name] = struct{}{}
return true
}); err != nil {
t.Errorf("failed to see children %v", err)
return
}
if len(found) != len(children) {
t.Errorf("unexpected number of children of %q : %d want %d", name, len(found), len(children))
}
for _, want := range children {
if _, ok := found[want]; !ok {
t.Errorf("expected child %q not found in %q", want, name)
}
}
}
}
func hasChardev(name string, maj, min int) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find chardev %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of chardev %q: %v", name, err)
return
}
if attr.Mode&os.ModeDevice == 0 || attr.Mode&os.ModeCharDevice == 0 {
t.Errorf("file %q is not a chardev: %v", name, attr.Mode)
return
}
if attr.DevMajor != maj || attr.DevMinor != min {
t.Errorf("unexpected major/minor of chardev %q: %d/%d want %d/%d", name, attr.DevMajor, attr.DevMinor, maj, min)
return
}
}
}
func hasBlockdev(name string, maj, min int) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find blockdev %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of blockdev %q: %v", name, err)
return
}
if attr.Mode&os.ModeDevice == 0 || attr.Mode&os.ModeCharDevice != 0 {
t.Errorf("file %q is not a blockdev: %v", name, attr.Mode)
return
}
if attr.DevMajor != maj || attr.DevMinor != min {
t.Errorf("unexpected major/minor of blockdev %q: %d/%d want %d/%d", name, attr.DevMajor, attr.DevMinor, maj, min)
return
}
}
}
func hasFifo(name string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find blockdev %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of blockdev %q: %v", name, err)
return
}
if attr.Mode&os.ModeNamedPipe == 0 {
t.Errorf("file %q is not a fifo: %v", name, attr.Mode)
return
}
}
}
func hasFile(name, content string, size int64) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of file %q: %v", name, err)
return
}
if !attr.Mode.IsRegular() {
t.Errorf("file %q is not a regular file: %v", name, attr.Mode)
return
}
sr, err := r.OpenFile(id)
if err != nil {
t.Errorf("cannot open file %q: %v", name, err)
return
}
data, err := ioutil.ReadAll(io.NewSectionReader(sr, 0, attr.Size))
if err != nil {
t.Errorf("cannot read file %q: %v", name, err)
return
}
if attr.Size != size {
t.Errorf("unexpected size of file %q : %d (%q) want %d (%q)", name, attr.Size, string(data), size, content)
return
}
if string(data) != content {
t.Errorf("unexpected content of %q: %q want %q", name, string(data), content)
return
}
}
}
func hasFileContentsOffset(name string, off int64, contents string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
return
}
fr, err := r.OpenFile(id)
if err != nil {
t.Errorf("failed to open file %q: %v", name, err)
return
}
buf := make([]byte, len(contents))
n, err := fr.ReadAt(buf, off)
if err != nil && err != io.EOF {
t.Errorf("failed to read file %q (off:%d, want:%q): %v", name, off, contents, err)
return
}
if n != len(contents) {
t.Errorf("failed to read contents %q (off:%d, want:%q) got %q", name, off, contents, string(buf))
return
}
}
}
func hasMode(name string, mode os.FileMode) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of file %q: %v", name, err)
return
}
if attr.Mode != mode {
t.Errorf("unexpected mode of %q: %v want %v", name, attr.Mode, mode)
return
}
}
}
func hasOwner(name string, uid, gid int) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of file %q: %v", name, err)
return
}
if attr.UID != uid || attr.GID != gid {
t.Errorf("unexpected owner of %q: (%d:%d) want (%d:%d)", name, attr.UID, attr.GID, uid, gid)
return
}
}
}
func hasModTime(name string, modTime time.Time) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of file %q: %v", name, err)
return
}
attrModTime := attr.ModTime
if attrModTime.Before(modTime) || attrModTime.After(modTime) {
t.Errorf("unexpected time of %q: %v; want %v", name, attrModTime, modTime)
return
}
}
}
func hasXattrs(name string, xattrs map[string]string) check {
return func(t *testing.T, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
return
}
attr, err := r.GetAttr(id)
if err != nil {
t.Errorf("cannot get attr of file %q: %v", name, err)
return
}
if len(attr.Xattrs) != len(xattrs) {
t.Errorf("unexpected size of xattr of %q: %d want %d", name, len(attr.Xattrs), len(xattrs))
return
}
for k, v := range attr.Xattrs {
if xattrs[k] != string(v) {
t.Errorf("unexpected xattr of %q: %q=%q want %q=%q", name, k, string(v), k, xattrs[k])
}
}
}
}
func lookup(r TestableReader, name string) (uint32, error) {
name = strings.TrimPrefix(path.Clean("/"+name), "/")
if name == "" {
return r.RootID(), nil
}
dir, base := filepath.Split(name)
pid, err := lookup(r, dir)
if err != nil {
return 0, err
}
id, _, err := r.GetChild(pid, base)
return id, err
}

View File

@@ -40,6 +40,7 @@ import (
"github.com/containerd/containerd/remotes/docker"
dconfig "github.com/containerd/containerd/remotes/docker/config"
"github.com/containerd/stargz-snapshotter/fs/source"
rhttp "github.com/hashicorp/go-retryablehttp"
"github.com/pkg/errors"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)
@@ -136,18 +137,24 @@ func RegistryHostsFromCRIConfig(ctx context.Context, config Registry, credsFuncs
}
var (
transport = http.DefaultTransport.(*http.Transport).Clone()
client = &http.Client{Transport: transport}
config = config.Configs[u.Host]
rclient = rhttp.NewClient()
config = config.Configs[u.Host]
)
rclient.Logger = nil // disable logging every request
if config.TLS != nil {
transport.TLSClientConfig, err = getTLSConfig(*config.TLS)
if err != nil {
return nil, errors.Wrapf(err, "get TLSConfig for registry %q", e)
if tr, ok := rclient.HTTPClient.Transport.(*http.Transport); ok {
tr.TLSClientConfig, err = getTLSConfig(*config.TLS)
if err != nil {
return nil, errors.Wrapf(err, "get TLSConfig for registry %q", e)
}
} else {
return nil, errors.New("TLS config cannot be applied; Client.Transport is not *http.Transport")
}
}
client := rclient.StandardClient()
authorizer := docker.NewDockerAuthorizer(
docker.WithAuthClient(client),
docker.WithAuthCreds(multiCredsFuncs(ref, credsFuncs...)))

View File

@@ -17,12 +17,12 @@
package resolver
import (
"net/http"
"time"
"github.com/containerd/containerd/reference"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/stargz-snapshotter/fs/source"
rhttp "github.com/hashicorp/go-retryablehttp"
)
const defaultRequestTimeoutSec = 30
@@ -59,7 +59,9 @@ func RegistryHostsFromConfig(cfg Config, credsFuncs ...Credential) source.Regist
for _, h := range append(cfg.Host[host].Mirrors, MirrorConfig{
Host: host,
}) {
tr := &http.Client{Transport: http.DefaultTransport.(*http.Transport).Clone()}
client := rhttp.NewClient()
client.Logger = nil // disable logging every request
tr := client.StandardClient()
if h.RequestTimeoutSec >= 0 {
if h.RequestTimeoutSec == 0 {
tr.Timeout = defaultRequestTimeoutSec * time.Second

View File

@@ -35,6 +35,7 @@ type Option func(*options)
type options struct {
credsFuncs []resolver.Credential
registryHosts source.RegistryHosts
fsOpts []stargzfs.Option
}
// WithCredsFuncs specifies credsFuncs to be used for connecting to the registries.
@@ -51,6 +52,13 @@ func WithCustomRegistryHosts(hosts source.RegistryHosts) Option {
}
}
// WithFilesystemOptions allowes to pass filesystem-related configuration.
func WithFilesystemOptions(opts ...stargzfs.Option) Option {
return func(o *options) {
o.fsOpts = opts
}
}
// NewStargzSnapshotterService returns stargz snapshotter.
func NewStargzSnapshotterService(ctx context.Context, root string, config *Config, opts ...Option) (snapshots.Snapshotter, error) {
var sOpts options
@@ -65,13 +73,11 @@ func NewStargzSnapshotterService(ctx context.Context, root string, config *Confi
}
// Configure filesystem and snapshotter
fs, err := stargzfs.NewFilesystem(fsRoot(root),
config.Config,
stargzfs.WithGetSources(sources(
sourceFromCRILabels(hosts), // provides source info based on CRI labels
source.FromDefaultLabels(hosts), // provides source info based on default labels
)),
)
fsOpts := append(sOpts.fsOpts, stargzfs.WithGetSources(sources(
sourceFromCRILabels(hosts), // provides source info based on CRI labels
source.FromDefaultLabels(hosts), // provides source info based on default labels
)))
fs, err := stargzfs.NewFilesystem(fsRoot(root), config.Config, fsOpts...)
if err != nil {
log.G(ctx).WithError(err).Fatalf("failed to configure filesystem")
}

View File

@@ -251,7 +251,7 @@ func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...s
WithError(err).Warn("failed to prepare remote snapshot")
} else {
base.Labels[remoteLabel] = remoteLabelVal // Mark this snapshot as remote
err := o.Commit(ctx, target, key, append(opts, snapshots.WithLabels(base.Labels))...)
err := o.commit(ctx, true, target, key, append(opts, snapshots.WithLabels(base.Labels))...)
if err == nil || errdefs.IsAlreadyExists(err) {
// count also AlreadyExists as "success"
log.G(lCtx).WithField(remoteSnapshotLogKey, prepareSucceeded).Debug("prepared remote snapshot")
@@ -293,6 +293,10 @@ func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
}
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
return o.commit(ctx, false, name, key, opts...)
}
func (o *snapshotter) commit(ctx context.Context, isRemote bool, name, key string, opts ...snapshots.Opt) error {
ctx, t, err := o.ms.TransactionContext(ctx, true)
if err != nil {
return err
@@ -307,17 +311,20 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
}()
// grab the existing id
id, _, _, err := storage.GetInfo(ctx, key)
id, _, usage, err := storage.GetInfo(ctx, key)
if err != nil {
return err
}
usage, err := fs.DiskUsage(ctx, o.upperPath(id))
if err != nil {
return err
if !isRemote { // skip diskusage for remote snapshots for allowing lazy preparation of nodes
du, err := fs.DiskUsage(ctx, o.upperPath(id))
if err != nil {
return err
}
usage = snapshots.Usage(du)
}
if _, err = storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
if _, err = storage.CommitActive(ctx, key, name, usage, opts...); err != nil {
return errors.Wrap(err, "failed to commit snapshot")
}

View File

@@ -0,0 +1,80 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testutil
import (
"compress/gzip"
"context"
"io"
"io/ioutil"
"net/http"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/local"
"github.com/containerd/containerd/images/archive"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
const (
// HelloArchiveURL points to an OCI archive of `hello-world`.
// Exported from `docker.io/library/hello-world@sha256:1a523af650137b8accdaed439c17d684df61ee4d74feac151b5b337bd29e7eec` .
// See https://github.com/AkihiroSuda/test-oci-archives/releases/tag/v20210101
HelloArchiveURL = "https://github.com/AkihiroSuda/test-oci-archives/releases/download/v20210101/hello-world.tar.gz"
// HelloArchiveDigest is the digest of the archive.
HelloArchiveDigest = "sha256:5aa022621c4de0e941ab2a30d4569c403e156b4ba2de2ec32e382ae8679f40e1"
)
// EnsureHello creates a temp content store and ensures `hello-world` image from HelloArchiveURL into the store.
func EnsureHello(ctx context.Context) (*ocispec.Descriptor, content.Store, error) {
// Pulling an image without the daemon is a mess, so we use OCI archive here.
resp, err := http.Get(HelloArchiveURL)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
sha256Digester := digest.SHA256.Digester()
sha256Hasher := sha256Digester.Hash()
tr := io.TeeReader(resp.Body, sha256Hasher)
gzReader, err := gzip.NewReader(tr)
if err != nil {
return nil, nil, err
}
tempDir, err := ioutil.TempDir("", "test-estargz")
if err != nil {
return nil, nil, err
}
cs, err := local.NewStore(tempDir)
if err != nil {
return nil, nil, err
}
desc, err := archive.ImportIndex(ctx, cs, gzReader)
if err != nil {
return nil, nil, err
}
resp.Body.Close()
if d := sha256Digester.Digest().String(); d != HelloArchiveDigest {
err = errors.Errorf("expected digest of %q to be %q, got %q", HelloArchiveURL, HelloArchiveDigest, d)
return nil, nil, err
}
return &desc, cs, nil
}

View File

@@ -0,0 +1,74 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testutil
import (
"bytes"
"io"
"github.com/containerd/stargz-snapshotter/estargz"
digest "github.com/opencontainers/go-digest"
)
type buildEStargzOptions struct {
estargzOptions []estargz.Option
buildTarOptions []BuildTarOption
}
type BuildEStargzOption func(o *buildEStargzOptions) error
// WithEStargzOptions specifies options for estargz lib
func WithEStargzOptions(eo ...estargz.Option) BuildEStargzOption {
return func(o *buildEStargzOptions) error {
o.estargzOptions = eo
return nil
}
}
// WithBuildTarOptions option specifies the options for tar creation
func WithBuildTarOptions(to ...BuildTarOption) BuildEStargzOption {
return func(o *buildEStargzOptions) error {
o.buildTarOptions = to
return nil
}
}
func BuildEStargz(ents []TarEntry, opts ...BuildEStargzOption) (*io.SectionReader, digest.Digest, error) {
var beOpts buildEStargzOptions
for _, o := range opts {
o(&beOpts)
}
tarBuf := new(bytes.Buffer)
if _, err := io.Copy(tarBuf, BuildTar(ents, beOpts.buildTarOptions...)); err != nil {
return nil, "", err
}
tarData := tarBuf.Bytes()
rc, err := estargz.Build(
io.NewSectionReader(bytes.NewReader(tarData), 0, int64(len(tarData))),
beOpts.estargzOptions...)
if err != nil {
return nil, "", err
}
defer rc.Close()
vsb := new(bytes.Buffer)
if _, err := io.Copy(vsb, rc); err != nil {
return nil, "", err
}
vsbb := vsb.Bytes()
return io.NewSectionReader(bytes.NewReader(vsbb), 0, int64(len(vsbb))), rc.TOCDigest(), nil
}

View File

@@ -0,0 +1,310 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testutil
// This utility helps test codes to generate sample tar blobs.
import (
"archive/tar"
"fmt"
"io"
"os"
"strings"
"time"
)
// TarEntry is an entry of tar.
type TarEntry interface {
AppendTar(tw *tar.Writer, opts BuildTarOptions) error
}
// BuildTarOptions is a set of options used during building blob.
type BuildTarOptions struct {
// Prefix is the prefix string need to be added to each file name (e.g. "./", "/", etc.)
Prefix string
}
// BuildTarOption is an option used during building blob.
type BuildTarOption func(o *BuildTarOptions)
// WithPrefix is an option to add a prefix string to each file name (e.g. "./", "/", etc.)
func WithPrefix(prefix string) BuildTarOption {
return func(o *BuildTarOptions) {
o.Prefix = prefix
}
}
// BuildTar builds a tar blob
func BuildTar(ents []TarEntry, opts ...BuildTarOption) io.Reader {
var bo BuildTarOptions
for _, o := range opts {
o(&bo)
}
pr, pw := io.Pipe()
go func() {
tw := tar.NewWriter(pw)
for _, ent := range ents {
if err := ent.AppendTar(tw, bo); err != nil {
pw.CloseWithError(err)
return
}
}
if err := tw.Close(); err != nil {
pw.CloseWithError(err)
return
}
pw.Close()
}()
return pr
}
type tarEntryFunc func(*tar.Writer, BuildTarOptions) error
func (f tarEntryFunc) AppendTar(tw *tar.Writer, opts BuildTarOptions) error { return f(tw, opts) }
// DirectoryBuildTarOption is an option for a directory entry.
type DirectoryBuildTarOption func(o *dirOpts)
type dirOpts struct {
uid int
gid int
xattrs map[string]string
mode *os.FileMode
modTime time.Time
}
// WithFileModTime specifies the modtime of the dir.
func WithDirModTime(modTime time.Time) DirectoryBuildTarOption {
return func(o *dirOpts) {
o.modTime = modTime
}
}
// WithDirOwner specifies the owner of the directory.
func WithDirOwner(uid, gid int) DirectoryBuildTarOption {
return func(o *dirOpts) {
o.uid = uid
o.gid = gid
}
}
// WithDirXattrs specifies the extended attributes of the directory.
func WithDirXattrs(xattrs map[string]string) DirectoryBuildTarOption {
return func(o *dirOpts) {
o.xattrs = xattrs
}
}
// WithDirMode specifies the mode of the directory.
func WithDirMode(mode os.FileMode) DirectoryBuildTarOption {
return func(o *dirOpts) {
o.mode = &mode
}
}
// Dir is a directory entry
func Dir(name string, opts ...DirectoryBuildTarOption) TarEntry {
return tarEntryFunc(func(tw *tar.Writer, buildOpts BuildTarOptions) error {
var dOpts dirOpts
for _, o := range opts {
o(&dOpts)
}
if !strings.HasSuffix(name, "/") {
panic(fmt.Sprintf("missing trailing slash in dir %q ", name))
}
var mode int64 = 0755
if dOpts.mode != nil {
mode = permAndExtraMode2TarMode(*dOpts.mode)
}
return tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeDir,
Name: buildOpts.Prefix + name,
Mode: mode,
ModTime: dOpts.modTime,
Xattrs: dOpts.xattrs,
Uid: dOpts.uid,
Gid: dOpts.gid,
})
})
}
// FileBuildTarOption is an option for a file entry.
type FileBuildTarOption func(o *fileOpts)
type fileOpts struct {
uid int
gid int
xattrs map[string]string
mode *os.FileMode
modTime time.Time
}
// WithFileOwner specifies the owner of the file.
func WithFileOwner(uid, gid int) FileBuildTarOption {
return func(o *fileOpts) {
o.uid = uid
o.gid = gid
}
}
// WithFileXattrs specifies the extended attributes of the file.
func WithFileXattrs(xattrs map[string]string) FileBuildTarOption {
return func(o *fileOpts) {
o.xattrs = xattrs
}
}
// WithFileModTime specifies the modtime of the file.
func WithFileModTime(modTime time.Time) FileBuildTarOption {
return func(o *fileOpts) {
o.modTime = modTime
}
}
// WithFileMode specifies the mode of the file.
func WithFileMode(mode os.FileMode) FileBuildTarOption {
return func(o *fileOpts) {
o.mode = &mode
}
}
// File is a regilar file entry
func File(name, contents string, opts ...FileBuildTarOption) TarEntry {
return tarEntryFunc(func(tw *tar.Writer, buildOpts BuildTarOptions) error {
var fOpts fileOpts
for _, o := range opts {
o(&fOpts)
}
if strings.HasSuffix(name, "/") {
return fmt.Errorf("bogus trailing slash in file %q", name)
}
var mode int64 = 0644
if fOpts.mode != nil {
mode = permAndExtraMode2TarMode(*fOpts.mode)
}
if err := tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeReg,
Name: buildOpts.Prefix + name,
Mode: mode,
ModTime: fOpts.modTime,
Xattrs: fOpts.xattrs,
Size: int64(len(contents)),
Uid: fOpts.uid,
Gid: fOpts.gid,
}); err != nil {
return err
}
_, err := io.WriteString(tw, contents)
return err
})
}
// Symlink is a symlink entry
func Symlink(name, target string) TarEntry {
return tarEntryFunc(func(tw *tar.Writer, buildOpts BuildTarOptions) error {
return tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeSymlink,
Name: buildOpts.Prefix + name,
Linkname: target,
Mode: 0644,
})
})
}
// Link is a hard-link entry
func Link(name, linkname string) TarEntry {
now := time.Now()
return tarEntryFunc(func(w *tar.Writer, buildOpts BuildTarOptions) error {
return w.WriteHeader(&tar.Header{
Typeflag: tar.TypeLink,
Name: buildOpts.Prefix + name,
Linkname: linkname,
ModTime: now,
AccessTime: now,
ChangeTime: now,
})
})
}
// Chardev is a character device entry
func Chardev(name string, major, minor int64) TarEntry {
now := time.Now()
return tarEntryFunc(func(w *tar.Writer, buildOpts BuildTarOptions) error {
return w.WriteHeader(&tar.Header{
Typeflag: tar.TypeChar,
Name: buildOpts.Prefix + name,
Devmajor: major,
Devminor: minor,
ModTime: now,
AccessTime: now,
ChangeTime: now,
})
})
}
// Blockdev is a block device entry
func Blockdev(name string, major, minor int64) TarEntry {
now := time.Now()
return tarEntryFunc(func(w *tar.Writer, buildOpts BuildTarOptions) error {
return w.WriteHeader(&tar.Header{
Typeflag: tar.TypeBlock,
Name: buildOpts.Prefix + name,
Devmajor: major,
Devminor: minor,
ModTime: now,
AccessTime: now,
ChangeTime: now,
})
})
}
// Fifo is a fifo entry
func Fifo(name string) TarEntry {
now := time.Now()
return tarEntryFunc(func(w *tar.Writer, buildOpts BuildTarOptions) error {
return w.WriteHeader(&tar.Header{
Typeflag: tar.TypeFifo,
Name: buildOpts.Prefix + name,
ModTime: now,
AccessTime: now,
ChangeTime: now,
})
})
}
// suid, guid, sticky bits for archive/tar
// https://github.com/golang/go/blob/release-branch.go1.13/src/archive/tar/common.go#L607-L609
const (
cISUID = 04000 // Set uid
cISGID = 02000 // Set gid
cISVTX = 01000 // Save text (sticky bit)
)
func permAndExtraMode2TarMode(fm os.FileMode) (tm int64) {
tm = int64(fm & os.ModePerm)
if fm&os.ModeSetuid != 0 {
tm |= cISUID
}
if fm&os.ModeSetgid != 0 {
tm |= cISGID
}
if fm&os.ModeSticky != 0 {
tm |= cISVTX
}
return
}

View File

@@ -9,6 +9,3 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

View File

@@ -1,24 +0,0 @@
dist: bionic
language: go
go:
- "1.13.x"
- "1.15.x"
install:
# Don't change local go.{mod, sum} by go get tools.
#
# ref: https://github.com/golang/go/issues/27643
- pushd ..; go get -u github.com/vbatts/git-validation; popd
- pushd ..; go get -u github.com/kunalkushwaha/ltag; popd
before_script:
- pushd ..; git clone https://github.com/containerd/project; popd
script:
- DCO_VERBOSITY=-q ../project/script/validate/dco
- ../project/script/validate/fileheader ../project/
- go test -v -race -covermode=atomic -coverprofile=coverage.txt ./...
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -1,6 +1,7 @@
# ttrpc
[![Build Status](https://travis-ci.org/containerd/ttrpc.svg?branch=master)](https://travis-ci.org/containerd/ttrpc)
[![Build Status](https://github.com/containerd/ttrpc/workflows/CI/badge.svg)](https://github.com/containerd/ttrpc/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/containerd/ttrpc/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/ttrpc)
GRPC for low-memory environments.
@@ -40,13 +41,8 @@ directly, if required.
# Status
Very new. YMMV.
TODO:
- [X] Plumb error codes and GRPC status
- [X] Remove use of any type and dependency on typeurl package
- [X] Ensure that protocol can support streaming in the future
- [ ] Document protocol layout
- [ ] Add testing under concurrent load to ensure
- [ ] Verify connection error handling
@@ -55,8 +51,8 @@ TODO:
ttrpc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@@ -19,11 +19,11 @@ package ttrpc
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"net"
"sync"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -105,7 +105,7 @@ func (ch *channel) recv() (messageHeader, []byte, error) {
if mh.Length > uint32(messageLengthMax) {
if _, err := ch.br.Discard(int(mh.Length)); err != nil {
return mh, nil, errors.Wrapf(err, "failed to discard after receiving oversized message")
return mh, nil, fmt.Errorf("failed to discard after receiving oversized message: %w", err)
}
return mh, nil, status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", mh.Length, messageLengthMax)
@@ -113,7 +113,7 @@ func (ch *channel) recv() (messageHeader, []byte, error) {
p := ch.getmbuf(int(mh.Length))
if _, err := io.ReadFull(ch.br, p); err != nil {
return messageHeader{}, nil, errors.Wrapf(err, "failed reading message")
return messageHeader{}, nil, fmt.Errorf("failed reading message: %w", err)
}
return mh, p, nil

View File

@@ -18,6 +18,7 @@ package ttrpc
import (
"context"
"errors"
"io"
"net"
"os"
@@ -27,7 +28,6 @@ import (
"time"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -194,72 +194,131 @@ type message struct {
err error
}
type receiver struct {
wg *sync.WaitGroup
messages chan *message
err error
// callMap provides access to a map of active calls, guarded by a mutex.
type callMap struct {
m sync.Mutex
activeCalls map[uint32]*callRequest
closeErr error
}
func (r *receiver) run(ctx context.Context, c *channel) {
defer r.wg.Done()
for {
select {
case <-ctx.Done():
r.err = ctx.Err()
return
default:
mh, p, err := c.recv()
if err != nil {
_, ok := status.FromError(err)
if !ok {
// treat all errors that are not an rpc status as terminal.
// all others poison the connection.
r.err = filterCloseErr(err)
return
}
}
select {
case r.messages <- &message{
messageHeader: mh,
p: p[:mh.Length],
err: err,
}:
case <-ctx.Done():
r.err = ctx.Err()
return
}
}
// newCallMap returns a new callMap with an empty set of active calls.
func newCallMap() *callMap {
return &callMap{
activeCalls: make(map[uint32]*callRequest),
}
}
// set adds a call entry to the map with the given streamID key.
func (cm *callMap) set(streamID uint32, cr *callRequest) error {
cm.m.Lock()
defer cm.m.Unlock()
if cm.closeErr != nil {
return cm.closeErr
}
cm.activeCalls[streamID] = cr
return nil
}
// get looks up the call entry for the given streamID key, then removes it
// from the map and returns it.
func (cm *callMap) get(streamID uint32) (cr *callRequest, ok bool, err error) {
cm.m.Lock()
defer cm.m.Unlock()
if cm.closeErr != nil {
return nil, false, cm.closeErr
}
cr, ok = cm.activeCalls[streamID]
if ok {
delete(cm.activeCalls, streamID)
}
return
}
// abort sends the given error to each active call, and clears the map.
// Once abort has been called, any subsequent calls to the callMap will return the error passed to abort.
func (cm *callMap) abort(err error) error {
cm.m.Lock()
defer cm.m.Unlock()
if cm.closeErr != nil {
return cm.closeErr
}
for streamID, call := range cm.activeCalls {
call.errs <- err
delete(cm.activeCalls, streamID)
}
cm.closeErr = err
return nil
}
func (c *Client) run() {
var (
streamID uint32 = 1
waiters = make(map[uint32]*callRequest)
calls = c.calls
incoming = make(chan *message)
receiversDone = make(chan struct{})
wg sync.WaitGroup
waiters = newCallMap()
receiverDone = make(chan struct{})
)
// broadcast the shutdown error to the remaining waiters.
abortWaiters := func(wErr error) {
for _, waiter := range waiters {
waiter.errs <- wErr
}
}
recv := &receiver{
wg: &wg,
messages: incoming,
}
wg.Add(1)
// Sender goroutine
// Receives calls from dispatch, adds them to the set of active calls, and sends them
// to the server.
go func() {
wg.Wait()
close(receiversDone)
var streamID uint32 = 1
for {
select {
case <-c.ctx.Done():
return
case call := <-c.calls:
id := streamID
streamID += 2 // enforce odd client initiated request ids
if err := waiters.set(id, call); err != nil {
call.errs <- err // errs is buffered so should not block.
continue
}
if err := c.send(id, messageTypeRequest, call.req); err != nil {
call.errs <- err // errs is buffered so should not block.
waiters.get(id) // remove from waiters set
}
}
}
}()
// Receiver goroutine
// Receives responses from the server, looks up the call info in the set of active calls,
// and notifies the caller of the response.
go func() {
defer close(receiverDone)
for {
select {
case <-c.ctx.Done():
c.setError(c.ctx.Err())
return
default:
mh, p, err := c.channel.recv()
if err != nil {
_, ok := status.FromError(err)
if !ok {
// treat all errors that are not an rpc status as terminal.
// all others poison the connection.
c.setError(filterCloseErr(err))
return
}
}
msg := &message{
messageHeader: mh,
p: p[:mh.Length],
err: err,
}
call, ok, err := waiters.get(mh.StreamID)
if err != nil {
logrus.Errorf("ttrpc: failed to look up active call: %s", err)
continue
}
if !ok {
logrus.Errorf("ttrpc: received message for unknown channel %v", mh.StreamID)
continue
}
call.errs <- c.recv(call.resp, msg)
}
}
}()
go recv.run(c.ctx, c.channel)
defer func() {
c.conn.Close()
@@ -269,32 +328,14 @@ func (c *Client) run() {
for {
select {
case call := <-calls:
if err := c.send(streamID, messageTypeRequest, call.req); err != nil {
call.errs <- err
continue
}
waiters[streamID] = call
streamID += 2 // enforce odd client initiated request ids
case msg := <-incoming:
call, ok := waiters[msg.StreamID]
if !ok {
logrus.Errorf("ttrpc: received message for unknown channel %v", msg.StreamID)
continue
}
call.errs <- c.recv(call.resp, msg)
delete(waiters, msg.StreamID)
case <-receiversDone:
// all the receivers have exited
if recv.err != nil {
c.setError(recv.err)
}
case <-receiverDone:
// The receiver has exited.
// don't return out, let the close of the context trigger the abort of waiters
c.Close()
case <-c.ctx.Done():
abortWaiters(c.error())
// Abort all active calls. This will also prevent any new calls from being added
// to waiters.
waiters.abort(c.error())
return
}
}
@@ -347,7 +388,7 @@ func filterCloseErr(err error) error {
return nil
case err == io.EOF:
return ErrClosed
case errors.Cause(err) == io.EOF:
case errors.Is(err, io.EOF):
return ErrClosed
case strings.Contains(err.Error(), "use of closed network connection"):
return ErrClosed

View File

@@ -17,8 +17,9 @@
package ttrpc
import (
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
)
type codec struct{}
@@ -28,7 +29,7 @@ func (c codec) Marshal(msg interface{}) ([]byte, error) {
case proto.Message:
return proto.Marshal(v)
default:
return nil, errors.Errorf("ttrpc: cannot marshal unknown type: %T", msg)
return nil, fmt.Errorf("ttrpc: cannot marshal unknown type: %T", msg)
}
}
@@ -37,6 +38,6 @@ func (c codec) Unmarshal(p []byte, msg interface{}) error {
case proto.Message:
return proto.Unmarshal(p, v)
default:
return errors.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg)
return fmt.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg)
}
}

View File

@@ -16,7 +16,7 @@
package ttrpc
import "github.com/pkg/errors"
import "errors"
type serverConfig struct {
handshaker Handshaker

View File

@@ -3,12 +3,11 @@ module github.com/containerd/ttrpc
go 1.13
require (
github.com/gogo/protobuf v1.3.1
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1
github.com/sirupsen/logrus v1.4.2
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24
google.golang.org/grpc v1.26.0
github.com/gogo/protobuf v1.3.2
github.com/prometheus/procfs v0.6.0
github.com/sirupsen/logrus v1.8.1
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
google.golang.org/grpc v1.27.1
google.golang.org/protobuf v1.27.1
)

View File

@@ -6,80 +6,94 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1 h1:Lo6mRUjdS99f3zxYOUalftWHUoOGaDRqFk1+j0Q57/I=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 h1:f005F/Jl5JLP036x7QIvUVhNTqxvSYwFIiyOh2q12iU=
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479 h1:LhLiKguPgZL+Tglay4GhVtfF0kb8cvOJ0dHTCBO8YNI=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24 h1:wDju+RU97qa0FZT0QnZDg9Uc2dH0Ql513kFvHocz+WM=
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -18,6 +18,7 @@ package ttrpc
import (
"context"
"errors"
"io"
"math/rand"
"net"
@@ -25,7 +26,6 @@ import (
"sync/atomic"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

View File

@@ -18,13 +18,14 @@ package ttrpc
import (
"context"
"errors"
"fmt"
"io"
"os"
"path"
"unsafe"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -51,7 +52,7 @@ func newServiceSet(interceptor UnaryServerInterceptor) *serviceSet {
func (s *serviceSet) register(name string, methods map[string]Method) {
if _, ok := s.services[name]; ok {
panic(errors.Errorf("duplicate service %v registered", name))
panic(fmt.Errorf("duplicate service %v registered", name))
}
s.services[name] = ServiceDesc{
@@ -116,12 +117,12 @@ func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName strin
func (s *serviceSet) resolve(service, method string) (Method, error) {
srv, ok := s.services[service]
if !ok {
return nil, status.Errorf(codes.NotFound, "service %v", service)
return nil, status.Errorf(codes.Unimplemented, "service %v", service)
}
mthd, ok := srv.Methods[method]
if !ok {
return nil, status.Errorf(codes.NotFound, "method %v", method)
return nil, status.Errorf(codes.Unimplemented, "method %v", method)
}
return mthd, nil

View File

@@ -18,11 +18,12 @@ package ttrpc
import (
"context"
"errors"
"fmt"
"net"
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -31,12 +32,12 @@ type UnixCredentialsFunc func(*unix.Ucred) error
func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
uc, err := requireUnixSocket(conn)
if err != nil {
return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: require unix socket")
return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: require unix socket: %w", err)
}
rs, err := uc.SyscallConn()
if err != nil {
return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed")
return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed: %w", err)
}
var (
ucred *unix.Ucred
@@ -45,15 +46,15 @@ func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net
if err := rs.Control(func(fd uintptr) {
ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED)
}); err != nil {
return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed")
return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed: %w", err)
}
if ucredErr != nil {
return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials")
return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials: %w", err)
}
if err := fn(ucred); err != nil {
return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: credential check failed")
return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: credential check failed: %w", err)
}
return uc, ucred, nil
@@ -93,7 +94,7 @@ func requireRoot(ucred *unix.Ucred) error {
func requireUidGid(ucred *unix.Ucred, uid, gid int) error {
if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) {
return errors.Wrap(syscall.EPERM, "ttrpc: invalid credentials")
return fmt.Errorf("ttrpc: invalid credentials: %v", syscall.EPERM)
}
return nil
}

32
vendor/github.com/goccy/go-json/.codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,32 @@
codecov:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project:
default:
target: 70%
threshold: 2%
patch: off
changes: no
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "header,diff"
behavior: default
require_changes: no
ignore:
- internal/encoder/vm_color
- internal/encoder/vm_color_indent

2
vendor/github.com/goccy/go-json/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
cover.html
cover.out

75
vendor/github.com/goccy/go-json/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,75 @@
run:
skip-files:
- encode_optype.go
- ".*_test\\.go$"
linters-settings:
govet:
enable-all: true
disable:
- shadow
linters:
enable-all: true
disable:
- dogsled
- dupl
- exhaustive
- exhaustivestruct
- errorlint
- forbidigo
- funlen
- gci
- gochecknoglobals
- gochecknoinits
- gocognit
- gocritic
- gocyclo
- godot
- godox
- goerr113
- gofumpt
- gomnd
- gosec
- ifshort
- lll
- makezero
- nakedret
- nestif
- nlreturn
- paralleltest
- testpackage
- thelper
- wrapcheck
- interfacer
- lll
- nakedret
- nestif
- nlreturn
- testpackage
- wsl
issues:
exclude-rules:
# not needed
- path: /*.go
text: "ST1003: should not use underscores in package names"
linters:
- stylecheck
- path: /*.go
text: "don't use an underscore in package name"
linters:
- golint
- path: rtype.go
linters:
- golint
- stylecheck
- path: error.go
linters:
- staticcheck
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

268
vendor/github.com/goccy/go-json/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,268 @@
# v0.7.10 - 2021/10/16
* Fix conversion from pointer to uint64 ( #294 )
# v0.7.9 - 2021/09/28
* Fix encoding of nil value about interface type that has method ( #291 )
# v0.7.8 - 2021/09/01
* Fix mapassign_faststr for indirect struct type ( #283 )
* Fix encoding of not empty interface type ( #284 )
* Fix encoding of empty struct interface type ( #286 )
# v0.7.7 - 2021/08/25
* Fix invalid utf8 on stream decoder ( #279 )
* Fix buffer length bug on string stream decoder ( #280 )
Thank you @orisano !!
# v0.7.6 - 2021/08/13
* Fix nil slice assignment ( #276 )
* Improve error message ( #277 )
# v0.7.5 - 2021/08/12
* Fix encoding of embedded struct with tags ( #265 )
* Fix encoding of embedded struct that isn't first field ( #272 )
* Fix decoding of binary type with escaped char ( #273 )
# v0.7.4 - 2021/07/06
* Fix encoding of indirect layout structure ( #264 )
# v0.7.3 - 2021/06/29
* Fix encoding of pointer type in empty interface ( #262 )
# v0.7.2 - 2021/06/26
### Fix decoder
* Add decoder for func type to fix decoding of nil function value ( #257 )
* Fix stream decoding of []byte type ( #258 )
### Performance
* Improve decoding performance of map[string]interface{} type ( use `mapassign_faststr` ) ( #256 )
* Improve encoding performance of empty interface type ( remove recursive calling of `vm.Run` ) ( #259 )
### Benchmark
* Add bytedance/sonic as benchmark target ( #254 )
# v0.7.1 - 2021/06/18
### Fix decoder
* Fix error when unmarshal empty array ( #253 )
# v0.7.0 - 2021/06/12
### Support context for MarshalJSON and UnmarshalJSON ( #248 )
* json.MarshalContext(context.Context, interface{}, ...json.EncodeOption) ([]byte, error)
* json.NewEncoder(io.Writer).EncodeContext(context.Context, interface{}, ...json.EncodeOption) error
* json.UnmarshalContext(context.Context, []byte, interface{}, ...json.DecodeOption) error
* json.NewDecoder(io.Reader).DecodeContext(context.Context, interface{}) error
```go
type MarshalerContext interface {
MarshalJSON(context.Context) ([]byte, error)
}
type UnmarshalerContext interface {
UnmarshalJSON(context.Context, []byte) error
}
```
### Add DecodeFieldPriorityFirstWin option ( #242 )
In the default behavior, go-json, like encoding/json, will reflect the result of the last evaluation when a field with the same name exists. I've added new options to allow you to change this behavior. `json.DecodeFieldPriorityFirstWin` option reflects the result of the first evaluation if a field with the same name exists. This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated.
### Fix encoder
* Fix indent number contains recursive type ( #249 )
* Fix encoding of using empty interface as map key ( #244 )
### Fix decoder
* Fix decoding fields containing escaped characters ( #237 )
### Refactor
* Move some tests to subdirectory ( #243 )
* Refactor package layout for decoder ( #238 )
# v0.6.1 - 2021/06/02
### Fix encoder
* Fix value of totalLength for encoding ( #236 )
# v0.6.0 - 2021/06/01
### Support Colorize option for encoding (#233)
```go
b, err := json.MarshalWithOption(v, json.Colorize(json.DefaultColorScheme))
if err != nil {
...
}
fmt.Println(string(b)) // print colored json
```
### Refactor
* Fix opcode layout - Adjust memory layout of the opcode to 128 bytes in a 64-bit environment ( #230 )
* Refactor encode option ( #231 )
* Refactor escape string ( #232 )
# v0.5.1 - 2021/5/20
### Optimization
* Add type addrShift to enable bigger encoder/decoder cache ( #213 )
### Fix decoder
* Keep original reference of slice element ( #229 )
### Refactor
* Refactor Debug mode for encoding ( #226 )
* Generate VM sources for encoding ( #227 )
* Refactor validator for null/true/false for decoding ( #221 )
# v0.5.0 - 2021/5/9
### Supports using omitempty and string tags at the same time ( #216 )
### Fix decoder
* Fix stream decoder for unicode char ( #215 )
* Fix decoding of slice element ( #219 )
* Fix calculating of buffer length for stream decoder ( #220 )
### Refactor
* replace skipWhiteSpace goto by loop ( #212 )
# v0.4.14 - 2021/5/4
### Benchmark
* Add valyala/fastjson to benchmark ( #193 )
* Add benchmark task for CI ( #211 )
### Fix decoder
* Fix decoding of slice with unmarshal json type ( #198 )
* Fix decoding of null value for interface type that does not implement Unmarshaler ( #205 )
* Fix decoding of null value to []byte by json.Unmarshal ( #206 )
* Fix decoding of backslash char at the end of string ( #207 )
* Fix stream decoder for null/true/false value ( #208 )
* Fix stream decoder for slow reader ( #211 )
### Performance
* If cap of slice is enough, reuse slice data for compatibility with encoding/json ( #200 )
# v0.4.13 - 2021/4/20
### Fix json.Compact and json.Indent
* Support validation the input buffer for json.Compact and json.Indent ( #189 )
* Optimize json.Compact and json.Indent ( improve memory footprint ) ( #190 )
# v0.4.12 - 2021/4/15
### Fix encoder
* Fix unnecessary indent for empty slice type ( #181 )
* Fix encoding of omitempty feature for the slice or interface type ( #183 )
* Fix encoding custom types zero values with omitempty when marshaller exists ( #187 )
### Fix decoder
* Fix decoder for invalid top level value ( #184 )
* Fix decoder for invalid number value ( #185 )
# v0.4.11 - 2021/4/3
* Improve decoder performance for interface type
# v0.4.10 - 2021/4/2
### Fix encoder
* Fixed a bug when encoding slice and map containing recursive structures
* Fixed a logic to determine if indirect reference
# v0.4.9 - 2021/3/29
### Add debug mode
If you use `json.MarshalWithOption(v, json.Debug())` and `panic` occurred in `go-json`, produces debug information to console.
### Support a new feature to compatible with encoding/json
- invalid UTF-8 is coerced to valid UTF-8 ( without performance down )
### Fix encoder
- Fixed handling of MarshalJSON of function type
### Fix decoding of slice of pointer type
If there is a pointer value, go-json will use it. (This behavior is necessary to achieve the ability to prioritize pre-filled values). However, since slices are reused internally, there was a bug that referred to the previous pointer value. Therefore, it is not necessary to refer to the pointer value in advance for the slice element, so we explicitly initialize slice element by `nil`.
# v0.4.8 - 2021/3/21
### Reduce memory usage at compile time
* go-json have used about 2GB of memory at compile time, but now it can compile with about less than 550MB.
### Fix any encoder's bug
* Add many test cases for encoder
* Fix composite type ( slice/array/map )
* Fix pointer types
* Fix encoding of MarshalJSON or MarshalText or json.Number type
### Refactor encoder
* Change package layout for reducing memory usage at compile
* Remove anonymous and only operation
* Remove root property from encodeCompileContext and opcode
### Fix CI
* Add Go 1.16
* Remove Go 1.13
* Fix `make cover` task
### Number/Delim/Token/RawMessage use the types defined in encoding/json by type alias
# v0.4.7 - 2021/02/22
### Fix decoder
* Fix decoding of deep recursive structure
* Fix decoding of embedded unexported pointer field
* Fix invalid test case
* Fix decoding of invalid value
* Fix decoding of prefilled value
* Fix not being able to return UnmarshalTypeError when it should be returned
* Fix decoding of null value
* Fix decoding of type of null string
* Use pre allocated pointer if exists it at decoding
### Reduce memory usage at compile
* Integrate int/int8/int16/int32/int64 and uint/uint8/uint16/uint32/uint64 operation to reduce memory usage at compile
### Remove unnecessary optype

21
vendor/github.com/goccy/go-json/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Masaaki Goshima
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

39
vendor/github.com/goccy/go-json/Makefile generated vendored Normal file
View File

@@ -0,0 +1,39 @@
PKG := github.com/goccy/go-json
BIN_DIR := $(CURDIR)/bin
PKGS := $(shell go list ./... | grep -v internal/cmd|grep -v test)
COVER_PKGS := $(foreach pkg,$(PKGS),$(subst $(PKG),.,$(pkg)))
COMMA := ,
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
COVERPKG_OPT := $(subst $(SPACE),$(COMMA),$(COVER_PKGS))
$(BIN_DIR):
@mkdir -p $(BIN_DIR)
.PHONY: cover
cover:
go test -coverpkg=$(COVERPKG_OPT) -coverprofile=cover.out ./...
.PHONY: cover-html
cover-html: cover
go tool cover -html=cover.out
.PHONY: lint
lint: golangci-lint
golangci-lint run
golangci-lint: | $(BIN_DIR)
@{ \
set -e; \
GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \
cd $$GOLANGCI_LINT_TMP_DIR; \
go mod init tmp; \
GOBIN=$(BIN_DIR) go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0; \
rm -rf $$GOLANGCI_LINT_TMP_DIR; \
}
.PHONY: generate
generate:
go generate ./internal/...

Some files were not shown because too many files have changed in this diff Show More