mirror of https://github.com/k3s-io/k3s
bump up RootlessKit
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>pull/3106/head
parent
9bae285bfd
commit
11ef43011a
13
go.mod
13
go.mod
|
@ -23,6 +23,7 @@ replace (
|
||||||
github.com/juju/errors => github.com/k3s-io/nocode v0.0.0-20200630202308-cb097102c09f
|
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.20.0-k3s1
|
github.com/kubernetes-sigs/cri-tools => github.com/k3s-io/cri-tools v1.20.0-k3s1
|
||||||
github.com/matryer/moq => github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009
|
github.com/matryer/moq => github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009
|
||||||
|
github.com/moby/sys/mountinfo => github.com/moby/sys/mountinfo v0.1.3
|
||||||
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92
|
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92
|
||||||
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
||||||
go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
||||||
|
@ -79,7 +80,7 @@ require (
|
||||||
github.com/go-bindata/go-bindata v3.1.2+incompatible
|
github.com/go-bindata/go-bindata v3.1.2+incompatible
|
||||||
github.com/go-sql-driver/mysql v1.4.1
|
github.com/go-sql-driver/mysql v1.4.1
|
||||||
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
|
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
|
||||||
github.com/google/uuid v1.1.2
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/k3s-io/helm-controller v0.8.3
|
github.com/k3s-io/helm-controller v0.8.3
|
||||||
|
@ -99,16 +100,16 @@ require (
|
||||||
github.com/rancher/wrangler v0.6.1
|
github.com/rancher/wrangler v0.6.1
|
||||||
github.com/rancher/wrangler-api v0.6.0
|
github.com/rancher/wrangler-api v0.6.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rootless-containers/rootlesskit v0.10.0
|
github.com/rootless-containers/rootlesskit v0.14.0
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
|
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
|
||||||
github.com/urfave/cli v1.22.2
|
github.com/urfave/cli v1.22.2
|
||||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
go.etcd.io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4
|
||||||
google.golang.org/grpc v1.33.2
|
google.golang.org/grpc v1.33.2
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
k8s.io/api v0.19.0
|
k8s.io/api v0.19.0
|
||||||
|
|
47
go.sum
47
go.sum
|
@ -253,6 +253,7 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||||
|
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
@ -353,8 +354,8 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uH
|
||||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
|
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
|
||||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||||
github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c=
|
github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c=
|
||||||
github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||||
|
@ -413,8 +414,9 @@ github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0X
|
||||||
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs=
|
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
@ -429,7 +431,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
@ -476,6 +477,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||||
github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
|
github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
|
||||||
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
|
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
@ -483,7 +485,7 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
|
github.com/insomniacslk/dhcp v0.0.0-20210120172423-cc9239ac6294/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
|
||||||
github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
|
github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
|
||||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||||
github.com/jamescun/tuntap v0.0.0-20190712092105-cb1fb277045c/go.mod h1:zzwpsgcYhzzIP5WyF8g9ivCv38cY9uAV9Gu0m3lThhE=
|
github.com/jamescun/tuntap v0.0.0-20190712092105-cb1fb277045c/go.mod h1:zzwpsgcYhzzIP5WyF8g9ivCv38cY9uAV9Gu0m3lThhE=
|
||||||
|
@ -497,6 +499,10 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
|
||||||
github.com/joho/godotenv v0.0.0-20161216230537-726cc8b906e3/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v0.0.0-20161216230537-726cc8b906e3/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
@ -593,7 +599,6 @@ github.com/knative/pkg v0.0.0-20190514205332-5e4512dcb2ca/go.mod h1:7Ijfhw7rfB+H
|
||||||
github.com/knative/serving v0.6.1/go.mod h1:ljvMfwQy2qanaM/8xnBSK4Mz3Vv2NawC2fo5kFRJS1A=
|
github.com/knative/serving v0.6.1/go.mod h1:ljvMfwQy2qanaM/8xnBSK4Mz3Vv2NawC2fo5kFRJS1A=
|
||||||
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.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.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/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=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
@ -642,6 +647,13 @@ github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGw
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||||
|
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||||
|
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||||
|
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||||
|
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||||
|
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||||
|
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||||
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
|
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
@ -676,7 +688,7 @@ github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1Ws
|
||||||
github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
|
github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
|
||||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
||||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||||
github.com/moby/vpnkit v0.4.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ=
|
github.com/moby/vpnkit v0.5.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
@ -720,7 +732,6 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
|
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/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
@ -807,8 +818,8 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rootless-containers/rootlesskit v0.10.0 h1:62HHP8s8qYYcolEtAsuo4GU6qau6pWmcQ1Te+TZTFds=
|
github.com/rootless-containers/rootlesskit v0.14.0 h1:4zfZqDv7JzsVuMkj4ZMB9cs9mQQnyl1gWBsrpOYcmtk=
|
||||||
github.com/rootless-containers/rootlesskit v0.10.0/go.mod h1:OZQfuRPb+2MA1p+hmjHmSmDRv9SdTzlQ3taNA/0d7XM=
|
github.com/rootless-containers/rootlesskit v0.14.0/go.mod h1:nV3TpRISvwhZQSwo0nmQQnxjCxXr3mvrMi0oASLvzcg=
|
||||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021 h1:if3/24+h9Sq6eDx8UUz1SO9cT9tizyIsATfB7b4D3tc=
|
github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021 h1:if3/24+h9Sq6eDx8UUz1SO9cT9tizyIsATfB7b4D3tc=
|
||||||
|
@ -816,8 +827,9 @@ github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvf
|
||||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
|
@ -826,16 +838,15 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
|
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
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.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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
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/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
@ -874,9 +885,9 @@ github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRci
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
|
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
|
||||||
|
@ -891,7 +902,7 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||||
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-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/u-root/u-root v6.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
|
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 v0.0.0-20170107133203-ded73eae5db7/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
@ -900,8 +911,6 @@ github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVU
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
|
|
||||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||||
|
|
|
@ -143,7 +143,7 @@ func createParentOpt(stateDir string) (*parent.Opt, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
debugWriter := &logrusDebugWriter{}
|
debugWriter := &logrusDebugWriter{}
|
||||||
opt.NetworkDriver, err = slirp4netns.NewParentDriver(debugWriter, binary, mtu, ipnet, disableHostLoopback, "", false, false)
|
opt.NetworkDriver, err = slirp4netns.NewParentDriver(debugWriter, binary, mtu, ipnet, "tap0", disableHostLoopback, "", false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.10.x
|
- 1.14.x
|
||||||
- 1.11.x
|
- 1.15.x
|
||||||
script: go test -v -check.vv -race ./...
|
script: go test -v -check.vv -race ./...
|
||||||
sudo: false
|
sudo: false
|
||||||
notifications:
|
notifications:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2015, Tim Heckman
|
Copyright (c) 2015-2020, Tim Heckman
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -11,9 +11,9 @@ modification, are permitted provided that the following conditions are met:
|
||||||
this list of conditions and the following disclaimer in the documentation
|
this list of conditions and the following disclaimer in the documentation
|
||||||
and/or other materials provided with the distribution.
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
* Neither the name of linode-netint nor the names of its
|
* Neither the name of gofrs nor the names of its contributors may be used
|
||||||
contributors may be used to endorse or promote products derived from
|
to endorse or promote products derived from this software without
|
||||||
this software without specific prior written permission.
|
specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# flock
|
# flock
|
||||||
[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock)
|
[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-go--flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock)
|
[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock)
|
||||||
[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE)
|
[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE)
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock)
|
[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ clone_folder: 'c:\gopath\src\github.com\gofrs\flock'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
GOPATH: 'c:\gopath'
|
GOPATH: 'c:\gopath'
|
||||||
GOVERSION: '1.11'
|
GOVERSION: '1.15'
|
||||||
|
|
||||||
init:
|
init:
|
||||||
- git config --global core.autocrlf input
|
- git config --global core.autocrlf input
|
||||||
|
|
|
@ -19,6 +19,7 @@ package flock
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -116,7 +117,15 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati
|
||||||
func (f *Flock) setFh() error {
|
func (f *Flock) setFh() error {
|
||||||
// open a new os.File instance
|
// open a new os.File instance
|
||||||
// create it if it doesn't exist, and open the file read-only.
|
// create it if it doesn't exist, and open the file read-only.
|
||||||
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600))
|
flags := os.O_CREATE
|
||||||
|
if runtime.GOOS == "aix" {
|
||||||
|
// AIX cannot preform write-lock (ie exclusive) on a
|
||||||
|
// read-only file.
|
||||||
|
flags |= os.O_RDWR
|
||||||
|
} else {
|
||||||
|
flags |= os.O_RDONLY
|
||||||
|
}
|
||||||
|
fh, err := os.OpenFile(f.path, flags, os.FileMode(0600))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -125,3 +134,11 @@ func (f *Flock) setFh() error {
|
||||||
f.fh = fh
|
f.fh = fh
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure the file handle is closed if no lock is held
|
||||||
|
func (f *Flock) ensureFhState() {
|
||||||
|
if !f.l && !f.r && f.fh != nil {
|
||||||
|
f.fh.Close()
|
||||||
|
f.fh = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
// Copyright 2019 Tim Heckman. All rights reserved. Use of this source code is
|
||||||
|
// governed by the BSD 3-Clause license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code implements the filelock API using POSIX 'fcntl' locks, which attach
|
||||||
|
// to an (inode, process) pair rather than a file descriptor. To avoid unlocking
|
||||||
|
// files prematurely when the same file is opened through different descriptors,
|
||||||
|
// we allow only one read-lock at a time.
|
||||||
|
//
|
||||||
|
// This code is adapted from the Go package:
|
||||||
|
// cmd/go/internal/lockedfile/internal/filelock
|
||||||
|
|
||||||
|
//+build aix
|
||||||
|
|
||||||
|
package flock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lockType int16
|
||||||
|
|
||||||
|
const (
|
||||||
|
readLock lockType = unix.F_RDLCK
|
||||||
|
writeLock lockType = unix.F_WRLCK
|
||||||
|
)
|
||||||
|
|
||||||
|
type inode = uint64
|
||||||
|
|
||||||
|
type inodeLock struct {
|
||||||
|
owner *Flock
|
||||||
|
queue []<-chan *Flock
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
inodes = map[*Flock]inode{}
|
||||||
|
locks = map[inode]inodeLock{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lock is a blocking call to try and take an exclusive file lock. It will wait
|
||||||
|
// until it is able to obtain the exclusive file lock. It's recommended that
|
||||||
|
// TryLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already exclusive-locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
//
|
||||||
|
// If the *Flock has a shared lock (RLock), this may transparently replace the
|
||||||
|
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
|
||||||
|
// careful when using exclusive locks in conjunction with shared locks
|
||||||
|
// (RLock()), because calling Unlock() may accidentally release the exclusive
|
||||||
|
// lock that was once a shared lock.
|
||||||
|
func (f *Flock) Lock() error {
|
||||||
|
return f.lock(&f.l, writeLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock is a blocking call to try and take a shared file lock. It will wait
|
||||||
|
// until it is able to obtain the shared file lock. It's recommended that
|
||||||
|
// TryRLock() be used over this function. This function may block the ability to
|
||||||
|
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||||
|
//
|
||||||
|
// If we are already shared-locked, this function short-circuits and returns
|
||||||
|
// immediately assuming it can take the mutex lock.
|
||||||
|
func (f *Flock) RLock() error {
|
||||||
|
return f.lock(&f.r, readLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) lock(locked *bool, flag lockType) error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := f.doLock(flag, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*locked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) doLock(lt lockType, blocking bool) (bool, error) {
|
||||||
|
// POSIX locks apply per inode and process, and the lock for an inode is
|
||||||
|
// released when *any* descriptor for that inode is closed. So we need to
|
||||||
|
// synchronize access to each inode internally, and must serialize lock and
|
||||||
|
// unlock calls that refer to the same inode through different descriptors.
|
||||||
|
fi, err := f.fh.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
ino := inode(fi.Sys().(*syscall.Stat_t).Ino)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if i, dup := inodes[f]; dup && i != ino {
|
||||||
|
mu.Unlock()
|
||||||
|
return false, &os.PathError{
|
||||||
|
Path: f.Path(),
|
||||||
|
Err: errors.New("inode for file changed since last Lock or RLock"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inodes[f] = ino
|
||||||
|
|
||||||
|
var wait chan *Flock
|
||||||
|
l := locks[ino]
|
||||||
|
if l.owner == f {
|
||||||
|
// This file already owns the lock, but the call may change its lock type.
|
||||||
|
} else if l.owner == nil {
|
||||||
|
// No owner: it's ours now.
|
||||||
|
l.owner = f
|
||||||
|
} else if !blocking {
|
||||||
|
// Already owned: cannot take the lock.
|
||||||
|
mu.Unlock()
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
// Already owned: add a channel to wait on.
|
||||||
|
wait = make(chan *Flock)
|
||||||
|
l.queue = append(l.queue, wait)
|
||||||
|
}
|
||||||
|
locks[ino] = l
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
if wait != nil {
|
||||||
|
wait <- f
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setlkw(f.fh.Fd(), lt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
f.doUnlock()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) Unlock() error {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
// if we aren't locked or if the lockfile instance is nil
|
||||||
|
// just return a nil error because we are unlocked
|
||||||
|
if (!f.l && !f.r) || f.fh == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f.doUnlock(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fh.Close()
|
||||||
|
|
||||||
|
f.l = false
|
||||||
|
f.r = false
|
||||||
|
f.fh = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) doUnlock() (err error) {
|
||||||
|
var owner *Flock
|
||||||
|
mu.Lock()
|
||||||
|
ino, ok := inodes[f]
|
||||||
|
if ok {
|
||||||
|
owner = locks[ino].owner
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
if owner == f {
|
||||||
|
err = setlkw(f.fh.Fd(), unix.F_UNLCK)
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
l := locks[ino]
|
||||||
|
if len(l.queue) == 0 {
|
||||||
|
// No waiters: remove the map entry.
|
||||||
|
delete(locks, ino)
|
||||||
|
} else {
|
||||||
|
// The first waiter is sending us their file now.
|
||||||
|
// Receive it and update the queue.
|
||||||
|
l.owner = <-l.queue[0]
|
||||||
|
l.queue = l.queue[1:]
|
||||||
|
locks[ino] = l
|
||||||
|
}
|
||||||
|
delete(inodes, f)
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock is the preferred function for taking an exclusive file lock. This
|
||||||
|
// function takes an RW-mutex lock before it tries to lock the file, so there is
|
||||||
|
// the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the exclusive
|
||||||
|
// file lock, the function will return false instead of waiting for the lock. If
|
||||||
|
// we get the lock, we also set the *Flock instance as being exclusive-locked.
|
||||||
|
func (f *Flock) TryLock() (bool, error) {
|
||||||
|
return f.try(&f.l, writeLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLock is the preferred function for taking a shared file lock. This
|
||||||
|
// function takes an RW-mutex lock before it tries to lock the file, so there is
|
||||||
|
// the possibility that this function may block for a short time if another
|
||||||
|
// goroutine is trying to take any action.
|
||||||
|
//
|
||||||
|
// The actual file lock is non-blocking. If we are unable to get the shared file
|
||||||
|
// lock, the function will return false instead of waiting for the lock. If we
|
||||||
|
// get the lock, we also set the *Flock instance as being share-locked.
|
||||||
|
func (f *Flock) TryRLock() (bool, error) {
|
||||||
|
return f.try(&f.r, readLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flock) try(locked *bool, flag lockType) (bool, error) {
|
||||||
|
f.m.Lock()
|
||||||
|
defer f.m.Unlock()
|
||||||
|
|
||||||
|
if *locked {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.fh == nil {
|
||||||
|
if err := f.setFh(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
|
}
|
||||||
|
|
||||||
|
haslock, err := f.doLock(flag, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
*locked = haslock
|
||||||
|
return haslock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setlkw calls FcntlFlock with F_SETLKW for the entire file indicated by fd.
|
||||||
|
func setlkw(fd uintptr, lt lockType) error {
|
||||||
|
for {
|
||||||
|
err := unix.FcntlFlock(fd, unix.F_SETLKW, &unix.Flock_t{
|
||||||
|
Type: int16(lt),
|
||||||
|
Whence: io.SeekStart,
|
||||||
|
Start: 0,
|
||||||
|
Len: 0, // All bytes.
|
||||||
|
})
|
||||||
|
if err != unix.EINTR {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by the BSD 3-Clause
|
// Use of this source code is governed by the BSD 3-Clause
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !windows
|
// +build !aix,!windows
|
||||||
|
|
||||||
package flock
|
package flock
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ func (f *Flock) lock(locked *bool, flag int) error {
|
||||||
if err := f.setFh(); err != nil {
|
if err := f.setFh(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
|
if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
|
||||||
|
@ -142,6 +143,7 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) {
|
||||||
if err := f.setFh(); err != nil {
|
if err := f.setFh(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
}
|
}
|
||||||
|
|
||||||
var retried bool
|
var retried bool
|
||||||
|
|
|
@ -46,6 +46,7 @@ func (f *Flock) lock(locked *bool, flag uint32) error {
|
||||||
if err := f.setFh(); err != nil {
|
if err := f.setFh(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
|
if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
|
||||||
|
@ -122,6 +123,7 @@ func (f *Flock) try(locked *bool, flag uint32) (bool, error) {
|
||||||
if err := f.setFh(); err != nil {
|
if err := f.setFh(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
defer f.ensureFhState()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
|
_, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
|
||||||
|
|
|
@ -26,8 +26,8 @@ var (
|
||||||
// NewMD5 and NewSHA1.
|
// NewMD5 and NewSHA1.
|
||||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||||
h.Reset()
|
h.Reset()
|
||||||
h.Write(space[:])
|
h.Write(space[:]) //nolint:errcheck
|
||||||
h.Write(data)
|
h.Write(data) //nolint:errcheck
|
||||||
s := h.Sum(nil)
|
s := h.Sum(nil)
|
||||||
var uuid UUID
|
var uuid UUID
|
||||||
copy(uuid[:], s)
|
copy(uuid[:], s)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
|
||||||
// Currently, database types that map to string and []byte are supported. Please
|
// Currently, database types that map to string and []byte are supported. Please
|
||||||
// consult database-specific driver documentation for matching types.
|
// consult database-specific driver documentation for matching types.
|
||||||
func (uuid *UUID) Scan(src interface{}) error {
|
func (uuid *UUID) Scan(src interface{}) error {
|
||||||
|
|
|
@ -35,6 +35,12 @@ const (
|
||||||
|
|
||||||
var rander = rand.Reader // random function
|
var rander = rand.Reader // random function
|
||||||
|
|
||||||
|
type invalidLengthError struct{ len int }
|
||||||
|
|
||||||
|
func (err invalidLengthError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid UUID length: %d", err.len)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
||||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
||||||
|
@ -68,7 +74,7 @@ func Parse(s string) (UUID, error) {
|
||||||
}
|
}
|
||||||
return uuid, nil
|
return uuid, nil
|
||||||
default:
|
default:
|
||||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
|
return uuid, invalidLengthError{len(s)}
|
||||||
}
|
}
|
||||||
// s is now at least 36 bytes long
|
// s is now at least 36 bytes long
|
||||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
@ -112,7 +118,7 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||||
}
|
}
|
||||||
return uuid, nil
|
return uuid, nil
|
||||||
default:
|
default:
|
||||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
|
return uuid, invalidLengthError{len(b)}
|
||||||
}
|
}
|
||||||
// s is now at least 36 bytes long
|
// s is now at least 36 bytes long
|
||||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
|
|
@ -14,6 +14,14 @@ func New() UUID {
|
||||||
return Must(NewRandom())
|
return Must(NewRandom())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewString creates a new random UUID and returns it as a string or panics.
|
||||||
|
// NewString is equivalent to the expression
|
||||||
|
//
|
||||||
|
// uuid.New().String()
|
||||||
|
func NewString() string {
|
||||||
|
return Must(NewRandom()).String()
|
||||||
|
}
|
||||||
|
|
||||||
// NewRandom returns a Random (Version 4) UUID.
|
// NewRandom returns a Random (Version 4) UUID.
|
||||||
//
|
//
|
||||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||||
|
|
|
@ -1,6 +1,36 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Version of the REST API, not implementation version.
|
||||||
|
// See openapi.yaml for the definition.
|
||||||
|
Version = "1.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
// ErrorJSON is returned with "application/json" content type and non-2XX status code
|
// ErrorJSON is returned with "application/json" content type and non-2XX status code
|
||||||
type ErrorJSON struct {
|
type ErrorJSON struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Info is the structure returned by `GET /info`
|
||||||
|
type Info struct {
|
||||||
|
APIVersion string `json:"apiVersion"` // REST API version
|
||||||
|
Version string `json:"version"` // Implementation version
|
||||||
|
StateDir string `json:"stateDir"`
|
||||||
|
ChildPID int `json:"childPID"`
|
||||||
|
NetworkDriver *NetworkDriverInfo `json:"networkDriver,omitempty"`
|
||||||
|
PortDriver *PortDriverInfo `json:"portDriver,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkDriverInfo in Info
|
||||||
|
type NetworkDriverInfo struct {
|
||||||
|
Driver string `json:"driver"`
|
||||||
|
DNS []net.IP `json:"dns,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortDriverInfo in Info
|
||||||
|
type PortDriverInfo struct {
|
||||||
|
Driver string `json:"driver"`
|
||||||
|
Protos []string `json:"protos"`
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
type Client interface {
|
type Client interface {
|
||||||
HTTPClient() *http.Client
|
HTTPClient() *http.Client
|
||||||
PortManager() port.Manager
|
PortManager() port.Manager
|
||||||
|
Info(context.Context) (*api.Info, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a client.
|
// New creates a client.
|
||||||
|
@ -65,6 +66,29 @@ func (c *client) PortManager() port.Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) Info(ctx context.Context) (*api.Info, error) {
|
||||||
|
u := fmt.Sprintf("http://%s/%s/info", c.dummyHost, c.version)
|
||||||
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
resp, err := c.HTTPClient().Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if err := successful(resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var info api.Info
|
||||||
|
dec := json.NewDecoder(resp.Body)
|
||||||
|
if err := dec.Decode(&info); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
func readAtMost(r io.Reader, maxBytes int) ([]byte, error) {
|
func readAtMost(r io.Reader, maxBytes int) ([]byte, error) {
|
||||||
lr := &io.LimitedReader{
|
lr := &io.LimitedReader{
|
||||||
R: r,
|
R: r,
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
# When you made a change to this YAML, please validate with https://editor.swagger.io
|
# When you made a change to this YAML, please validate with https://editor.swagger.io
|
||||||
openapi: 3.0.2
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
version: 1.0.0
|
version: 1.1.0
|
||||||
title: RootlessKit API
|
title: RootlessKit API
|
||||||
servers:
|
servers:
|
||||||
- url: 'http://rootlesskit/v1'
|
- url: 'http://rootlesskit/v1'
|
||||||
description: Local UNIX socket server. The host part of the URL is ignored.
|
description: Local UNIX socket server. The host part of the URL is ignored.
|
||||||
paths:
|
paths:
|
||||||
|
# /info: API >= 1.1.0
|
||||||
|
/info:
|
||||||
|
get:
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Info. Available since API 1.1.0.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Info'
|
||||||
/ports:
|
/ports:
|
||||||
get:
|
get:
|
||||||
responses:
|
responses:
|
||||||
|
@ -44,16 +54,25 @@ paths:
|
||||||
description: Null response
|
description: Null response
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
|
Proto:
|
||||||
|
type: string
|
||||||
|
description: "protocol for listening. Corresponds to Go's net.Listen. The strings with \"4\" and \"6\" suffixes were introduced in API 1.1.0."
|
||||||
|
enum:
|
||||||
|
- tcp
|
||||||
|
- tcp4
|
||||||
|
- tcp6
|
||||||
|
- udp
|
||||||
|
- udp4
|
||||||
|
- udp6
|
||||||
|
- sctp
|
||||||
|
- sctp4
|
||||||
|
- sctp6
|
||||||
PortSpec:
|
PortSpec:
|
||||||
required:
|
required:
|
||||||
- proto
|
- proto
|
||||||
properties:
|
properties:
|
||||||
proto:
|
proto:
|
||||||
type: string
|
$ref: '#/components/schemas/Proto'
|
||||||
enum:
|
|
||||||
- tcp
|
|
||||||
- udp
|
|
||||||
- sctp
|
|
||||||
parentIP:
|
parentIP:
|
||||||
type: string
|
type: string
|
||||||
parentPort:
|
parentPort:
|
||||||
|
@ -61,6 +80,8 @@ components:
|
||||||
format: int32
|
format: int32
|
||||||
minimum: 1
|
minimum: 1
|
||||||
maximum: 65535
|
maximum: 65535
|
||||||
|
childIP:
|
||||||
|
type: string
|
||||||
# future version may support requests with parentPort<=0 for automatic port assignment
|
# future version may support requests with parentPort<=0 for automatic port assignment
|
||||||
childPort:
|
childPort:
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -80,3 +101,61 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/PortStatus'
|
$ref: '#/components/schemas/PortStatus'
|
||||||
|
# Info: API >= 1.1.0
|
||||||
|
Info:
|
||||||
|
required:
|
||||||
|
- apiVersion
|
||||||
|
- version
|
||||||
|
- stateDir
|
||||||
|
- childPID
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
type: string
|
||||||
|
description: "API version, without \"v\" prefix"
|
||||||
|
example: "1.1.0"
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
description: "Implementation version, without \"v\" prefix"
|
||||||
|
example: "0.42.0-beta.1+dev"
|
||||||
|
stateDir:
|
||||||
|
type: string
|
||||||
|
description: "state dir"
|
||||||
|
example: "/run/user/1000/rootlesskit"
|
||||||
|
childPID:
|
||||||
|
type: integer
|
||||||
|
description: "child PID"
|
||||||
|
example: 10042
|
||||||
|
networkDriver:
|
||||||
|
$ref: '#/components/schemas/NetworkDriverInfo'
|
||||||
|
portDriver:
|
||||||
|
$ref: '#/components/schemas/PortDriverInfo'
|
||||||
|
NetworkDriverInfo:
|
||||||
|
required:
|
||||||
|
- driver
|
||||||
|
properties:
|
||||||
|
driver:
|
||||||
|
type: string
|
||||||
|
description: "network driver. Empty when --net=host."
|
||||||
|
example: "slirp4netns"
|
||||||
|
# TODO: return TAP info
|
||||||
|
dns:
|
||||||
|
type: array
|
||||||
|
description: "DNS addresses"
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: ["10.0.2.3"]
|
||||||
|
PortDriverInfo:
|
||||||
|
required:
|
||||||
|
- driver
|
||||||
|
- supportedProtos
|
||||||
|
properties:
|
||||||
|
driver:
|
||||||
|
type: string
|
||||||
|
description: "port driver"
|
||||||
|
example: "builtin"
|
||||||
|
protos:
|
||||||
|
type: array
|
||||||
|
description: "The supported protocol strings for listening ports"
|
||||||
|
example: ["tcp","udp"]
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Proto'
|
||||||
|
|
|
@ -11,12 +11,28 @@ import (
|
||||||
|
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/api"
|
"github.com/rootless-containers/rootlesskit/pkg/api"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port"
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NetworkDriver is implemented by network.ParentDriver
|
||||||
|
type NetworkDriver interface {
|
||||||
|
Info(context.Context) (*api.NetworkDriverInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortDriver is implemented by port.ParentDriver
|
||||||
|
type PortDriver interface {
|
||||||
|
Info(context.Context) (*api.PortDriverInfo, error)
|
||||||
|
port.Manager
|
||||||
|
}
|
||||||
|
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
|
StateDir string
|
||||||
|
ChildPID int
|
||||||
|
// NetworkDriver can be nil
|
||||||
|
NetworkDriver NetworkDriver
|
||||||
// PortDriver MUST be thread-safe.
|
// PortDriver MUST be thread-safe.
|
||||||
// PortDriver can be nil
|
// PortDriver can be nil
|
||||||
PortDriver port.ParentDriver
|
PortDriver PortDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) onError(w http.ResponseWriter, r *http.Request, err error, ec int) {
|
func (b *Backend) onError(w http.ResponseWriter, r *http.Request, err error, ec int) {
|
||||||
|
@ -104,9 +120,43 @@ func (b *Backend) DeletePort(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Backend) GetInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info := &api.Info{
|
||||||
|
APIVersion: api.Version,
|
||||||
|
Version: version.Version,
|
||||||
|
StateDir: b.StateDir,
|
||||||
|
ChildPID: b.ChildPID,
|
||||||
|
}
|
||||||
|
if b.NetworkDriver != nil {
|
||||||
|
ndInfo, err := b.NetworkDriver.Info(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
b.onError(w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.NetworkDriver = ndInfo
|
||||||
|
}
|
||||||
|
if b.PortDriver != nil {
|
||||||
|
pdInfo, err := b.PortDriver.Info(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
b.onError(w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.PortDriver = pdInfo
|
||||||
|
}
|
||||||
|
m, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
b.onError(w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(m)
|
||||||
|
}
|
||||||
|
|
||||||
func AddRoutes(r *mux.Router, b *Backend) {
|
func AddRoutes(r *mux.Router, b *Backend) {
|
||||||
v1 := r.PathPrefix("/v1").Subrouter()
|
v1 := r.PathPrefix("/v1").Subrouter()
|
||||||
v1.Path("/ports").Methods("GET").HandlerFunc(b.GetPorts)
|
v1.Path("/ports").Methods("GET").HandlerFunc(b.GetPorts)
|
||||||
v1.Path("/ports").Methods("POST").HandlerFunc(b.PostPort)
|
v1.Path("/ports").Methods("POST").HandlerFunc(b.PostPort)
|
||||||
v1.Path("/ports/{id}").Methods("DELETE").HandlerFunc(b.DeletePort)
|
v1.Path("/ports/{id}").Methods("DELETE").HandlerFunc(b.DeletePort)
|
||||||
|
v1.Path("/info").Methods("GET").HandlerFunc(b.GetInfo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package child
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -50,15 +51,31 @@ func createCmd(targetCmd []string) (*exec.Cmd, error) {
|
||||||
|
|
||||||
// mountSysfs is needed for mounting /sys/class/net
|
// mountSysfs is needed for mounting /sys/class/net
|
||||||
// when netns is unshared.
|
// when netns is unshared.
|
||||||
func mountSysfs() error {
|
func mountSysfs(hostNetwork, evacuateCgroup2 bool) error {
|
||||||
|
const cgroupDir = "/sys/fs/cgroup"
|
||||||
|
if hostNetwork {
|
||||||
|
if evacuateCgroup2 {
|
||||||
|
// We need to mount tmpfs before cgroup2 to avoid EBUSY
|
||||||
|
if err := unix.Mount("none", cgroupDir, "tmpfs", 0, ""); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mount tmpfs on %s", cgroupDir)
|
||||||
|
}
|
||||||
|
if err := unix.Mount("none", cgroupDir, "cgroup2", 0, ""); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mount cgroup2 on %s", cgroupDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOP
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
tmp, err := ioutil.TempDir("/tmp", "rksys")
|
tmp, err := ioutil.TempDir("/tmp", "rksys")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "creating a directory under /tmp")
|
return errors.Wrap(err, "creating a directory under /tmp")
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmp)
|
defer os.RemoveAll(tmp)
|
||||||
cgroupDir := "/sys/fs/cgroup"
|
if !evacuateCgroup2 {
|
||||||
if err := unix.Mount(cgroupDir, tmp, "", uintptr(unix.MS_BIND|unix.MS_REC), ""); err != nil {
|
if err := unix.Mount(cgroupDir, tmp, "", uintptr(unix.MS_BIND|unix.MS_REC), ""); err != nil {
|
||||||
return errors.Wrapf(err, "failed to create bind mount on %s", cgroupDir)
|
return errors.Wrapf(err, "failed to create bind mount on %s", cgroupDir)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unix.Mount("none", "/sys", "sysfs", 0, ""); err != nil {
|
if err := unix.Mount("none", "/sys", "sysfs", 0, ""); err != nil {
|
||||||
|
@ -72,8 +89,14 @@ func mountSysfs() error {
|
||||||
logrus.Warnf("failed to mount sysfs: %v", err)
|
logrus.Warnf("failed to mount sysfs: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := unix.Mount(tmp, cgroupDir, "", uintptr(unix.MS_MOVE), ""); err != nil {
|
if evacuateCgroup2 {
|
||||||
return errors.Wrapf(err, "failed to move mount point from %s to %s", tmp, cgroupDir)
|
if err := unix.Mount("none", cgroupDir, "cgroup2", 0, ""); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mount cgroup2 on %s", cgroupDir)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := unix.Mount(tmp, cgroupDir, "", uintptr(unix.MS_MOVE), ""); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to move mount point from %s to %s", tmp, cgroupDir)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -134,10 +157,6 @@ func setupNet(msg common.Message, etcWasCopied bool, driver network.ChildDriver)
|
||||||
if driver == nil {
|
if driver == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// for /sys/class/net
|
|
||||||
if err := mountSysfs(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := activateLoopback(); err != nil {
|
if err := activateLoopback(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -171,15 +190,16 @@ func setupNet(msg common.Message, etcWasCopied bool, driver network.ChildDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
PipeFDEnvKey string // needs to be set
|
PipeFDEnvKey string // needs to be set
|
||||||
TargetCmd []string // needs to be set
|
TargetCmd []string // needs to be set
|
||||||
NetworkDriver network.ChildDriver // nil for HostNetwork
|
NetworkDriver network.ChildDriver // nil for HostNetwork
|
||||||
CopyUpDriver copyup.ChildDriver // cannot be nil if len(CopyUpDirs) != 0
|
CopyUpDriver copyup.ChildDriver // cannot be nil if len(CopyUpDirs) != 0
|
||||||
CopyUpDirs []string
|
CopyUpDirs []string
|
||||||
PortDriver port.ChildDriver
|
PortDriver port.ChildDriver
|
||||||
MountProcfs bool // needs to be set if (and only if) parent.Opt.CreatePIDNS is set
|
MountProcfs bool // needs to be set if (and only if) parent.Opt.CreatePIDNS is set
|
||||||
Propagation string // mount propagation type
|
Propagation string // mount propagation type
|
||||||
Reaper bool
|
Reaper bool
|
||||||
|
EvacuateCgroup2 bool // needs to correspond to parent.Opt.EvacuateCgroup2 is set
|
||||||
}
|
}
|
||||||
|
|
||||||
func Child(opt Opt) error {
|
func Child(opt Opt) error {
|
||||||
|
@ -234,6 +254,9 @@ func Child(opt Opt) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := mountSysfs(opt.NetworkDriver == nil, opt.EvacuateCgroup2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := setupNet(msg, etcWasCopied, opt.NetworkDriver); err != nil {
|
if err := setupNet(msg, etcWasCopied, opt.NetworkDriver); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -288,6 +311,7 @@ func setMountPropagation(propagation string) error {
|
||||||
func runAndReap(cmd *exec.Cmd) error {
|
func runAndReap(cmd *exec.Cmd) error {
|
||||||
c := make(chan os.Signal, 32)
|
c := make(chan os.Signal, 32)
|
||||||
signal.Notify(c, syscall.SIGCHLD)
|
signal.Notify(c, syscall.SIGCHLD)
|
||||||
|
cmd.SysProcAttr.Setsid = true
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -297,17 +321,54 @@ func runAndReap(cmd *exec.Cmd) error {
|
||||||
result := make(chan error)
|
result := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(result)
|
defer close(result)
|
||||||
for range c {
|
for cEntry := range c {
|
||||||
for {
|
logrus.Debugf("reaper: got signal %q", cEntry)
|
||||||
if pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); err != nil || pid <= 0 {
|
if wsPtr := reap(cmd.Process.Pid); wsPtr != nil {
|
||||||
break
|
ws := *wsPtr
|
||||||
} else {
|
if ws.Exited() && ws.ExitStatus() == 0 {
|
||||||
if pid == cmd.Process.Pid {
|
result <- nil
|
||||||
result <- cmd.Wait()
|
continue
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var resultErr common.ErrorWithSys = &reaperErr{
|
||||||
|
ws: ws,
|
||||||
|
}
|
||||||
|
result <- resultErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return <-result
|
return <-result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reap(myPid int) *syscall.WaitStatus {
|
||||||
|
var res *syscall.WaitStatus
|
||||||
|
for {
|
||||||
|
var ws syscall.WaitStatus
|
||||||
|
pid, err := syscall.Wait4(-1, &ws, syscall.WNOHANG, nil)
|
||||||
|
logrus.Debugf("reaper: got ws=%+v, pid=%d, err=%+v", ws, pid, err)
|
||||||
|
if err != nil || pid <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if pid == myPid {
|
||||||
|
res = &ws
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type reaperErr struct {
|
||||||
|
ws syscall.WaitStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *reaperErr) Sys() interface{} {
|
||||||
|
return e.ws
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *reaperErr) Error() string {
|
||||||
|
if e.ws.Exited() {
|
||||||
|
return fmt.Sprintf("exit status %d", e.ws.ExitStatus())
|
||||||
|
}
|
||||||
|
if e.ws.Signaled() {
|
||||||
|
return fmt.Sprintf("signal: %s", e.ws.Signal())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("exited with WAITSTATUS=0x%08x", e.ws)
|
||||||
|
}
|
||||||
|
|
|
@ -9,12 +9,18 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrorWithSys is implemented by *exec.ExitError and *child.reaperErr
|
||||||
|
type ErrorWithSys interface {
|
||||||
|
error
|
||||||
|
Sys() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
func GetExecExitStatus(err error) (int, bool) {
|
func GetExecExitStatus(err error) (int, bool) {
|
||||||
err = errors.Cause(err)
|
err = errors.Cause(err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
exitErr, ok := err.(*exec.ExitError)
|
exitErr, ok := err.(ErrorWithSys)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/api"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/common"
|
"github.com/rootless-containers/rootlesskit/pkg/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParentDriver is called from the parent namespace
|
// ParentDriver is called from the parent namespace
|
||||||
type ParentDriver interface {
|
type ParentDriver interface {
|
||||||
|
Info(ctx context.Context) (*api.NetworkDriverInfo, error)
|
||||||
// MTU returns MTU
|
// MTU returns MTU
|
||||||
MTU() int
|
MTU() int
|
||||||
// ConfigureNetwork sets up Slirp, updates msg, and returns destructor function.
|
// ConfigureNetwork sets up Slirp, updates msg, and returns destructor function.
|
||||||
|
|
51
vendor/github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns/slirp4netns.go
generated
vendored
51
vendor/github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns/slirp4netns.go
generated
vendored
|
@ -8,6 +8,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/api"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/common"
|
"github.com/rootless-containers/rootlesskit/pkg/common"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/network"
|
"github.com/rootless-containers/rootlesskit/pkg/network"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/network/iputils"
|
"github.com/rootless-containers/rootlesskit/pkg/network/iputils"
|
||||||
|
@ -22,6 +24,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Features struct {
|
type Features struct {
|
||||||
|
// SupportsEnableIPv6 --enable-ipv6 (v0.2.0)
|
||||||
|
SupportsEnableIPv6 bool
|
||||||
// SupportsCIDR --cidr (v0.3.0)
|
// SupportsCIDR --cidr (v0.3.0)
|
||||||
SupportsCIDR bool
|
SupportsCIDR bool
|
||||||
// SupportsDisableHostLoopback --disable-host-loopback (v0.3.0)
|
// SupportsDisableHostLoopback --disable-host-loopback (v0.3.0)
|
||||||
|
@ -63,6 +67,7 @@ func DetectFeatures(binary string) (*Features, error) {
|
||||||
kernelSupportsEnableSeccomp = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL
|
kernelSupportsEnableSeccomp = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL
|
||||||
}
|
}
|
||||||
f := Features{
|
f := Features{
|
||||||
|
SupportsEnableIPv6: strings.Contains(s, "--enable-ipv6"),
|
||||||
SupportsCIDR: strings.Contains(s, "--cidr"),
|
SupportsCIDR: strings.Contains(s, "--cidr"),
|
||||||
SupportsDisableHostLoopback: strings.Contains(s, "--disable-host-loopback"),
|
SupportsDisableHostLoopback: strings.Contains(s, "--disable-host-loopback"),
|
||||||
SupportsAPISocket: strings.Contains(s, "--api-socket"),
|
SupportsAPISocket: strings.Contains(s, "--api-socket"),
|
||||||
|
@ -75,7 +80,8 @@ func DetectFeatures(binary string) (*Features, error) {
|
||||||
|
|
||||||
// NewParentDriver instantiates new parent driver.
|
// NewParentDriver instantiates new parent driver.
|
||||||
// Requires slirp4netns v0.4.0 or later.
|
// Requires slirp4netns v0.4.0 or later.
|
||||||
func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet *net.IPNet, disableHostLoopback bool, apiSocketPath string, enableSandbox, enableSeccomp bool) (network.ParentDriver, error) {
|
func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet *net.IPNet, ifname string, disableHostLoopback bool, apiSocketPath string,
|
||||||
|
enableSandbox, enableSeccomp, enableIPv6 bool) (network.ParentDriver, error) {
|
||||||
if binary == "" {
|
if binary == "" {
|
||||||
return nil, errors.New("got empty slirp4netns binary")
|
return nil, errors.New("got empty slirp4netns binary")
|
||||||
}
|
}
|
||||||
|
@ -85,10 +91,18 @@ func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet *net.IPN
|
||||||
if mtu == 0 {
|
if mtu == 0 {
|
||||||
mtu = 65520
|
mtu = 65520
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ifname == "" {
|
||||||
|
ifname = "tap0"
|
||||||
|
}
|
||||||
|
|
||||||
features, err := DetectFeatures(binary)
|
features, err := DetectFeatures(binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if enableIPv6 && !features.SupportsEnableIPv6 {
|
||||||
|
return nil, errors.New("this version of slirp4netns does not support --enable-sandbox")
|
||||||
|
}
|
||||||
if ipnet != nil && !features.SupportsCIDR {
|
if ipnet != nil && !features.SupportsCIDR {
|
||||||
return nil, errors.New("this version of slirp4netns does not support --cidr")
|
return nil, errors.New("this version of slirp4netns does not support --cidr")
|
||||||
}
|
}
|
||||||
|
@ -117,6 +131,8 @@ func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet *net.IPN
|
||||||
apiSocketPath: apiSocketPath,
|
apiSocketPath: apiSocketPath,
|
||||||
enableSandbox: enableSandbox,
|
enableSandbox: enableSandbox,
|
||||||
enableSeccomp: enableSeccomp,
|
enableSeccomp: enableSeccomp,
|
||||||
|
enableIPv6: enableIPv6,
|
||||||
|
ifname: ifname,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +145,25 @@ type parentDriver struct {
|
||||||
apiSocketPath string
|
apiSocketPath string
|
||||||
enableSandbox bool
|
enableSandbox bool
|
||||||
enableSeccomp bool
|
enableSeccomp bool
|
||||||
|
enableIPv6 bool
|
||||||
|
ifname string
|
||||||
|
infoMu sync.RWMutex
|
||||||
|
info func() *api.NetworkDriverInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
const DriverName = "slirp4netns"
|
||||||
|
|
||||||
|
func (d *parentDriver) Info(ctx context.Context) (*api.NetworkDriverInfo, error) {
|
||||||
|
d.infoMu.RLock()
|
||||||
|
infoFn := d.info
|
||||||
|
d.infoMu.RUnlock()
|
||||||
|
if infoFn == nil {
|
||||||
|
return &api.NetworkDriverInfo{
|
||||||
|
Driver: DriverName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoFn(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *parentDriver) MTU() int {
|
func (d *parentDriver) MTU() int {
|
||||||
|
@ -136,7 +171,7 @@ func (d *parentDriver) MTU() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *parentDriver) ConfigureNetwork(childPID int, stateDir string) (*common.NetworkMessage, func() error, error) {
|
func (d *parentDriver) ConfigureNetwork(childPID int, stateDir string) (*common.NetworkMessage, func() error, error) {
|
||||||
tap := "tap0"
|
tap := d.ifname
|
||||||
var cleanups []func() error
|
var cleanups []func() error
|
||||||
if err := parentutils.PrepareTap(childPID, tap); err != nil {
|
if err := parentutils.PrepareTap(childPID, tap); err != nil {
|
||||||
return nil, common.Seq(cleanups), errors.Wrapf(err, "setting up tap %s", tap)
|
return nil, common.Seq(cleanups), errors.Wrapf(err, "setting up tap %s", tap)
|
||||||
|
@ -165,6 +200,9 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir string) (*common.
|
||||||
if d.enableSeccomp {
|
if d.enableSeccomp {
|
||||||
opts = append(opts, "--enable-seccomp")
|
opts = append(opts, "--enable-seccomp")
|
||||||
}
|
}
|
||||||
|
if d.enableIPv6 {
|
||||||
|
opts = append(opts, "--enable-ipv6")
|
||||||
|
}
|
||||||
cmd := exec.CommandContext(ctx, d.binary, append(opts, []string{strconv.Itoa(childPID), tap}...)...)
|
cmd := exec.CommandContext(ctx, d.binary, append(opts, []string{strconv.Itoa(childPID), tap}...)...)
|
||||||
// FIXME: Stdout doen't seem captured
|
// FIXME: Stdout doen't seem captured
|
||||||
cmd.Stdout = d.logWriter
|
cmd.Stdout = d.logWriter
|
||||||
|
@ -215,6 +253,15 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir string) (*common.
|
||||||
netmsg.Gateway = "10.0.2.2"
|
netmsg.Gateway = "10.0.2.2"
|
||||||
netmsg.DNS = "10.0.2.3"
|
netmsg.DNS = "10.0.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.infoMu.Lock()
|
||||||
|
d.info = func() *api.NetworkDriverInfo {
|
||||||
|
return &api.NetworkDriverInfo{
|
||||||
|
Driver: DriverName,
|
||||||
|
DNS: []net.IP{net.ParseIP(netmsg.DNS)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.infoMu.Unlock()
|
||||||
return &netmsg, common.Seq(cleanups), nil
|
return &netmsg, common.Seq(cleanups), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
114
vendor/github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil/cgrouputil.go
generated
vendored
Normal file
114
vendor/github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil/cgrouputil.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package cgrouputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvacuateCgroup2 evacuates cgroup2. Must be called in the parent PID namespace.
|
||||||
|
//
|
||||||
|
// When the current process belongs to "/foo" group (visible under "/sys/fs/cgroup/foo") and evac is like "bar",
|
||||||
|
// - All processes in the "/foo" group are moved to "/foo/bar" group, by writing PIDs into "/sys/fs/cgroup/foo/bar/cgroup.procs"
|
||||||
|
// - As many controllers as possible are enabled for "/foo/*" groups, by writing "/sys/fs/cgroup/foo/cgroup.subtree_control"
|
||||||
|
//
|
||||||
|
// Returns nil when cgroup2 is not enabled.
|
||||||
|
// Ported from https://github.com/rootless-containers/usernetes/commit/46ad812db7489914897ff8b1774f2fab0efda62b
|
||||||
|
func EvacuateCgroup2(evac string) error {
|
||||||
|
if evac == "" {
|
||||||
|
return errors.New("got empty evacuation group name")
|
||||||
|
}
|
||||||
|
if strings.Contains(evac, "/") {
|
||||||
|
return errors.Errorf("unexpected evacuation group name %q: must not contain \"/\"", evac)
|
||||||
|
}
|
||||||
|
|
||||||
|
mountpoint := findCgroup2Mountpoint()
|
||||||
|
if mountpoint == "" {
|
||||||
|
logrus.Warn("cgroup2 is not mounted. cgroup2 evacuation is discarded.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
oldGroup := getCgroup2(os.Getpid())
|
||||||
|
if mountpoint == "" {
|
||||||
|
logrus.Warn("process is not running with cgroup2. cgroup2 evacuation is discarded.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newGroup := filepath.Join(oldGroup, evac)
|
||||||
|
|
||||||
|
oldPath := filepath.Join(mountpoint, oldGroup)
|
||||||
|
newPath := filepath.Join(mountpoint, newGroup)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(newPath, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// evacuate existing procs from oldGroup to newGroup, so that we can enable all controllers including threaded ones
|
||||||
|
cgroupProcsBytes, err := ioutil.ReadFile(filepath.Join(oldPath, "cgroup.procs"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, pidStr := range strings.Split(string(cgroupProcsBytes), "\n") {
|
||||||
|
if pidStr == "" || pidStr == "0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(newPath, "cgroup.procs"), []byte(pidStr), 0644); err != nil {
|
||||||
|
logrus.WithError(err).Warnf("failed to move process %s to cgroup %q", pidStr, newGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable controllers for all subgroups under the oldGroup
|
||||||
|
controllerBytes, err := ioutil.ReadFile(filepath.Join(oldPath, "cgroup.controllers"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, controller := range strings.Fields(string(controllerBytes)) {
|
||||||
|
logrus.Debugf("enabling controller %q", controller)
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(oldPath, "cgroup.subtree_control"), []byte("+"+controller), 0644); err != nil {
|
||||||
|
logrus.WithError(err).Warnf("failed to enable controller %q", controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findCgroup2Mountpoint() string {
|
||||||
|
f := mountinfoFSTypeFilter("cgroup2")
|
||||||
|
mounts, err := mountinfo.GetMounts(f)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Warn("failed to find mountpoint for cgroup2")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(mounts) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(mounts) != 1 {
|
||||||
|
logrus.Warnf("expected single mountpoint for cgroup2, got %d", len(mounts))
|
||||||
|
}
|
||||||
|
return mounts[0].Mountpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroup2(pid int) string {
|
||||||
|
p := fmt.Sprintf("/proc/%d/cgroup", pid)
|
||||||
|
b, err := ioutil.ReadFile(p)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Warnf("failed to read %q", p)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return getCgroup2FromProcPidCgroup(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroup2FromProcPidCgroup(b []byte) string {
|
||||||
|
for _, l := range strings.Split(string(b), "\n") {
|
||||||
|
if strings.HasPrefix(l, "0::") {
|
||||||
|
return strings.TrimPrefix(l, "0::")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
44
vendor/github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil/moby_sys_mountinfo_tmphack.go
generated
vendored
Normal file
44
vendor/github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil/moby_sys_mountinfo_tmphack.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package cgrouputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mountinfoFSType returns m.Fstype on mountinfo v0.1.3,
|
||||||
|
// returns m.FSType on mountinfo v0.4.0.
|
||||||
|
func mountinfoFSType(m *mountinfo.Info) (string, bool) {
|
||||||
|
elem := reflect.ValueOf(m).Elem()
|
||||||
|
for i := 0; i < elem.NumField(); i++ {
|
||||||
|
typeField := elem.Type().Field(i)
|
||||||
|
name := typeField.Name
|
||||||
|
typ := typeField.Type.String()
|
||||||
|
if strings.ToLower(name) == "fstype" && typ == "string" {
|
||||||
|
value := elem.Field(i).String()
|
||||||
|
return value, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// mountinfoFSTypeFilter is reimplementation of mountinfo.FSTypeFilter.
|
||||||
|
// Temporary solution for supporting both moby/sys/mountinfo@v0.1.3 and @v0.4.0 .
|
||||||
|
// Will be removed after downstream projects stop using @v0.1.3 .
|
||||||
|
func mountinfoFSTypeFilter(fstype ...string) mountinfo.FilterFunc {
|
||||||
|
return func(m *mountinfo.Info) (bool, bool) {
|
||||||
|
mFSType, ok := mountinfoFSType(m)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("failed to get Fstype (FSType) of %+v", m))
|
||||||
|
}
|
||||||
|
for _, t := range fstype {
|
||||||
|
if mFSType == t {
|
||||||
|
return false, false // don't skeep, keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, false // skip, keep going
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/common"
|
"github.com/rootless-containers/rootlesskit/pkg/common"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/msgutil"
|
"github.com/rootless-containers/rootlesskit/pkg/msgutil"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/network"
|
"github.com/rootless-containers/rootlesskit/pkg/network"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/parent/idtools"
|
"github.com/rootless-containers/rootlesskit/pkg/parent/idtools"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port"
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
|
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
|
||||||
|
@ -43,6 +44,7 @@ type Opt struct {
|
||||||
ParentEUIDEnvKey string // optional env key to propagate geteuid() value
|
ParentEUIDEnvKey string // optional env key to propagate geteuid() value
|
||||||
ParentEGIDEnvKey string // optional env key to propagate getegid() value
|
ParentEGIDEnvKey string // optional env key to propagate getegid() value
|
||||||
Propagation string
|
Propagation string
|
||||||
|
EvacuateCgroup2 string // e.g. "rootlesskit_evacuation"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Documented state files. Undocumented ones are subject to change.
|
// Documented state files. Undocumented ones are subject to change.
|
||||||
|
@ -77,29 +79,59 @@ func checkPreflight(opt Opt) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createCleanupLock uses LOCK_SH for preventing automatic cleanup of
|
||||||
|
// "/tmp/<Our State Dir>" caused by by systemd.
|
||||||
|
//
|
||||||
|
// This LOCK_SH lock is different from our lock file in the state dir.
|
||||||
|
// We could unify the lock file into LOCK_SH, but we are still keeping
|
||||||
|
// the lock file for a historical reason.
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// - https://github.com/rootless-containers/rootlesskit/issues/185
|
||||||
|
// - https://github.com/rootless-containers/rootlesskit/pull/188
|
||||||
|
func createCleanupLock(sDir string) error {
|
||||||
|
//lock state dir when using /tmp/ path
|
||||||
|
stateDir, err := os.Open(sDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = unix.Flock(int(stateDir.Fd()), unix.LOCK_SH)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("Failed to lock the state dir %s", sDir)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockStateDir creates and locks "lock" file in the state dir.
|
||||||
|
func LockStateDir(stateDir string) (*flock.Flock, error) {
|
||||||
|
lockPath := filepath.Join(stateDir, StateFileLock)
|
||||||
|
lock := flock.New(lockPath)
|
||||||
|
locked, err := lock.TryLock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to lock %s", lockPath)
|
||||||
|
}
|
||||||
|
if !locked {
|
||||||
|
return nil, errors.Errorf("failed to lock %s, another RootlessKit is running with the same state directory?", lockPath)
|
||||||
|
}
|
||||||
|
return lock, nil
|
||||||
|
}
|
||||||
|
|
||||||
func Parent(opt Opt) error {
|
func Parent(opt Opt) error {
|
||||||
if err := checkPreflight(opt); err != nil {
|
if err := checkPreflight(opt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lockPath := filepath.Join(opt.StateDir, StateFileLock)
|
|
||||||
lock := flock.NewFlock(lockPath)
|
err := createCleanupLock(opt.StateDir)
|
||||||
locked, err := lock.TryLock()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to lock %s", lockPath)
|
return err
|
||||||
}
|
}
|
||||||
if !locked {
|
|
||||||
return errors.Errorf("failed to lock %s, another RootlessKit is running with the same state directory?", lockPath)
|
lock, err := LockStateDir(opt.StateDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(opt.StateDir)
|
defer os.RemoveAll(opt.StateDir)
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
// when the previous execution crashed, the state dir may not be removed successfully.
|
|
||||||
// explicitly remove everything in the state dir except the lock file here.
|
|
||||||
for _, f := range []string{StateFileChildPID} {
|
|
||||||
p := filepath.Join(opt.StateDir, f)
|
|
||||||
if err := os.RemoveAll(p); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to remove %s", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeR, pipeW, err := os.Pipe()
|
pipeR, pipeW, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -149,6 +181,12 @@ func Parent(opt Opt) error {
|
||||||
sigc := sigproxy.ForwardAllSignals(context.TODO(), cmd.Process.Pid)
|
sigc := sigproxy.ForwardAllSignals(context.TODO(), cmd.Process.Pid)
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
|
|
||||||
|
if opt.EvacuateCgroup2 != "" {
|
||||||
|
if err := cgrouputil.EvacuateCgroup2(opt.EvacuateCgroup2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// send message 0
|
// send message 0
|
||||||
msg := common.Message{
|
msg := common.Message{
|
||||||
Stage: 0,
|
Stage: 0,
|
||||||
|
@ -223,7 +261,12 @@ func Parent(opt Opt) error {
|
||||||
}
|
}
|
||||||
// listens the API
|
// listens the API
|
||||||
apiSockPath := filepath.Join(opt.StateDir, StateFileAPISock)
|
apiSockPath := filepath.Join(opt.StateDir, StateFileAPISock)
|
||||||
apiCloser, err := listenServeAPI(apiSockPath, &router.Backend{PortDriver: opt.PortDriver})
|
apiCloser, err := listenServeAPI(apiSockPath, &router.Backend{
|
||||||
|
StateDir: opt.StateDir,
|
||||||
|
ChildPID: cmd.Process.Pid,
|
||||||
|
NetworkDriver: opt.NetworkDriver,
|
||||||
|
PortDriver: opt.PortDriver,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -330,3 +373,32 @@ func listenServeAPI(socketPath string, backend *router.Backend) (apiCloser, erro
|
||||||
go srv.Serve(l)
|
go srv.Serve(l)
|
||||||
return srv, nil
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitStateDir removes everything in the state dir except the lock file.
|
||||||
|
// This is needed because when the previous execution crashed, the state dir may not be removed successfully.
|
||||||
|
//
|
||||||
|
// InitStateDir must be called before calling parent functions.
|
||||||
|
func InitStateDir(stateDir string) error {
|
||||||
|
if err := os.MkdirAll(stateDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lk, err := LockStateDir(stateDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer lk.Unlock()
|
||||||
|
stateDirStuffs, err := ioutil.ReadDir(stateDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, f := range stateDirStuffs {
|
||||||
|
if f.Name() == StateFileLock {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p := filepath.Join(stateDir, f.Name())
|
||||||
|
if err := os.RemoveAll(p); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to remove %s", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
21
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/child/child.go
generated
vendored
21
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/child/child.go
generated
vendored
|
@ -1,10 +1,11 @@
|
||||||
package child
|
package child
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
@ -101,12 +102,28 @@ func (d *childDriver) handleConnectInit(c *net.UnixConn, req *msg.Request) error
|
||||||
func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) error {
|
func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) error {
|
||||||
switch req.Proto {
|
switch req.Proto {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
|
case "tcp4":
|
||||||
|
case "tcp6":
|
||||||
case "udp":
|
case "udp":
|
||||||
|
case "udp4":
|
||||||
|
case "udp6":
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unknown proto: %q", req.Proto)
|
return errors.Errorf("unknown proto: %q", req.Proto)
|
||||||
}
|
}
|
||||||
|
// dialProto does not need "4", "6" suffix
|
||||||
|
dialProto := strings.TrimSuffix(strings.TrimSuffix(req.Proto, "6"), "4")
|
||||||
var dialer net.Dialer
|
var dialer net.Dialer
|
||||||
targetConn, err := dialer.Dial(req.Proto, fmt.Sprintf("127.0.0.1:%d", req.Port))
|
ip := req.IP
|
||||||
|
if ip == "" {
|
||||||
|
ip = "127.0.0.1"
|
||||||
|
} else {
|
||||||
|
p := net.ParseIP(ip)
|
||||||
|
if p == nil {
|
||||||
|
return errors.Errorf("invalid IP: %q", ip)
|
||||||
|
}
|
||||||
|
ip = p.String()
|
||||||
|
}
|
||||||
|
targetConn, err := dialer.Dial(dialProto, net.JoinHostPort(ip, strconv.Itoa(req.Port)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ const (
|
||||||
// Request and Response are encoded as JSON with uint32le length header.
|
// Request and Response are encoded as JSON with uint32le length header.
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Type string // "init" or "connect"
|
Type string // "init" or "connect"
|
||||||
Proto string // "tcp" or "udp"
|
Proto string // "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"
|
||||||
|
IP string
|
||||||
Port int
|
Port int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ func ConnectToChild(c *net.UnixConn, spec port.Spec) (int, error) {
|
||||||
Type: RequestTypeConnect,
|
Type: RequestTypeConnect,
|
||||||
Proto: spec.Proto,
|
Proto: spec.Proto,
|
||||||
Port: spec.ChildPort,
|
Port: spec.ChildPort,
|
||||||
|
IP: spec.ChildIP,
|
||||||
}
|
}
|
||||||
if _, err := msgutil.MarshalToWriter(c, &req); err != nil {
|
if _, err := msgutil.MarshalToWriter(c, &req); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
15
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go
generated
vendored
15
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go
generated
vendored
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/api"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port"
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg"
|
"github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg"
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque"
|
"github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque"
|
||||||
|
@ -56,6 +57,14 @@ type driver struct {
|
||||||
nextID int
|
nextID int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *driver) Info(ctx context.Context) (*api.PortDriverInfo, error) {
|
||||||
|
info := &api.PortDriverInfo{
|
||||||
|
Driver: "builtin",
|
||||||
|
Protos: []string{"tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"},
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *driver) OpaqueForChild() map[string]string {
|
func (d *driver) OpaqueForChild() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
opaque.SocketPath: d.socketPath,
|
opaque.SocketPath: d.socketPath,
|
||||||
|
@ -111,7 +120,7 @@ func annotateEPERM(origErr error, spec port.Spec) error {
|
||||||
// origErr is unrelated to ip_unprivileged_port_start
|
// origErr is unrelated to ip_unprivileged_port_start
|
||||||
return origErr
|
return origErr
|
||||||
}
|
}
|
||||||
text := fmt.Sprintf("cannot expose privileged port %d, you might need to add \"net.ipv4.ip_unprivileged_port_start=0\" (currently %d) to /etc/sysctl.conf", spec.ParentPort, start)
|
text := fmt.Sprintf("cannot expose privileged port %d, you can add 'net.ipv4.ip_unprivileged_port_start=%d' to /etc/sysctl.conf (currently %d)", spec.ParentPort, spec.ParentPort, start)
|
||||||
if filepath.Base(os.Args[0]) == "rootlesskit" {
|
if filepath.Base(os.Args[0]) == "rootlesskit" {
|
||||||
// NOTE: The following sentence is appended only if Args[0] == "rootlesskit", because it does not apply to Podman (as of Podman v1.9).
|
// NOTE: The following sentence is appended only if Args[0] == "rootlesskit", because it does not apply to Podman (as of Podman v1.9).
|
||||||
// Podman launches the parent driver in the child user namespace (but in the parent network namespace), which disables the file capability.
|
// Podman launches the parent driver in the child user namespace (but in the parent network namespace), which disables the file capability.
|
||||||
|
@ -134,9 +143,9 @@ func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, err
|
||||||
return nil // FIXME
|
return nil // FIXME
|
||||||
}
|
}
|
||||||
switch spec.Proto {
|
switch spec.Proto {
|
||||||
case "tcp":
|
case "tcp", "tcp4", "tcp6":
|
||||||
err = tcp.Run(d.socketPath, spec, routineStopCh, d.logWriter)
|
err = tcp.Run(d.socketPath, spec, routineStopCh, d.logWriter)
|
||||||
case "udp":
|
case "udp", "udp4", "udp6":
|
||||||
err = udp.Run(d.socketPath, spec, routineStopCh, d.logWriter)
|
err = udp.Run(d.socketPath, spec, routineStopCh, d.logWriter)
|
||||||
default:
|
default:
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
|
|
3
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go
generated
vendored
3
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port"
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
|
@ -12,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error {
|
func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error {
|
||||||
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort))
|
ln, err := net.Listen(spec.Proto, net.JoinHostPort(spec.ParentIP, strconv.Itoa(spec.ParentPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(logWriter, "listen: %v\n", err)
|
fmt.Fprintf(logWriter, "listen: %v\n", err)
|
||||||
return err
|
return err
|
||||||
|
|
6
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go
generated
vendored
6
vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go
generated
vendored
|
@ -1,10 +1,10 @@
|
||||||
package udp
|
package udp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error {
|
func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error {
|
||||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort))
|
addr, err := net.ResolveUDPAddr(spec.Proto, net.JoinHostPort(spec.ParentIP, strconv.Itoa(spec.ParentPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c, err := net.ListenUDP("udp", addr)
|
c, err := net.ListenUDP(spec.Proto, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,22 @@ package port
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
Proto string `json:"proto,omitempty"` // either "tcp" or "udp". in future "sctp" will be supported as well.
|
// Proto is one of ["tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"].
|
||||||
ParentIP string `json:"parentIP,omitempty"` // IPv4 address. can be empty (0.0.0.0).
|
// "tcp" may cause listening on both IPv4 and IPv6. (Corresponds to Go's net.Listen .)
|
||||||
|
Proto string `json:"proto,omitempty"`
|
||||||
|
ParentIP string `json:"parentIP,omitempty"` // IPv4 or IPv6 address. can be empty (0.0.0.0).
|
||||||
ParentPort int `json:"parentPort,omitempty"`
|
ParentPort int `json:"parentPort,omitempty"`
|
||||||
ChildPort int `json:"childPort,omitempty"`
|
ChildPort int `json:"childPort,omitempty"`
|
||||||
|
// ChildIP is an IPv4 or IPv6 address.
|
||||||
|
// Default values:
|
||||||
|
// - builtin driver: 127.0.0.1
|
||||||
|
// - slirp4netns driver: slirp4netns's child IP, e.g., 10.0.2.100
|
||||||
|
ChildIP string `json:"childIP,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
|
@ -35,6 +44,7 @@ type ChildContext struct {
|
||||||
// ParentDriver is a driver for the parent process.
|
// ParentDriver is a driver for the parent process.
|
||||||
type ParentDriver interface {
|
type ParentDriver interface {
|
||||||
Manager
|
Manager
|
||||||
|
Info(ctx context.Context) (*api.PortDriverInfo, error)
|
||||||
// OpaqueForChild typically consists of socket path
|
// OpaqueForChild typically consists of socket path
|
||||||
// for controlling child from parent
|
// for controlling child from parent
|
||||||
OpaqueForChild() map[string]string
|
OpaqueForChild() map[string]string
|
||||||
|
|
143
vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go
generated
vendored
143
vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go
generated
vendored
|
@ -2,52 +2,137 @@ package portutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/scanner"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/rootless-containers/rootlesskit/pkg/port"
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParsePortSpec parses a Docker-like representation of PortSpec.
|
// ParsePortSpec parses a Docker-like representation of PortSpec, but with
|
||||||
// e.g. "127.0.0.1:8080:80/tcp"
|
// support for both "parent IP" and "child IP" (optional);
|
||||||
func ParsePortSpec(s string) (*port.Spec, error) {
|
// e.g. "127.0.0.1:8080:80/tcp", or "127.0.0.1:8080:10.0.2.100:80/tcp"
|
||||||
r := regexp.MustCompile("^([0-9a-f\\.]+):([0-9]+):([0-9]+)/([a-z]+)$")
|
//
|
||||||
g := r.FindStringSubmatch(s)
|
// Format is as follows:
|
||||||
if len(g) != 5 {
|
//
|
||||||
return nil, errors.Errorf("unexpected PortSpec string: %q", s)
|
// <parent IP>:<parent port>[:<child IP>]:<child port>/<proto>
|
||||||
|
//
|
||||||
|
// Note that (child IP being optional) the format can either contain 5 or 4
|
||||||
|
// components. When using IPv6 IP addresses, addresses must use square brackets
|
||||||
|
// to prevent the colons being mistaken for delimiters. For example:
|
||||||
|
//
|
||||||
|
// [::1]:8080:[::2]:80/udp
|
||||||
|
func ParsePortSpec(portSpec string) (*port.Spec, error) {
|
||||||
|
const (
|
||||||
|
parentIP = iota
|
||||||
|
parentPort = iota
|
||||||
|
childIP = iota
|
||||||
|
childPort = iota
|
||||||
|
proto = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
s scanner.Scanner
|
||||||
|
err error
|
||||||
|
parts = make([]string, 5)
|
||||||
|
index = parentIP
|
||||||
|
delimiter = ':'
|
||||||
|
)
|
||||||
|
|
||||||
|
// First get the "proto" and "parent-port" at the end. These parts are
|
||||||
|
// required, whereas "ParentIP" is optional. Removing them first makes
|
||||||
|
// it easier to parse the remaining parts, as otherwise the third part
|
||||||
|
// could be _either_ an IP-address _or_ a Port.
|
||||||
|
|
||||||
|
// Get the proto
|
||||||
|
protoPos := strings.LastIndex(portSpec, "/")
|
||||||
|
if protoPos < 0 {
|
||||||
|
return nil, errors.Errorf("missing proto in PortSpec string: %q", portSpec)
|
||||||
}
|
}
|
||||||
parentIP := g[1]
|
parts[proto] = portSpec[protoPos+1:]
|
||||||
parentPort, err := strconv.Atoi(g[2])
|
err = validateProto(parts[proto])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "unexpected ParentPort in PortSpec string: %q", s)
|
return nil, errors.Wrapf(err, "invalid PortSpec string: %q", portSpec)
|
||||||
}
|
}
|
||||||
childPort, err := strconv.Atoi(g[3])
|
|
||||||
|
// Get the parent port
|
||||||
|
portPos := strings.LastIndex(portSpec, ":")
|
||||||
|
if portPos < 0 {
|
||||||
|
return nil, errors.Errorf("unexpected PortSpec string: %q", portSpec)
|
||||||
|
}
|
||||||
|
parts[childPort] = portSpec[portPos+1 : protoPos]
|
||||||
|
|
||||||
|
// Scan the remainder "<IP-address>:<port>[:<IP-address>]"
|
||||||
|
s.Init(strings.NewReader(portSpec[:portPos]))
|
||||||
|
|
||||||
|
for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
|
||||||
|
if index > childPort {
|
||||||
|
return nil, errors.Errorf("unexpected PortSpec string: %q", portSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tok {
|
||||||
|
case '[':
|
||||||
|
// Start of IPv6 IP-address; value ends at closing bracket (])
|
||||||
|
delimiter = ']'
|
||||||
|
continue
|
||||||
|
case delimiter:
|
||||||
|
if delimiter == ']' {
|
||||||
|
// End of IPv6 IP-address
|
||||||
|
delimiter = ':'
|
||||||
|
// Skip the next token, which should be a colon delimiter (:)
|
||||||
|
tok = s.Scan()
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
parts[index] += s.TokenText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[parentIP] != "" && net.ParseIP(parts[parentIP]) == nil {
|
||||||
|
return nil, errors.Errorf("unexpected ParentIP in PortSpec string: %q", portSpec)
|
||||||
|
}
|
||||||
|
if parts[childIP] != "" && net.ParseIP(parts[childIP]) == nil {
|
||||||
|
return nil, errors.Errorf("unexpected ParentIP in PortSpec string: %q", portSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := &port.Spec{
|
||||||
|
Proto: parts[proto],
|
||||||
|
ParentIP: parts[parentIP],
|
||||||
|
ChildIP: parts[childIP],
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.ParentPort, err = strconv.Atoi(parts[parentPort])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "unexpected ChildPort in PortSpec string: %q", s)
|
return nil, errors.Wrapf(err, "unexpected ChildPort in PortSpec string: %q", portSpec)
|
||||||
}
|
}
|
||||||
proto := g[4]
|
|
||||||
// validation is up to the caller (as json.Unmarshal doesn't validate values)
|
ps.ChildPort, err = strconv.Atoi(parts[childPort])
|
||||||
return &port.Spec{
|
if err != nil {
|
||||||
Proto: proto,
|
return nil, errors.Wrapf(err, "unexpected ParentPort in PortSpec string: %q", portSpec)
|
||||||
ParentIP: parentIP,
|
}
|
||||||
ParentPort: parentPort,
|
|
||||||
ChildPort: childPort,
|
return ps, nil
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePortSpec validates *port.Spec.
|
// ValidatePortSpec validates *port.Spec.
|
||||||
// existingPorts can be optionally passed for detecting conflicts.
|
// existingPorts can be optionally passed for detecting conflicts.
|
||||||
func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error {
|
func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error {
|
||||||
if spec.Proto != "tcp" && spec.Proto != "udp" {
|
if err := validateProto(spec.Proto); err != nil {
|
||||||
return errors.Errorf("unknown proto: %q", spec.Proto)
|
return err
|
||||||
}
|
}
|
||||||
if spec.ParentIP != "" {
|
if spec.ParentIP != "" {
|
||||||
if net.ParseIP(spec.ParentIP) == nil {
|
if net.ParseIP(spec.ParentIP) == nil {
|
||||||
return errors.Errorf("invalid ParentIP: %q", spec.ParentIP)
|
return errors.Errorf("invalid ParentIP: %q", spec.ParentIP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if spec.ChildIP != "" {
|
||||||
|
if net.ParseIP(spec.ChildIP) == nil {
|
||||||
|
return errors.Errorf("invalid ChildIP: %q", spec.ChildIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
if spec.ParentPort <= 0 || spec.ParentPort > 65535 {
|
if spec.ParentPort <= 0 || spec.ParentPort > 65535 {
|
||||||
return errors.Errorf("invalid ParentPort: %q", spec.ParentPort)
|
return errors.Errorf("invalid ParentPort: %q", spec.ParentPort)
|
||||||
}
|
}
|
||||||
|
@ -64,3 +149,15 @@ func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateProto(proto string) error {
|
||||||
|
switch proto {
|
||||||
|
case
|
||||||
|
"tcp", "tcp4", "tcp6",
|
||||||
|
"udp", "udp4", "udp6",
|
||||||
|
"sctp", "sctp4", "sctp6":
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unknown proto: %q", proto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
3
vendor/github.com/rootless-containers/rootlesskit/pkg/version/version.go
generated
vendored
Normal file
3
vendor/github.com/rootless-containers/rootlesskit/pkg/version/version.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
const Version = "0.14.0"
|
|
@ -1,4 +1,6 @@
|
||||||
Blackfriday [![Build Status](https://travis-ci.org/russross/blackfriday.svg?branch=master)](https://travis-ci.org/russross/blackfriday)
|
Blackfriday
|
||||||
|
[![Build Status][BuildV2SVG]][BuildV2URL]
|
||||||
|
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
||||||
|
@ -16,19 +18,21 @@ It started as a translation from C of [Sundown][3].
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Blackfriday is compatible with any modern Go release. With Go 1.7 and git
|
Blackfriday is compatible with modern Go releases in module mode.
|
||||||
installed:
|
With Go installed:
|
||||||
|
|
||||||
go get gopkg.in/russross/blackfriday.v2
|
go get github.com/russross/blackfriday/v2
|
||||||
|
|
||||||
will download, compile, and install the package into your `$GOPATH`
|
will resolve and add the package to the current development module,
|
||||||
directory hierarchy. Alternatively, you can achieve the same if you
|
then build and install it. Alternatively, you can achieve the same
|
||||||
import it into a project:
|
if you import it in a package:
|
||||||
|
|
||||||
import "gopkg.in/russross/blackfriday.v2"
|
import "github.com/russross/blackfriday/v2"
|
||||||
|
|
||||||
and `go get` without parameters.
|
and `go get` without parameters.
|
||||||
|
|
||||||
|
Legacy GOPATH mode is unsupported.
|
||||||
|
|
||||||
|
|
||||||
Versions
|
Versions
|
||||||
--------
|
--------
|
||||||
|
@ -36,13 +40,9 @@ Versions
|
||||||
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
||||||
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
||||||
documentation is available at
|
documentation is available at
|
||||||
https://godoc.org/gopkg.in/russross/blackfriday.v2.
|
https://pkg.go.dev/github.com/russross/blackfriday/v2.
|
||||||
|
|
||||||
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
|
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
|
||||||
but we highly recommend using package management tool like [dep][7] or
|
|
||||||
[Glide][8] and make use of semantic versioning. With package management you
|
|
||||||
should import `github.com/russross/blackfriday` and specify that you're using
|
|
||||||
version 2.0.0.
|
|
||||||
|
|
||||||
Version 2 offers a number of improvements over v1:
|
Version 2 offers a number of improvements over v1:
|
||||||
|
|
||||||
|
@ -62,6 +62,11 @@ Potential drawbacks:
|
||||||
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
||||||
tracking.
|
tracking.
|
||||||
|
|
||||||
|
If you are still interested in the legacy `v1`, you can import it from
|
||||||
|
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
|
||||||
|
here: https://pkg.go.dev/github.com/russross/blackfriday.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -91,7 +96,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday:
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
"github.com/russross/blackfriday"
|
"github.com/russross/blackfriday/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
@ -104,6 +109,8 @@ html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
|
||||||
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
||||||
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
||||||
|
|
||||||
|
### `blackfriday-tool`
|
||||||
|
|
||||||
You can also check out `blackfriday-tool` for a more complete example
|
You can also check out `blackfriday-tool` for a more complete example
|
||||||
of how to use it. Download and install it using:
|
of how to use it. Download and install it using:
|
||||||
|
|
||||||
|
@ -114,7 +121,7 @@ markdown file using a standalone program. You can also browse the
|
||||||
source directly on github if you are just looking for some example
|
source directly on github if you are just looking for some example
|
||||||
code:
|
code:
|
||||||
|
|
||||||
* <http://github.com/russross/blackfriday-tool>
|
* <https://github.com/russross/blackfriday-tool>
|
||||||
|
|
||||||
Note that if you have not already done so, installing
|
Note that if you have not already done so, installing
|
||||||
`blackfriday-tool` will be sufficient to download and install
|
`blackfriday-tool` will be sufficient to download and install
|
||||||
|
@ -123,6 +130,22 @@ installed in `$GOPATH/bin`. This is a statically-linked binary that
|
||||||
can be copied to wherever you need it without worrying about
|
can be copied to wherever you need it without worrying about
|
||||||
dependencies and library versions.
|
dependencies and library versions.
|
||||||
|
|
||||||
|
### Sanitized anchor names
|
||||||
|
|
||||||
|
Blackfriday includes an algorithm for creating sanitized anchor names
|
||||||
|
corresponding to a given input text. This algorithm is used to create
|
||||||
|
anchors for headings when `AutoHeadingIDs` extension is enabled. The
|
||||||
|
algorithm has a specification, so that other packages can create
|
||||||
|
compatible anchor names and links to those anchors.
|
||||||
|
|
||||||
|
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
|
||||||
|
|
||||||
|
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
|
||||||
|
create compatible links to the anchor names generated by blackfriday.
|
||||||
|
This algorithm is also implemented in a small standalone package at
|
||||||
|
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
|
||||||
|
that want a small package and don't need full functionality of blackfriday.
|
||||||
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
@ -199,6 +222,15 @@ implements the following extensions:
|
||||||
You can use 3 or more backticks to mark the beginning of the
|
You can use 3 or more backticks to mark the beginning of the
|
||||||
block, and the same number to mark the end of the block.
|
block, and the same number to mark the end of the block.
|
||||||
|
|
||||||
|
To preserve classes of fenced code blocks while using the bluemonday
|
||||||
|
HTML sanitizer, use the following policy:
|
||||||
|
|
||||||
|
```go
|
||||||
|
p := bluemonday.UGCPolicy()
|
||||||
|
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
|
||||||
|
html := p.SanitizeBytes(unsafe)
|
||||||
|
```
|
||||||
|
|
||||||
* **Definition lists**. A simple definition list is made of a single-line
|
* **Definition lists**. A simple definition list is made of a single-line
|
||||||
term followed by a colon and the definition for that term.
|
term followed by a colon and the definition for that term.
|
||||||
|
|
||||||
|
@ -250,7 +282,7 @@ Other renderers
|
||||||
Blackfriday is structured to allow alternative rendering engines. Here
|
Blackfriday is structured to allow alternative rendering engines. Here
|
||||||
are a few of note:
|
are a few of note:
|
||||||
|
|
||||||
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown):
|
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
|
||||||
provides a GitHub Flavored Markdown renderer with fenced code block
|
provides a GitHub Flavored Markdown renderer with fenced code block
|
||||||
highlighting, clickable heading anchor links.
|
highlighting, clickable heading anchor links.
|
||||||
|
|
||||||
|
@ -261,20 +293,28 @@ are a few of note:
|
||||||
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||||
but for markdown.
|
but for markdown.
|
||||||
|
|
||||||
* [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX):
|
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
|
||||||
renders output as LaTeX.
|
renders output as LaTeX.
|
||||||
|
|
||||||
|
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
|
||||||
|
integration with the [Chroma](https://github.com/alecthomas/chroma) code
|
||||||
|
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
|
||||||
|
provides a drop-in renderer ready to use with Blackfriday, as well as
|
||||||
|
options and means for further customization.
|
||||||
|
|
||||||
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
||||||
|
|
||||||
|
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
|
||||||
|
|
||||||
Todo
|
|
||||||
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
* More unit testing
|
* More unit testing
|
||||||
* Improve unicode support. It does not understand all unicode
|
* Improve Unicode support. It does not understand all Unicode
|
||||||
rules (about what constitutes a letter, a punctuation symbol,
|
rules (about what constitutes a letter, a punctuation symbol,
|
||||||
etc.), so it may fail to detect word boundaries correctly in
|
etc.), so it may fail to detect word boundaries correctly in
|
||||||
some instances. It is safe on all utf-8 input.
|
some instances. It is safe on all UTF-8 input.
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
|
@ -286,6 +326,10 @@ License
|
||||||
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
||||||
[2]: https://golang.org/ "Go Language"
|
[2]: https://golang.org/ "Go Language"
|
||||||
[3]: https://github.com/vmg/sundown "Sundown"
|
[3]: https://github.com/vmg/sundown "Sundown"
|
||||||
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
|
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
|
||||||
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
||||||
[6]: https://labix.org/gopkg.in "gopkg.in"
|
|
||||||
|
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
|
||||||
|
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
|
||||||
|
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
|
||||||
|
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
|
||||||
|
|
|
@ -18,8 +18,7 @@ import (
|
||||||
"html"
|
"html"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"github.com/shurcooL/sanitized_anchor_name"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -259,7 +258,7 @@ func (p *Markdown) prefixHeading(data []byte) int {
|
||||||
}
|
}
|
||||||
if end > i {
|
if end > i {
|
||||||
if id == "" && p.extensions&AutoHeadingIDs != 0 {
|
if id == "" && p.extensions&AutoHeadingIDs != 0 {
|
||||||
id = sanitized_anchor_name.Create(string(data[i:end]))
|
id = SanitizedAnchorName(string(data[i:end]))
|
||||||
}
|
}
|
||||||
block := p.addBlock(Heading, data[i:end])
|
block := p.addBlock(Heading, data[i:end])
|
||||||
block.HeadingID = id
|
block.HeadingID = id
|
||||||
|
@ -673,6 +672,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
|
||||||
if beg == 0 || beg >= len(data) {
|
if beg == 0 || beg >= len(data) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
fenceLength := beg - 1
|
||||||
|
|
||||||
var work bytes.Buffer
|
var work bytes.Buffer
|
||||||
work.Write([]byte(info))
|
work.Write([]byte(info))
|
||||||
|
@ -706,6 +706,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
|
||||||
if doRender {
|
if doRender {
|
||||||
block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
|
block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
|
||||||
block.IsFenced = true
|
block.IsFenced = true
|
||||||
|
block.FenceLength = fenceLength
|
||||||
finalizeCodeBlock(block)
|
finalizeCodeBlock(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1503,7 +1504,7 @@ func (p *Markdown) paragraph(data []byte) int {
|
||||||
|
|
||||||
id := ""
|
id := ""
|
||||||
if p.extensions&AutoHeadingIDs != 0 {
|
if p.extensions&AutoHeadingIDs != 0 {
|
||||||
id = sanitized_anchor_name.Create(string(data[prev:eol]))
|
id = SanitizedAnchorName(string(data[prev:eol]))
|
||||||
}
|
}
|
||||||
|
|
||||||
block := p.addBlock(Heading, data[prev:eol])
|
block := p.addBlock(Heading, data[prev:eol])
|
||||||
|
@ -1588,3 +1589,24 @@ func skipUntilChar(text []byte, start int, char byte) int {
|
||||||
}
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SanitizedAnchorName returns a sanitized anchor name for the given text.
|
||||||
|
//
|
||||||
|
// It implements the algorithm specified in the package comment.
|
||||||
|
func SanitizedAnchorName(text string) string {
|
||||||
|
var anchorName []rune
|
||||||
|
futureDash := false
|
||||||
|
for _, r := range text {
|
||||||
|
switch {
|
||||||
|
case unicode.IsLetter(r) || unicode.IsNumber(r):
|
||||||
|
if futureDash && len(anchorName) > 0 {
|
||||||
|
anchorName = append(anchorName, '-')
|
||||||
|
}
|
||||||
|
futureDash = false
|
||||||
|
anchorName = append(anchorName, unicode.ToLower(r))
|
||||||
|
default:
|
||||||
|
futureDash = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(anchorName)
|
||||||
|
}
|
||||||
|
|
|
@ -15,4 +15,32 @@
|
||||||
//
|
//
|
||||||
// If you're interested in calling Blackfriday from command line, see
|
// If you're interested in calling Blackfriday from command line, see
|
||||||
// https://github.com/russross/blackfriday-tool.
|
// https://github.com/russross/blackfriday-tool.
|
||||||
|
//
|
||||||
|
// Sanitized Anchor Names
|
||||||
|
//
|
||||||
|
// Blackfriday includes an algorithm for creating sanitized anchor names
|
||||||
|
// corresponding to a given input text. This algorithm is used to create
|
||||||
|
// anchors for headings when AutoHeadingIDs extension is enabled. The
|
||||||
|
// algorithm is specified below, so that other packages can create
|
||||||
|
// compatible anchor names and links to those anchors.
|
||||||
|
//
|
||||||
|
// The algorithm iterates over the input text, interpreted as UTF-8,
|
||||||
|
// one Unicode code point (rune) at a time. All runes that are letters (category L)
|
||||||
|
// or numbers (category N) are considered valid characters. They are mapped to
|
||||||
|
// lower case, and included in the output. All other runes are considered
|
||||||
|
// invalid characters. Invalid characters that precede the first valid character,
|
||||||
|
// as well as invalid character that follow the last valid character
|
||||||
|
// are dropped completely. All other sequences of invalid characters
|
||||||
|
// between two valid characters are replaced with a single dash character '-'.
|
||||||
|
//
|
||||||
|
// SanitizedAnchorName exposes this functionality, and can be used to
|
||||||
|
// create compatible links to the anchor names generated by blackfriday.
|
||||||
|
// This algorithm is also implemented in a small standalone package at
|
||||||
|
// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
|
||||||
|
// that want a small package and don't need full functionality of blackfriday.
|
||||||
package blackfriday
|
package blackfriday
|
||||||
|
|
||||||
|
// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
|
||||||
|
// github.com/shurcooL/sanitized_anchor_name.
|
||||||
|
// Otherwise, users of sanitized_anchor_name will get anchor names
|
||||||
|
// that are incompatible with those generated by blackfriday.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,13 +13,27 @@ var htmlEscaper = [256][]byte{
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeHTML(w io.Writer, s []byte) {
|
func escapeHTML(w io.Writer, s []byte) {
|
||||||
|
escapeEntities(w, s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeAllHTML(w io.Writer, s []byte) {
|
||||||
|
escapeEntities(w, s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) {
|
||||||
var start, end int
|
var start, end int
|
||||||
for end < len(s) {
|
for end < len(s) {
|
||||||
escSeq := htmlEscaper[s[end]]
|
escSeq := htmlEscaper[s[end]]
|
||||||
if escSeq != nil {
|
if escSeq != nil {
|
||||||
w.Write(s[start:end])
|
isEntity, entityEnd := nodeIsEntity(s, end)
|
||||||
w.Write(escSeq)
|
if isEntity && !escapeValidEntities {
|
||||||
start = end + 1
|
w.Write(s[start : entityEnd+1])
|
||||||
|
start = entityEnd + 1
|
||||||
|
} else {
|
||||||
|
w.Write(s[start:end])
|
||||||
|
w.Write(escSeq)
|
||||||
|
start = end + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end++
|
end++
|
||||||
}
|
}
|
||||||
|
@ -28,6 +42,28 @@ func escapeHTML(w io.Writer, s []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) {
|
||||||
|
isEntity = false
|
||||||
|
endEntityPos = end + 1
|
||||||
|
|
||||||
|
if s[end] == '&' {
|
||||||
|
for endEntityPos < len(s) {
|
||||||
|
if s[endEntityPos] == ';' {
|
||||||
|
if entities[string(s[end:endEntityPos+1])] {
|
||||||
|
isEntity = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
endEntityPos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEntity, endEntityPos
|
||||||
|
}
|
||||||
|
|
||||||
func escLink(w io.Writer, text []byte) {
|
func escLink(w io.Writer, text []byte) {
|
||||||
unesc := html.UnescapeString(string(text))
|
unesc := html.UnescapeString(string(text))
|
||||||
escapeHTML(w, []byte(unesc))
|
escapeHTML(w, []byte(unesc))
|
||||||
|
|
|
@ -132,7 +132,10 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.FootnoteReturnLinkContents == "" {
|
if params.FootnoteReturnLinkContents == "" {
|
||||||
params.FootnoteReturnLinkContents = `<sup>[return]</sup>`
|
// U+FE0E is VARIATION SELECTOR-15.
|
||||||
|
// It suppresses automatic emoji presentation of the preceding
|
||||||
|
// U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS.
|
||||||
|
params.FootnoteReturnLinkContents = "<span aria-label='Return'>↩\ufe0e</span>"
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HTMLRenderer{
|
return &HTMLRenderer{
|
||||||
|
@ -616,7 +619,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||||
}
|
}
|
||||||
case Code:
|
case Code:
|
||||||
r.out(w, codeTag)
|
r.out(w, codeTag)
|
||||||
escapeHTML(w, node.Literal)
|
escapeAllHTML(w, node.Literal)
|
||||||
r.out(w, codeCloseTag)
|
r.out(w, codeCloseTag)
|
||||||
case Document:
|
case Document:
|
||||||
break
|
break
|
||||||
|
@ -762,7 +765,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||||
r.cr(w)
|
r.cr(w)
|
||||||
r.out(w, preTag)
|
r.out(w, preTag)
|
||||||
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
||||||
escapeHTML(w, node.Literal)
|
escapeAllHTML(w, node.Literal)
|
||||||
r.out(w, codeCloseTag)
|
r.out(w, codeCloseTag)
|
||||||
r.out(w, preCloseTag)
|
r.out(w, preCloseTag)
|
||||||
if node.Parent.Type != Item {
|
if node.Parent.Type != Item {
|
||||||
|
|
|
@ -278,7 +278,7 @@ func link(p *Markdown, data []byte, offset int) (int, *Node) {
|
||||||
case data[i] == '\n':
|
case data[i] == '\n':
|
||||||
textHasNl = true
|
textHasNl = true
|
||||||
|
|
||||||
case data[i-1] == '\\':
|
case isBackslashEscaped(data, i):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case data[i] == '[':
|
case data[i] == '[':
|
||||||
|
|
|
@ -199,7 +199,8 @@ func (n *Node) InsertBefore(sibling *Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) isContainer() bool {
|
// IsContainer returns true if 'n' can contain children.
|
||||||
|
func (n *Node) IsContainer() bool {
|
||||||
switch n.Type {
|
switch n.Type {
|
||||||
case Document:
|
case Document:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
@ -238,6 +239,11 @@ func (n *Node) isContainer() bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLeaf returns true if 'n' is a leaf node.
|
||||||
|
func (n *Node) IsLeaf() bool {
|
||||||
|
return !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Node) canContain(t NodeType) bool {
|
func (n *Node) canContain(t NodeType) bool {
|
||||||
if n.Type == List {
|
if n.Type == List {
|
||||||
return t == Item
|
return t == Item
|
||||||
|
@ -309,11 +315,11 @@ func newNodeWalker(root *Node) *nodeWalker {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nw *nodeWalker) next() {
|
func (nw *nodeWalker) next() {
|
||||||
if (!nw.current.isContainer() || !nw.entering) && nw.current == nw.root {
|
if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
|
||||||
nw.current = nil
|
nw.current = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if nw.entering && nw.current.isContainer() {
|
if nw.entering && nw.current.IsContainer() {
|
||||||
if nw.current.FirstChild != nil {
|
if nw.current.FirstChild != nil {
|
||||||
nw.current = nw.current.FirstChild
|
nw.current = nw.current.FirstChild
|
||||||
nw.entering = true
|
nw.entering = true
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.x
|
|
||||||
- master
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: master
|
|
||||||
fast_finish: true
|
|
||||||
install:
|
|
||||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
|
||||||
- go tool vet .
|
|
||||||
- go test -v -race ./...
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2015 Dmitri Shuralyov
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,36 +0,0 @@
|
||||||
sanitized_anchor_name
|
|
||||||
=====================
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/shurcooL/sanitized_anchor_name.svg?branch=master)](https://travis-ci.org/shurcooL/sanitized_anchor_name) [![GoDoc](https://godoc.org/github.com/shurcooL/sanitized_anchor_name?status.svg)](https://godoc.org/github.com/shurcooL/sanitized_anchor_name)
|
|
||||||
|
|
||||||
Package sanitized_anchor_name provides a func to create sanitized anchor names.
|
|
||||||
|
|
||||||
Its logic can be reused by multiple packages to create interoperable anchor names
|
|
||||||
and links to those anchors.
|
|
||||||
|
|
||||||
At this time, it does not try to ensure that generated anchor names
|
|
||||||
are unique, that responsibility falls on the caller.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get -u github.com/shurcooL/sanitized_anchor_name
|
|
||||||
```
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
```Go
|
|
||||||
anchorName := sanitized_anchor_name.Create("This is a header")
|
|
||||||
|
|
||||||
fmt.Println(anchorName)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// this-is-a-header
|
|
||||||
```
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
- [MIT License](LICENSE)
|
|
|
@ -1 +0,0 @@
|
||||||
module github.com/shurcooL/sanitized_anchor_name
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Package sanitized_anchor_name provides a func to create sanitized anchor names.
|
|
||||||
//
|
|
||||||
// Its logic can be reused by multiple packages to create interoperable anchor names
|
|
||||||
// and links to those anchors.
|
|
||||||
//
|
|
||||||
// At this time, it does not try to ensure that generated anchor names
|
|
||||||
// are unique, that responsibility falls on the caller.
|
|
||||||
package sanitized_anchor_name // import "github.com/shurcooL/sanitized_anchor_name"
|
|
||||||
|
|
||||||
import "unicode"
|
|
||||||
|
|
||||||
// Create returns a sanitized anchor name for the given text.
|
|
||||||
func Create(text string) string {
|
|
||||||
var anchorName []rune
|
|
||||||
var futureDash = false
|
|
||||||
for _, r := range text {
|
|
||||||
switch {
|
|
||||||
case unicode.IsLetter(r) || unicode.IsNumber(r):
|
|
||||||
if futureDash && len(anchorName) > 0 {
|
|
||||||
anchorName = append(anchorName, '-')
|
|
||||||
}
|
|
||||||
futureDash = false
|
|
||||||
anchorName = append(anchorName, unicode.ToLower(r))
|
|
||||||
default:
|
|
||||||
futureDash = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(anchorName)
|
|
||||||
}
|
|
|
@ -4,14 +4,12 @@ git:
|
||||||
depth: 1
|
depth: 1
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
go: [1.13.x, 1.14.x]
|
go: 1.15.x
|
||||||
os: [linux, osx]
|
os: linux
|
||||||
install:
|
install:
|
||||||
- ./travis/install.sh
|
- ./travis/install.sh
|
||||||
script:
|
script:
|
||||||
- ./travis/cross_build.sh
|
- cd ci
|
||||||
- ./travis/lint.sh
|
- go run mage.go -v -w ../ crossBuild
|
||||||
- export GOMAXPROCS=4
|
- go run mage.go -v -w ../ lint
|
||||||
- export GORACE=halt_on_error=1
|
- go run mage.go -v -w ../ test
|
||||||
- go test -race -v ./...
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi
|
|
||||||
|
|
|
@ -1,3 +1,39 @@
|
||||||
|
# 1.8.1
|
||||||
|
Code quality:
|
||||||
|
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
||||||
|
* improve timestamp format documentation
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* fix race condition on logger hooks
|
||||||
|
|
||||||
|
|
||||||
|
# 1.8.0
|
||||||
|
|
||||||
|
Correct versioning number replacing v1.7.1.
|
||||||
|
|
||||||
|
# 1.7.1
|
||||||
|
|
||||||
|
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
||||||
|
|
||||||
|
Code quality:
|
||||||
|
* use go 1.15 in travis
|
||||||
|
* use magefile as task runner
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* small fixes about new go 1.13 error formatting system
|
||||||
|
* Fix for long time race condiction with mutating data hooks
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* build support for zos
|
||||||
|
|
||||||
|
# 1.7.0
|
||||||
|
Fixes:
|
||||||
|
* the dependency toward a windows terminal library has been removed
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* a new buffer pool management API has been added
|
||||||
|
* a set of `<LogLevel>Fn()` functions have been added
|
||||||
|
|
||||||
# 1.6.0
|
# 1.6.0
|
||||||
Fixes:
|
Fixes:
|
||||||
* end of line cleanup
|
* end of line cleanup
|
||||||
|
|
|
@ -402,7 +402,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
// source of the official loggers.
|
// source of the official loggers.
|
||||||
serialized, err := json.Marshal(entry.Data)
|
serialized, err := json.Marshal(entry.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
||||||
}
|
}
|
||||||
return append(serialized, '\n'), nil
|
return append(serialized, '\n'), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,14 @@ func NewEntry(logger *Logger) *Entry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Dup() *Entry {
|
||||||
|
data := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the bytes representation of this entry from the formatter.
|
// Returns the bytes representation of this entry from the formatter.
|
||||||
func (entry *Entry) Bytes() ([]byte, error) {
|
func (entry *Entry) Bytes() ([]byte, error) {
|
||||||
return entry.Logger.Formatter.Format(entry)
|
return entry.Logger.Formatter.Format(entry)
|
||||||
|
@ -123,11 +131,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
for k, v := range fields {
|
for k, v := range fields {
|
||||||
isErrField := false
|
isErrField := false
|
||||||
if t := reflect.TypeOf(v); t != nil {
|
if t := reflect.TypeOf(v); t != nil {
|
||||||
switch t.Kind() {
|
switch {
|
||||||
case reflect.Func:
|
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
||||||
isErrField = true
|
isErrField = true
|
||||||
case reflect.Ptr:
|
|
||||||
isErrField = t.Elem().Kind() == reflect.Func
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isErrField {
|
if isErrField {
|
||||||
|
@ -212,68 +218,72 @@ func (entry Entry) HasCaller() (has bool) {
|
||||||
entry.Caller != nil
|
entry.Caller != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is not declared with a pointer value because otherwise
|
func (entry *Entry) log(level Level, msg string) {
|
||||||
// race conditions will occur when using multiple goroutines
|
|
||||||
func (entry Entry) log(level Level, msg string) {
|
|
||||||
var buffer *bytes.Buffer
|
var buffer *bytes.Buffer
|
||||||
|
|
||||||
// Default to now, but allow users to override if they want.
|
newEntry := entry.Dup()
|
||||||
//
|
|
||||||
// We don't have to worry about polluting future calls to Entry#log()
|
if newEntry.Time.IsZero() {
|
||||||
// with this assignment because this function is declared with a
|
newEntry.Time = time.Now()
|
||||||
// non-pointer receiver.
|
|
||||||
if entry.Time.IsZero() {
|
|
||||||
entry.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Level = level
|
newEntry.Level = level
|
||||||
entry.Message = msg
|
newEntry.Message = msg
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
if entry.Logger.ReportCaller {
|
|
||||||
entry.Caller = getCaller()
|
|
||||||
}
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
entry.fireHooks()
|
newEntry.Logger.mu.Lock()
|
||||||
|
reportCaller := newEntry.Logger.ReportCaller
|
||||||
|
newEntry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
if reportCaller {
|
||||||
|
newEntry.Caller = getCaller()
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry.fireHooks()
|
||||||
|
|
||||||
buffer = getBuffer()
|
buffer = getBuffer()
|
||||||
defer func() {
|
defer func() {
|
||||||
entry.Buffer = nil
|
newEntry.Buffer = nil
|
||||||
putBuffer(buffer)
|
putBuffer(buffer)
|
||||||
}()
|
}()
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
entry.Buffer = buffer
|
newEntry.Buffer = buffer
|
||||||
|
|
||||||
entry.write()
|
newEntry.write()
|
||||||
|
|
||||||
entry.Buffer = nil
|
newEntry.Buffer = nil
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
// directly here.
|
// directly here.
|
||||||
if level <= PanicLevel {
|
if level <= PanicLevel {
|
||||||
panic(&entry)
|
panic(newEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) fireHooks() {
|
func (entry *Entry) fireHooks() {
|
||||||
|
var tmpHooks LevelHooks
|
||||||
entry.Logger.mu.Lock()
|
entry.Logger.mu.Lock()
|
||||||
defer entry.Logger.mu.Unlock()
|
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
||||||
err := entry.Logger.Hooks.Fire(entry.Level, entry)
|
for k, v := range entry.Logger.Hooks {
|
||||||
|
tmpHooks[k] = v
|
||||||
|
}
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
err := tmpHooks.Fire(entry.Level, entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) write() {
|
func (entry *Entry) write() {
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
defer entry.Logger.mu.Unlock()
|
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err = entry.Logger.Out.Write(serialized); err != nil {
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +329,6 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
entry.Log(PanicLevel, args...)
|
entry.Log(PanicLevel, args...)
|
||||||
panic(fmt.Sprint(args...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry Printf family functions
|
// Entry Printf family functions
|
||||||
|
|
|
@ -4,7 +4,5 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
|
@ -23,6 +23,9 @@ func (f FieldMap) resolve(key fieldKey) string {
|
||||||
// JSONFormatter formats logs into parsable json
|
// JSONFormatter formats logs into parsable json
|
||||||
type JSONFormatter struct {
|
type JSONFormatter struct {
|
||||||
// TimestampFormat sets the format used for marshaling timestamps.
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
TimestampFormat string
|
TimestampFormat string
|
||||||
|
|
||||||
// DisableTimestamp allows disabling automatic timestamps in output
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
|
@ -118,7 +121,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
}
|
}
|
||||||
if err := encoder.Encode(data); err != nil {
|
if err := encoder.Encode(data); err != nil {
|
||||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
|
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// LogFunction For big messages, it can be more efficient to pass a function
|
// LogFunction For big messages, it can be more efficient to pass a function
|
||||||
// and only call it if the log level is actually enables rather than
|
// and only call it if the log level is actually enables rather than
|
||||||
// generating the log message and then checking if the level is enabled
|
// generating the log message and then checking if the level is enabled
|
||||||
type LogFunction func()[]interface{}
|
type LogFunction func() []interface{}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux aix
|
// +build linux aix zos
|
||||||
// +build !js
|
// +build !js
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
|
@ -53,7 +53,10 @@ type TextFormatter struct {
|
||||||
// the time passed since beginning of execution.
|
// the time passed since beginning of execution.
|
||||||
FullTimestamp bool
|
FullTimestamp bool
|
||||||
|
|
||||||
// TimestampFormat to use for display when a full timestamp is printed
|
// TimestampFormat to use for display when a full timestamp is printed.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
TimestampFormat string
|
TimestampFormat string
|
||||||
|
|
||||||
// The fields are sorted by default for a consistent output. For applications
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
@ -235,6 +238,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||||
levelColor = yellow
|
levelColor = yellow
|
||||||
case ErrorLevel, FatalLevel, PanicLevel:
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
levelColor = red
|
levelColor = red
|
||||||
|
case InfoLevel:
|
||||||
|
levelColor = blue
|
||||||
default:
|
default:
|
||||||
levelColor = blue
|
levelColor = blue
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,42 @@ const (
|
||||||
compareGreater
|
compareGreater
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
intType = reflect.TypeOf(int(1))
|
||||||
|
int8Type = reflect.TypeOf(int8(1))
|
||||||
|
int16Type = reflect.TypeOf(int16(1))
|
||||||
|
int32Type = reflect.TypeOf(int32(1))
|
||||||
|
int64Type = reflect.TypeOf(int64(1))
|
||||||
|
|
||||||
|
uintType = reflect.TypeOf(uint(1))
|
||||||
|
uint8Type = reflect.TypeOf(uint8(1))
|
||||||
|
uint16Type = reflect.TypeOf(uint16(1))
|
||||||
|
uint32Type = reflect.TypeOf(uint32(1))
|
||||||
|
uint64Type = reflect.TypeOf(uint64(1))
|
||||||
|
|
||||||
|
float32Type = reflect.TypeOf(float32(1))
|
||||||
|
float64Type = reflect.TypeOf(float64(1))
|
||||||
|
|
||||||
|
stringType = reflect.TypeOf("")
|
||||||
|
)
|
||||||
|
|
||||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
|
obj1Value := reflect.ValueOf(obj1)
|
||||||
|
obj2Value := reflect.ValueOf(obj2)
|
||||||
|
|
||||||
|
// throughout this switch we try and avoid calling .Convert() if possible,
|
||||||
|
// as this has a pretty big performance impact
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
{
|
{
|
||||||
intobj1 := obj1.(int)
|
intobj1, ok := obj1.(int)
|
||||||
intobj2 := obj2.(int)
|
if !ok {
|
||||||
|
intobj1 = obj1Value.Convert(intType).Interface().(int)
|
||||||
|
}
|
||||||
|
intobj2, ok := obj2.(int)
|
||||||
|
if !ok {
|
||||||
|
intobj2 = obj2Value.Convert(intType).Interface().(int)
|
||||||
|
}
|
||||||
if intobj1 > intobj2 {
|
if intobj1 > intobj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
{
|
{
|
||||||
int8obj1 := obj1.(int8)
|
int8obj1, ok := obj1.(int8)
|
||||||
int8obj2 := obj2.(int8)
|
if !ok {
|
||||||
|
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8)
|
||||||
|
}
|
||||||
|
int8obj2, ok := obj2.(int8)
|
||||||
|
if !ok {
|
||||||
|
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8)
|
||||||
|
}
|
||||||
if int8obj1 > int8obj2 {
|
if int8obj1 > int8obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
{
|
{
|
||||||
int16obj1 := obj1.(int16)
|
int16obj1, ok := obj1.(int16)
|
||||||
int16obj2 := obj2.(int16)
|
if !ok {
|
||||||
|
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16)
|
||||||
|
}
|
||||||
|
int16obj2, ok := obj2.(int16)
|
||||||
|
if !ok {
|
||||||
|
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16)
|
||||||
|
}
|
||||||
if int16obj1 > int16obj2 {
|
if int16obj1 > int16obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Int32:
|
case reflect.Int32:
|
||||||
{
|
{
|
||||||
int32obj1 := obj1.(int32)
|
int32obj1, ok := obj1.(int32)
|
||||||
int32obj2 := obj2.(int32)
|
if !ok {
|
||||||
|
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32)
|
||||||
|
}
|
||||||
|
int32obj2, ok := obj2.(int32)
|
||||||
|
if !ok {
|
||||||
|
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32)
|
||||||
|
}
|
||||||
if int32obj1 > int32obj2 {
|
if int32obj1 > int32obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
{
|
{
|
||||||
int64obj1 := obj1.(int64)
|
int64obj1, ok := obj1.(int64)
|
||||||
int64obj2 := obj2.(int64)
|
if !ok {
|
||||||
|
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64)
|
||||||
|
}
|
||||||
|
int64obj2, ok := obj2.(int64)
|
||||||
|
if !ok {
|
||||||
|
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64)
|
||||||
|
}
|
||||||
if int64obj1 > int64obj2 {
|
if int64obj1 > int64obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
{
|
{
|
||||||
uintobj1 := obj1.(uint)
|
uintobj1, ok := obj1.(uint)
|
||||||
uintobj2 := obj2.(uint)
|
if !ok {
|
||||||
|
uintobj1 = obj1Value.Convert(uintType).Interface().(uint)
|
||||||
|
}
|
||||||
|
uintobj2, ok := obj2.(uint)
|
||||||
|
if !ok {
|
||||||
|
uintobj2 = obj2Value.Convert(uintType).Interface().(uint)
|
||||||
|
}
|
||||||
if uintobj1 > uintobj2 {
|
if uintobj1 > uintobj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
{
|
{
|
||||||
uint8obj1 := obj1.(uint8)
|
uint8obj1, ok := obj1.(uint8)
|
||||||
uint8obj2 := obj2.(uint8)
|
if !ok {
|
||||||
|
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)
|
||||||
|
}
|
||||||
|
uint8obj2, ok := obj2.(uint8)
|
||||||
|
if !ok {
|
||||||
|
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)
|
||||||
|
}
|
||||||
if uint8obj1 > uint8obj2 {
|
if uint8obj1 > uint8obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
{
|
{
|
||||||
uint16obj1 := obj1.(uint16)
|
uint16obj1, ok := obj1.(uint16)
|
||||||
uint16obj2 := obj2.(uint16)
|
if !ok {
|
||||||
|
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)
|
||||||
|
}
|
||||||
|
uint16obj2, ok := obj2.(uint16)
|
||||||
|
if !ok {
|
||||||
|
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)
|
||||||
|
}
|
||||||
if uint16obj1 > uint16obj2 {
|
if uint16obj1 > uint16obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Uint32:
|
case reflect.Uint32:
|
||||||
{
|
{
|
||||||
uint32obj1 := obj1.(uint32)
|
uint32obj1, ok := obj1.(uint32)
|
||||||
uint32obj2 := obj2.(uint32)
|
if !ok {
|
||||||
|
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)
|
||||||
|
}
|
||||||
|
uint32obj2, ok := obj2.(uint32)
|
||||||
|
if !ok {
|
||||||
|
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)
|
||||||
|
}
|
||||||
if uint32obj1 > uint32obj2 {
|
if uint32obj1 > uint32obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
{
|
{
|
||||||
uint64obj1 := obj1.(uint64)
|
uint64obj1, ok := obj1.(uint64)
|
||||||
uint64obj2 := obj2.(uint64)
|
if !ok {
|
||||||
|
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)
|
||||||
|
}
|
||||||
|
uint64obj2, ok := obj2.(uint64)
|
||||||
|
if !ok {
|
||||||
|
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)
|
||||||
|
}
|
||||||
if uint64obj1 > uint64obj2 {
|
if uint64obj1 > uint64obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
{
|
{
|
||||||
float32obj1 := obj1.(float32)
|
float32obj1, ok := obj1.(float32)
|
||||||
float32obj2 := obj2.(float32)
|
if !ok {
|
||||||
|
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32)
|
||||||
|
}
|
||||||
|
float32obj2, ok := obj2.(float32)
|
||||||
|
if !ok {
|
||||||
|
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32)
|
||||||
|
}
|
||||||
if float32obj1 > float32obj2 {
|
if float32obj1 > float32obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
{
|
{
|
||||||
float64obj1 := obj1.(float64)
|
float64obj1, ok := obj1.(float64)
|
||||||
float64obj2 := obj2.(float64)
|
if !ok {
|
||||||
|
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64)
|
||||||
|
}
|
||||||
|
float64obj2, ok := obj2.(float64)
|
||||||
|
if !ok {
|
||||||
|
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64)
|
||||||
|
}
|
||||||
if float64obj1 > float64obj2 {
|
if float64obj1 > float64obj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||||
}
|
}
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
{
|
{
|
||||||
stringobj1 := obj1.(string)
|
stringobj1, ok := obj1.(string)
|
||||||
stringobj2 := obj2.(string)
|
if !ok {
|
||||||
|
stringobj1 = obj1Value.Convert(stringType).Interface().(string)
|
||||||
|
}
|
||||||
|
stringobj2, ok := obj2.(string)
|
||||||
|
if !ok {
|
||||||
|
stringobj2 = obj2Value.Convert(stringType).Interface().(string)
|
||||||
|
}
|
||||||
if stringobj1 > stringobj2 {
|
if stringobj1 > stringobj2 {
|
||||||
return compareGreater, true
|
return compareGreater, true
|
||||||
}
|
}
|
||||||
|
@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
|
||||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Positive asserts that the specified element is positive
|
||||||
|
//
|
||||||
|
// assert.Positive(t, 1)
|
||||||
|
// assert.Positive(t, 1.23)
|
||||||
|
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
zero := reflect.Zero(reflect.TypeOf(e))
|
||||||
|
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative asserts that the specified element is negative
|
||||||
|
//
|
||||||
|
// assert.Negative(t, -1)
|
||||||
|
// assert.Negative(t, -1.23)
|
||||||
|
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
zero := reflect.Zero(reflect.TypeOf(e))
|
||||||
|
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
h.Helper()
|
h.Helper()
|
||||||
|
|
|
@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||||
return Error(t, err, append([]interface{}{msg}, args...)...)
|
return Error(t, err, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||||
|
// This is a wrapper for errors.As.
|
||||||
|
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
// periodically checking target function each tick.
|
// periodically checking target function each tick.
|
||||||
//
|
//
|
||||||
|
@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
|
||||||
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDecreasingf asserts that the collection is decreasing
|
||||||
|
//
|
||||||
|
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||||
|
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||||
|
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||||
|
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsDecreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIncreasingf asserts that the collection is increasing
|
||||||
|
//
|
||||||
|
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||||
|
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||||
|
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||||
|
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsIncreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||||
|
//
|
||||||
|
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||||
|
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||||
|
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||||
|
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonIncreasingf asserts that the collection is not increasing
|
||||||
|
//
|
||||||
|
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||||
|
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||||
|
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||||
|
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// IsTypef asserts that the specified objects are of the same type.
|
// IsTypef asserts that the specified objects are of the same type.
|
||||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
|
@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
||||||
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
|
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Negativef asserts that the specified element is negative
|
||||||
|
//
|
||||||
|
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||||
|
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||||
|
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Negative(t, e, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
||||||
// periodically checking the target function each tick.
|
// periodically checking the target function each tick.
|
||||||
//
|
//
|
||||||
|
@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
|
||||||
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// NotNilf asserts that the specified object is not nil.
|
// NotNilf asserts that the specified object is not nil.
|
||||||
//
|
//
|
||||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||||
|
@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||||
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
|
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Positivef asserts that the specified element is positive
|
||||||
|
//
|
||||||
|
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||||
|
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||||
|
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Positive(t, e, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Regexpf asserts that a specified regexp matches a string.
|
// Regexpf asserts that a specified regexp matches a string.
|
||||||
//
|
//
|
||||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||||
|
|
|
@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||||
return Error(a.t, err, msgAndArgs...)
|
return Error(a.t, err, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||||
|
// This is a wrapper for errors.As.
|
||||||
|
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorAs(a.t, err, target, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||||
|
// This is a wrapper for errors.As.
|
||||||
|
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorAsf(a.t, err, target, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorIs(a.t, err, target, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return ErrorIsf(a.t, err, target, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||||
//
|
//
|
||||||
// actualObj, err := SomeFunction()
|
// actualObj, err := SomeFunction()
|
||||||
|
@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
|
||||||
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
|
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDecreasing asserts that the collection is decreasing
|
||||||
|
//
|
||||||
|
// a.IsDecreasing([]int{2, 1, 0})
|
||||||
|
// a.IsDecreasing([]float{2, 1})
|
||||||
|
// a.IsDecreasing([]string{"b", "a"})
|
||||||
|
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsDecreasing(a.t, object, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDecreasingf asserts that the collection is decreasing
|
||||||
|
//
|
||||||
|
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
|
||||||
|
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
|
||||||
|
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsDecreasingf(a.t, object, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIncreasing asserts that the collection is increasing
|
||||||
|
//
|
||||||
|
// a.IsIncreasing([]int{1, 2, 3})
|
||||||
|
// a.IsIncreasing([]float{1, 2})
|
||||||
|
// a.IsIncreasing([]string{"a", "b"})
|
||||||
|
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsIncreasing(a.t, object, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIncreasingf asserts that the collection is increasing
|
||||||
|
//
|
||||||
|
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
|
||||||
|
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
|
||||||
|
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsIncreasingf(a.t, object, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonDecreasing asserts that the collection is not decreasing
|
||||||
|
//
|
||||||
|
// a.IsNonDecreasing([]int{1, 1, 2})
|
||||||
|
// a.IsNonDecreasing([]float{1, 2})
|
||||||
|
// a.IsNonDecreasing([]string{"a", "b"})
|
||||||
|
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonDecreasing(a.t, object, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||||
|
//
|
||||||
|
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
|
||||||
|
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
|
||||||
|
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonDecreasingf(a.t, object, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonIncreasing asserts that the collection is not increasing
|
||||||
|
//
|
||||||
|
// a.IsNonIncreasing([]int{2, 1, 1})
|
||||||
|
// a.IsNonIncreasing([]float{2, 1})
|
||||||
|
// a.IsNonIncreasing([]string{"b", "a"})
|
||||||
|
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonIncreasing(a.t, object, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonIncreasingf asserts that the collection is not increasing
|
||||||
|
//
|
||||||
|
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
|
||||||
|
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
|
||||||
|
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return IsNonIncreasingf(a.t, object, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// IsType asserts that the specified objects are of the same type.
|
// IsType asserts that the specified objects are of the same type.
|
||||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
|
||||||
return Lessf(a.t, e1, e2, msg, args...)
|
return Lessf(a.t, e1, e2, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Negative asserts that the specified element is negative
|
||||||
|
//
|
||||||
|
// a.Negative(-1)
|
||||||
|
// a.Negative(-1.23)
|
||||||
|
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Negative(a.t, e, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negativef asserts that the specified element is negative
|
||||||
|
//
|
||||||
|
// a.Negativef(-1, "error message %s", "formatted")
|
||||||
|
// a.Negativef(-1.23, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Negativef(a.t, e, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||||
// periodically checking the target function each tick.
|
// periodically checking the target function each tick.
|
||||||
//
|
//
|
||||||
|
@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
|
||||||
return NotEqualf(a.t, expected, actual, msg, args...)
|
return NotEqualf(a.t, expected, actual, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorIs(a.t, err, target, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorIsf(a.t, err, target, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// NotNil asserts that the specified object is not nil.
|
// NotNil asserts that the specified object is not nil.
|
||||||
//
|
//
|
||||||
// a.NotNil(err)
|
// a.NotNil(err)
|
||||||
|
@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
|
||||||
return Panicsf(a.t, f, msg, args...)
|
return Panicsf(a.t, f, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Positive asserts that the specified element is positive
|
||||||
|
//
|
||||||
|
// a.Positive(1)
|
||||||
|
// a.Positive(1.23)
|
||||||
|
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Positive(a.t, e, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positivef asserts that the specified element is positive
|
||||||
|
//
|
||||||
|
// a.Positivef(1, "error message %s", "formatted")
|
||||||
|
// a.Positivef(1.23, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Positivef(a.t, e, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Regexp asserts that a specified regexp matches a string.
|
// Regexp asserts that a specified regexp matches a string.
|
||||||
//
|
//
|
||||||
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isOrdered checks that collection contains orderable elements.
|
||||||
|
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
objKind := reflect.TypeOf(object).Kind()
|
||||||
|
if objKind != reflect.Slice && objKind != reflect.Array {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
objValue := reflect.ValueOf(object)
|
||||||
|
objLen := objValue.Len()
|
||||||
|
|
||||||
|
if objLen <= 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
value := objValue.Index(0)
|
||||||
|
valueInterface := value.Interface()
|
||||||
|
firstValueKind := value.Kind()
|
||||||
|
|
||||||
|
for i := 1; i < objLen; i++ {
|
||||||
|
prevValue := value
|
||||||
|
prevValueInterface := valueInterface
|
||||||
|
|
||||||
|
value = objValue.Index(i)
|
||||||
|
valueInterface = value.Interface()
|
||||||
|
|
||||||
|
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
|
||||||
|
|
||||||
|
if !isComparable {
|
||||||
|
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !containsValue(allowedComparesResults, compareResult) {
|
||||||
|
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIncreasing asserts that the collection is increasing
|
||||||
|
//
|
||||||
|
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||||
|
// assert.IsIncreasing(t, []float{1, 2})
|
||||||
|
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||||
|
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonIncreasing asserts that the collection is not increasing
|
||||||
|
//
|
||||||
|
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||||
|
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||||
|
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||||
|
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDecreasing asserts that the collection is decreasing
|
||||||
|
//
|
||||||
|
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||||
|
// assert.IsDecreasing(t, []float{2, 1})
|
||||||
|
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||||
|
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonDecreasing asserts that the collection is not decreasing
|
||||||
|
//
|
||||||
|
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||||
|
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||||
|
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||||
|
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||||
|
}
|
|
@ -172,8 +172,8 @@ func isTest(name, prefix string) bool {
|
||||||
if len(name) == len(prefix) { // "Test" is ok
|
if len(name) == len(prefix) { // "Test" is ok
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||||
return !unicode.IsLower(rune)
|
return !unicode.IsLower(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||||
|
@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{
|
||||||
DisableCapacities: true,
|
DisableCapacities: true,
|
||||||
SortKeys: true,
|
SortKeys: true,
|
||||||
DisableMethods: true,
|
DisableMethods: true,
|
||||||
|
MaxDepth: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
type tHelper interface {
|
type tHelper interface {
|
||||||
|
@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorIs asserts that at least one of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
if errors.Is(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedText string
|
||||||
|
if target != nil {
|
||||||
|
expectedText = target.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := buildErrorChainString(err)
|
||||||
|
|
||||||
|
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
|
||||||
|
"expected: %q\n"+
|
||||||
|
"in chain: %s", expectedText, chain,
|
||||||
|
), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
||||||
|
// This is a wrapper for errors.Is.
|
||||||
|
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
if !errors.Is(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedText string
|
||||||
|
if target != nil {
|
||||||
|
expectedText = target.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := buildErrorChainString(err)
|
||||||
|
|
||||||
|
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
|
||||||
|
"found: %q\n"+
|
||||||
|
"in chain: %s", expectedText, chain,
|
||||||
|
), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
|
||||||
|
// This is a wrapper for errors.As.
|
||||||
|
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
if errors.As(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := buildErrorChainString(err)
|
||||||
|
|
||||||
|
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
|
||||||
|
"expected: %q\n"+
|
||||||
|
"in chain: %s", target, chain,
|
||||||
|
), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildErrorChainString(err error) string {
|
||||||
|
if err == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
e := errors.Unwrap(err)
|
||||||
|
chain := fmt.Sprintf("%q", err.Error())
|
||||||
|
for e != nil {
|
||||||
|
chain += fmt.Sprintf("\n\t%q", e.Error())
|
||||||
|
e = errors.Unwrap(e)
|
||||||
|
}
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
|
@ -514,7 +514,7 @@ github.com/go-openapi/swag
|
||||||
github.com/go-sql-driver/mysql
|
github.com/go-sql-driver/mysql
|
||||||
# github.com/godbus/dbus/v5 v5.0.3
|
# github.com/godbus/dbus/v5 v5.0.3
|
||||||
github.com/godbus/dbus/v5
|
github.com/godbus/dbus/v5
|
||||||
# github.com/gofrs/flock v0.7.1
|
# github.com/gofrs/flock v0.8.0
|
||||||
github.com/gofrs/flock
|
github.com/gofrs/flock
|
||||||
# github.com/gogo/googleapis v1.3.2
|
# github.com/gogo/googleapis v1.3.2
|
||||||
github.com/gogo/googleapis/google/rpc
|
github.com/gogo/googleapis/google/rpc
|
||||||
|
@ -595,7 +595,7 @@ github.com/google/gofuzz
|
||||||
# github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
|
# github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/google/tcpproxy
|
github.com/google/tcpproxy
|
||||||
# github.com/google/uuid v1.1.2
|
# github.com/google/uuid v1.2.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/google/uuid
|
github.com/google/uuid
|
||||||
# github.com/googleapis/gax-go/v2 v2.0.5
|
# github.com/googleapis/gax-go/v2 v2.0.5
|
||||||
|
@ -767,7 +767,7 @@ github.com/mitchellh/go-wordwrap
|
||||||
github.com/mitchellh/mapstructure
|
github.com/mitchellh/mapstructure
|
||||||
# github.com/moby/ipvs v1.0.1
|
# github.com/moby/ipvs v1.0.1
|
||||||
github.com/moby/ipvs
|
github.com/moby/ipvs
|
||||||
# github.com/moby/sys/mountinfo v0.1.3
|
# github.com/moby/sys/mountinfo v0.4.1 => github.com/moby/sys/mountinfo v0.1.3
|
||||||
github.com/moby/sys/mountinfo
|
github.com/moby/sys/mountinfo
|
||||||
# github.com/moby/term v0.0.0-20200312100748-672ec06f55cd
|
# github.com/moby/term v0.0.0-20200312100748-672ec06f55cd
|
||||||
github.com/moby/term
|
github.com/moby/term
|
||||||
|
@ -917,7 +917,7 @@ github.com/robfig/cron
|
||||||
# github.com/robfig/cron/v3 v3.0.1
|
# github.com/robfig/cron/v3 v3.0.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/robfig/cron/v3
|
github.com/robfig/cron/v3
|
||||||
# github.com/rootless-containers/rootlesskit v0.10.0
|
# github.com/rootless-containers/rootlesskit v0.14.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/rootless-containers/rootlesskit/pkg/api
|
github.com/rootless-containers/rootlesskit/pkg/api
|
||||||
github.com/rootless-containers/rootlesskit/pkg/api/client
|
github.com/rootless-containers/rootlesskit/pkg/api/client
|
||||||
|
@ -932,6 +932,7 @@ github.com/rootless-containers/rootlesskit/pkg/network/iputils
|
||||||
github.com/rootless-containers/rootlesskit/pkg/network/parentutils
|
github.com/rootless-containers/rootlesskit/pkg/network/parentutils
|
||||||
github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns
|
github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns
|
||||||
github.com/rootless-containers/rootlesskit/pkg/parent
|
github.com/rootless-containers/rootlesskit/pkg/parent
|
||||||
|
github.com/rootless-containers/rootlesskit/pkg/parent/cgrouputil
|
||||||
github.com/rootless-containers/rootlesskit/pkg/parent/idtools
|
github.com/rootless-containers/rootlesskit/pkg/parent/idtools
|
||||||
github.com/rootless-containers/rootlesskit/pkg/port
|
github.com/rootless-containers/rootlesskit/pkg/port
|
||||||
github.com/rootless-containers/rootlesskit/pkg/port/builtin
|
github.com/rootless-containers/rootlesskit/pkg/port/builtin
|
||||||
|
@ -945,21 +946,20 @@ github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy
|
||||||
github.com/rootless-containers/rootlesskit/pkg/port/portutil
|
github.com/rootless-containers/rootlesskit/pkg/port/portutil
|
||||||
github.com/rootless-containers/rootlesskit/pkg/sigproxy
|
github.com/rootless-containers/rootlesskit/pkg/sigproxy
|
||||||
github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal
|
github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal
|
||||||
|
github.com/rootless-containers/rootlesskit/pkg/version
|
||||||
# github.com/rs/xid v1.2.1
|
# github.com/rs/xid v1.2.1
|
||||||
github.com/rs/xid
|
github.com/rs/xid
|
||||||
# github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021
|
# github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021
|
||||||
github.com/rubiojr/go-vhd/vhd
|
github.com/rubiojr/go-vhd/vhd
|
||||||
# github.com/russross/blackfriday v1.5.2
|
# github.com/russross/blackfriday v1.5.2
|
||||||
github.com/russross/blackfriday
|
github.com/russross/blackfriday
|
||||||
# github.com/russross/blackfriday/v2 v2.0.1
|
# github.com/russross/blackfriday/v2 v2.1.0
|
||||||
github.com/russross/blackfriday/v2
|
github.com/russross/blackfriday/v2
|
||||||
# github.com/satori/go.uuid v1.2.0
|
# github.com/satori/go.uuid v1.2.0
|
||||||
github.com/satori/go.uuid
|
github.com/satori/go.uuid
|
||||||
# github.com/seccomp/libseccomp-golang v0.9.1
|
# github.com/seccomp/libseccomp-golang v0.9.1
|
||||||
github.com/seccomp/libseccomp-golang
|
github.com/seccomp/libseccomp-golang
|
||||||
# github.com/shurcooL/sanitized_anchor_name v1.0.0
|
# github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/shurcooL/sanitized_anchor_name
|
|
||||||
# github.com/sirupsen/logrus v1.7.0
|
|
||||||
## explicit
|
## explicit
|
||||||
github.com/sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
# github.com/soheilhy/cmux v0.1.4
|
# github.com/soheilhy/cmux v0.1.4
|
||||||
|
@ -972,7 +972,7 @@ github.com/spf13/cobra
|
||||||
# github.com/spf13/pflag v1.0.5
|
# github.com/spf13/pflag v1.0.5
|
||||||
## explicit
|
## explicit
|
||||||
github.com/spf13/pflag
|
github.com/spf13/pflag
|
||||||
# github.com/stretchr/testify v1.6.1
|
# github.com/stretchr/testify v1.7.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/stretchr/testify/assert
|
github.com/stretchr/testify/assert
|
||||||
# github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
|
# github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
|
||||||
|
@ -1166,7 +1166,7 @@ golang.org/x/crypto/ssh/terminal
|
||||||
# golang.org/x/mod v0.3.0
|
# golang.org/x/mod v0.3.0
|
||||||
golang.org/x/mod/module
|
golang.org/x/mod/module
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.0.0-20201110031124-69a78807bb2b => golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
# golang.org/x/net v0.0.0-20210119194325-5f4716e94777 => golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/net/bpf
|
golang.org/x/net/bpf
|
||||||
golang.org/x/net/context
|
golang.org/x/net/context
|
||||||
|
@ -1197,7 +1197,7 @@ golang.org/x/oauth2/jwt
|
||||||
golang.org/x/sync/errgroup
|
golang.org/x/sync/errgroup
|
||||||
golang.org/x/sync/semaphore
|
golang.org/x/sync/semaphore
|
||||||
golang.org/x/sync/singleflight
|
golang.org/x/sync/singleflight
|
||||||
# golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 => golang.org/x/sys v0.0.0-20201112073958-5cba982894dd
|
# golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 => golang.org/x/sys v0.0.0-20201112073958-5cba982894dd
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/sys/cpu
|
golang.org/x/sys/cpu
|
||||||
golang.org/x/sys/internal/unsafeheader
|
golang.org/x/sys/internal/unsafeheader
|
||||||
|
@ -3011,6 +3011,7 @@ sigs.k8s.io/yaml
|
||||||
# github.com/juju/errors => github.com/k3s-io/nocode v0.0.0-20200630202308-cb097102c09f
|
# 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.20.0-k3s1
|
# github.com/kubernetes-sigs/cri-tools => github.com/k3s-io/cri-tools v1.20.0-k3s1
|
||||||
# github.com/matryer/moq => github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009
|
# github.com/matryer/moq => github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009
|
||||||
|
# github.com/moby/sys/mountinfo => github.com/moby/sys/mountinfo v0.1.3
|
||||||
# github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92
|
# github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92
|
||||||
# github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
# github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
||||||
# go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
# go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
|
||||||
|
|
Loading…
Reference in New Issue