Vendor in latest containers/(storage, image) and runtime-tools
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
e46d9ae23b
commit
fc1a4a31ee
|
|
@ -7,9 +7,9 @@ require (
|
||||||
github.com/containerd/containerd v1.6.8
|
github.com/containerd/containerd v1.6.8
|
||||||
github.com/containernetworking/cni v1.1.2
|
github.com/containernetworking/cni v1.1.2
|
||||||
github.com/containernetworking/plugins v1.1.1
|
github.com/containernetworking/plugins v1.1.1
|
||||||
github.com/containers/image/v5 v5.23.0
|
github.com/containers/image/v5 v5.23.1-0.20221013202101-87afcefe9766
|
||||||
github.com/containers/ocicrypt v1.1.6
|
github.com/containers/ocicrypt v1.1.6
|
||||||
github.com/containers/storage v1.43.0
|
github.com/containers/storage v1.43.1-0.20221014072257-a144fee6f51c
|
||||||
github.com/coreos/go-systemd/v22 v22.4.0
|
github.com/coreos/go-systemd/v22 v22.4.0
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3
|
github.com/cyphar/filepath-securejoin v0.2.3
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
|
@ -28,8 +28,8 @@ require (
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2
|
github.com/opencontainers/image-spec v1.1.0-rc2
|
||||||
github.com/opencontainers/runc v1.1.4
|
github.com/opencontainers/runc v1.1.4
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb
|
||||||
github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7
|
github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86
|
||||||
github.com/opencontainers/selinux v1.10.2
|
github.com/opencontainers/selinux v1.10.2
|
||||||
github.com/pkg/sftp v1.13.5
|
github.com/pkg/sftp v1.13.5
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
|
|
@ -52,7 +52,7 @@ require (
|
||||||
github.com/Microsoft/hcsshim v0.9.4 // indirect
|
github.com/Microsoft/hcsshim v0.9.4 // indirect
|
||||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/chzyer/readline v1.5.1 // indirect
|
github.com/chzyer/readline v1.5.1 // indirect
|
||||||
github.com/containerd/cgroups v1.0.4 // indirect
|
github.com/containerd/cgroups v1.0.4 // indirect
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect
|
github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect
|
||||||
|
|
@ -71,7 +71,7 @@ require (
|
||||||
github.com/imdario/mergo v0.3.13 // indirect
|
github.com/imdario/mergo v0.3.13 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/klauspost/compress v1.15.11 // indirect
|
github.com/klauspost/compress v1.15.11 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/letsencrypt/boulder v0.0.0-20220723181115-27de4befb95e // indirect
|
github.com/letsencrypt/boulder v0.0.0-20220723181115-27de4befb95e // indirect
|
||||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||||
|
|
|
||||||
1327
common/go.sum
1327
common/go.sum
File diff suppressed because it is too large
Load Diff
|
|
@ -1,21 +0,0 @@
|
||||||
language: go
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.4.3
|
|
||||||
- go: 1.5.4
|
|
||||||
- go: 1.6.3
|
|
||||||
- go: 1.7
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
install:
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
script:
|
|
||||||
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
|
|
||||||
-repotoken $COVERALLS_TOKEN
|
|
||||||
- echo "Build examples" ; cd examples && go build
|
|
||||||
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
|
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
semver for golang [](https://travis-ci.org/blang/semver) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master)
|
|
||||||
======
|
|
||||||
|
|
||||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
```bash
|
|
||||||
$ go get github.com/blang/semver
|
|
||||||
```
|
|
||||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
v1, err := semver.Make("1.0.0-beta")
|
|
||||||
v2, err := semver.Make("2.0.0-beta")
|
|
||||||
v1.Compare(v2)
|
|
||||||
```
|
|
||||||
|
|
||||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
|
||||||
|
|
||||||
Why should I use this lib?
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Fully spec compatible
|
|
||||||
- No reflection
|
|
||||||
- No regex
|
|
||||||
- Fully tested (Coverage >99%)
|
|
||||||
- Readable parsing/validation errors
|
|
||||||
- Fast (See [Benchmarks](#benchmarks))
|
|
||||||
- Only Stdlib
|
|
||||||
- Uses values instead of pointers
|
|
||||||
- Many features, see below
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Parsing and validation at all levels
|
|
||||||
- Comparator-like comparisons
|
|
||||||
- Compare Helper Methods
|
|
||||||
- InPlace manipulation
|
|
||||||
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
|
|
||||||
- Wildcards `>=1.x`, `<=2.5.x`
|
|
||||||
- Sortable (implements sort.Interface)
|
|
||||||
- database/sql compatible (sql.Scanner/Valuer)
|
|
||||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
|
||||||
|
|
||||||
Ranges
|
|
||||||
------
|
|
||||||
|
|
||||||
A `Range` is a set of conditions which specify which versions satisfy the range.
|
|
||||||
|
|
||||||
A condition is composed of an operator and a version. The supported operators are:
|
|
||||||
|
|
||||||
- `<1.0.0` Less than `1.0.0`
|
|
||||||
- `<=1.0.0` Less than or equal to `1.0.0`
|
|
||||||
- `>1.0.0` Greater than `1.0.0`
|
|
||||||
- `>=1.0.0` Greater than or equal to `1.0.0`
|
|
||||||
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
|
|
||||||
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
|
|
||||||
|
|
||||||
Note that spaces between the operator and the version will be gracefully tolerated.
|
|
||||||
|
|
||||||
A `Range` can link multiple `Ranges` separated by space:
|
|
||||||
|
|
||||||
Ranges can be linked by logical AND:
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
|
|
||||||
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
|
|
||||||
|
|
||||||
Ranges can also be linked by logical OR:
|
|
||||||
|
|
||||||
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
|
|
||||||
|
|
||||||
AND has a higher precedence than OR. It's not possible to use brackets.
|
|
||||||
|
|
||||||
Ranges can be combined by both AND and OR
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
|
||||||
|
|
||||||
Range usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
v, err := semver.Parse("1.2.3")
|
|
||||||
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
|
|
||||||
if range(v) {
|
|
||||||
//valid
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Example
|
|
||||||
-----
|
|
||||||
|
|
||||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
|
|
||||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
|
||||||
fmt.Printf("Major: %d\n", v.Major)
|
|
||||||
fmt.Printf("Minor: %d\n", v.Minor)
|
|
||||||
fmt.Printf("Patch: %d\n", v.Patch)
|
|
||||||
fmt.Printf("Pre: %s\n", v.Pre)
|
|
||||||
fmt.Printf("Build: %s\n", v.Build)
|
|
||||||
|
|
||||||
// Prerelease versions array
|
|
||||||
if len(v.Pre) > 0 {
|
|
||||||
fmt.Println("Prerelease versions:")
|
|
||||||
for i, pre := range v.Pre {
|
|
||||||
fmt.Printf("%d: %q\n", i, pre)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build meta data array
|
|
||||||
if len(v.Build) > 0 {
|
|
||||||
fmt.Println("Build meta data:")
|
|
||||||
for i, build := range v.Build {
|
|
||||||
fmt.Printf("%d: %q\n", i, build)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v001, err := semver.Make("0.0.1")
|
|
||||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
|
||||||
v001.GT(v) == true
|
|
||||||
v.LT(v001) == true
|
|
||||||
v.GTE(v) == true
|
|
||||||
v.LTE(v) == true
|
|
||||||
|
|
||||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
|
||||||
v001.Compare(v) == 1
|
|
||||||
v.Compare(v001) == -1
|
|
||||||
v.Compare(v) == 0
|
|
||||||
|
|
||||||
// Manipulate Version in place:
|
|
||||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error parsing pre release version: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nValidate versions:")
|
|
||||||
v.Build[0] = "?"
|
|
||||||
|
|
||||||
err = v.Validate()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Validation failed: %s\n", err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Benchmarks
|
|
||||||
-----
|
|
||||||
|
|
||||||
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
|
|
||||||
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
|
|
||||||
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
|
|
||||||
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
|
|
||||||
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
|
|
||||||
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
|
|
||||||
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
|
|
||||||
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
|
|
||||||
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
|
|
||||||
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
See benchmark cases at [semver_test.go](semver_test.go)
|
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
-----
|
|
||||||
|
|
||||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
|
||||||
|
|
||||||
|
|
||||||
Contribution
|
|
||||||
-----
|
|
||||||
|
|
||||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-----
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE) file.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"author": "blang",
|
|
||||||
"bugs": {
|
|
||||||
"URL": "https://github.com/blang/semver/issues",
|
|
||||||
"url": "https://github.com/blang/semver/issues"
|
|
||||||
},
|
|
||||||
"gx": {
|
|
||||||
"dvcsimport": "github.com/blang/semver"
|
|
||||||
},
|
|
||||||
"gxVersion": "0.10.0",
|
|
||||||
"language": "go",
|
|
||||||
"license": "MIT",
|
|
||||||
"name": "semver",
|
|
||||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
|
||||||
"version": "3.5.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -327,7 +327,7 @@ func expandWildcardVersion(parts [][]string) ([][]string, error) {
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
var newParts []string
|
var newParts []string
|
||||||
for _, ap := range p {
|
for _, ap := range p {
|
||||||
if strings.Index(ap, "x") != -1 {
|
if strings.Contains(ap, "x") {
|
||||||
opStr, vStr, err := splitComparatorVersion(ap)
|
opStr, vStr, err := splitComparatorVersion(ap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -26,7 +26,7 @@ type Version struct {
|
||||||
Minor uint64
|
Minor uint64
|
||||||
Patch uint64
|
Patch uint64
|
||||||
Pre []PRVersion
|
Pre []PRVersion
|
||||||
Build []string //No Precendence
|
Build []string //No Precedence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version to string
|
// Version to string
|
||||||
|
|
@ -61,6 +61,18 @@ func (v Version) String() string {
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinalizeVersion discards prerelease and build number and only returns
|
||||||
|
// major, minor and patch number.
|
||||||
|
func (v Version) FinalizeVersion() string {
|
||||||
|
b := make([]byte, 0, 5)
|
||||||
|
b = strconv.AppendUint(b, v.Major, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Minor, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Patch, 10)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
// Equals checks if v is equal to o.
|
// Equals checks if v is equal to o.
|
||||||
func (v Version) Equals(o Version) bool {
|
func (v Version) Equals(o Version) bool {
|
||||||
return (v.Compare(o) == 0)
|
return (v.Compare(o) == 0)
|
||||||
|
|
@ -161,6 +173,27 @@ func (v Version) Compare(o Version) int {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IncrementPatch increments the patch version
|
||||||
|
func (v *Version) IncrementPatch() error {
|
||||||
|
v.Patch++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementMinor increments the minor version
|
||||||
|
func (v *Version) IncrementMinor() error {
|
||||||
|
v.Minor++
|
||||||
|
v.Patch = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementMajor increments the major version
|
||||||
|
func (v *Version) IncrementMajor() error {
|
||||||
|
v.Major++
|
||||||
|
v.Minor = 0
|
||||||
|
v.Patch = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates v and returns error in case
|
// Validate validates v and returns error in case
|
||||||
func (v Version) Validate() error {
|
func (v Version) Validate() error {
|
||||||
// Major, Minor, Patch already validated using uint64
|
// Major, Minor, Patch already validated using uint64
|
||||||
|
|
@ -189,10 +222,10 @@ func (v Version) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||||
func New(s string) (vp *Version, err error) {
|
func New(s string) (*Version, error) {
|
||||||
v, err := Parse(s)
|
v, err := Parse(s)
|
||||||
vp = &v
|
vp := &v
|
||||||
return
|
return vp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||||
|
|
@ -202,14 +235,25 @@ func Make(s string) (Version, error) {
|
||||||
|
|
||||||
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
||||||
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
||||||
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
|
// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions
|
||||||
// with only major and minor components specified
|
// with only major and minor components specified, and removes leading 0s.
|
||||||
func ParseTolerant(s string) (Version, error) {
|
func ParseTolerant(s string) (Version, error) {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
s = strings.TrimPrefix(s, "v")
|
s = strings.TrimPrefix(s, "v")
|
||||||
|
|
||||||
// Split into major.minor.(patch+pr+meta)
|
// Split into major.minor.(patch+pr+meta)
|
||||||
parts := strings.SplitN(s, ".", 3)
|
parts := strings.SplitN(s, ".", 3)
|
||||||
|
// Remove leading zeros.
|
||||||
|
for i, p := range parts {
|
||||||
|
if len(p) > 1 {
|
||||||
|
p = strings.TrimLeft(p, "0")
|
||||||
|
if len(p) == 0 || !strings.ContainsAny(p[0:1], "0123456789") {
|
||||||
|
p = "0" + p
|
||||||
|
}
|
||||||
|
parts[i] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fill up shortened versions.
|
||||||
if len(parts) < 3 {
|
if len(parts) < 3 {
|
||||||
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
||||||
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
||||||
|
|
@ -217,8 +261,8 @@ func ParseTolerant(s string) (Version, error) {
|
||||||
for len(parts) < 3 {
|
for len(parts) < 3 {
|
||||||
parts = append(parts, "0")
|
parts = append(parts, "0")
|
||||||
}
|
}
|
||||||
s = strings.Join(parts, ".")
|
|
||||||
}
|
}
|
||||||
|
s = strings.Join(parts, ".")
|
||||||
|
|
||||||
return Parse(s)
|
return Parse(s)
|
||||||
}
|
}
|
||||||
|
|
@ -416,3 +460,17 @@ func NewBuildVersion(s string) (string, error) {
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinalizeVersion returns the major, minor and patch number only and discards
|
||||||
|
// prerelease and build number.
|
||||||
|
func FinalizeVersion(s string) (string, error) {
|
||||||
|
v, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
v.Pre = nil
|
||||||
|
v.Build = nil
|
||||||
|
|
||||||
|
finalVer := v.String()
|
||||||
|
return finalVer, nil
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ func (v *Version) Scan(src interface{}) (err error) {
|
||||||
case []byte:
|
case []byte:
|
||||||
str = string(src)
|
str = string(src)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
return fmt.Errorf("version.Scan: cannot convert %T to string", src)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, err := Parse(str); err == nil {
|
if t, err := Parse(str); err == nil {
|
||||||
|
|
@ -105,7 +105,7 @@ func newImageDestination(sys *types.SystemContext, ref dirReference) (private.Im
|
||||||
AcceptsForeignLayerURLs: false,
|
AcceptsForeignLayerURLs: false,
|
||||||
MustMatchRuntimeOS: false,
|
MustMatchRuntimeOS: false,
|
||||||
IgnoresEmbeddedDockerReference: false, // N/A, DockerReference() returns nil.
|
IgnoresEmbeddedDockerReference: false, // N/A, DockerReference() returns nil.
|
||||||
HasThreadSafePutBlob: false,
|
HasThreadSafePutBlob: true,
|
||||||
}),
|
}),
|
||||||
NoPutBlobPartialInitialize: stubs.NoPutBlobPartial(ref),
|
NoPutBlobPartialInitialize: stubs.NoPutBlobPartial(ref),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -313,8 +313,14 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
return httpResponseToError(resp, "")
|
err := registryHTTPResponseToError(resp)
|
||||||
|
if resp.StatusCode == http.StatusUnauthorized {
|
||||||
|
err = ErrUnauthorizedForCredentials{Err: err}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchResult holds the information of each matching image
|
// SearchResult holds the information of each matching image
|
||||||
|
|
@ -411,7 +417,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
err := httpResponseToError(resp, "")
|
err := registryHTTPResponseToError(resp)
|
||||||
logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, err)
|
logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, err)
|
||||||
return nil, fmt.Errorf("couldn't search registry %q: %w", registry, err)
|
return nil, fmt.Errorf("couldn't search registry %q: %w", registry, err)
|
||||||
}
|
}
|
||||||
|
|
@ -816,7 +822,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
logrus.Debugf("Ping %s status %d", url.Redacted(), resp.StatusCode)
|
logrus.Debugf("Ping %s status %d", url.Redacted(), resp.StatusCode)
|
||||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
|
||||||
return httpResponseToError(resp, "")
|
return registryHTTPResponseToError(resp)
|
||||||
}
|
}
|
||||||
c.challenges = parseAuthHeader(resp.Header)
|
c.challenges = parseAuthHeader(resp.Header)
|
||||||
c.scheme = scheme
|
c.scheme = scheme
|
||||||
|
|
@ -956,9 +962,10 @@ func (c *dockerClient) getBlob(ctx context.Context, ref dockerReference, info ty
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if err := httpResponseToError(res, "Error fetching blob"); err != nil {
|
if res.StatusCode != http.StatusOK {
|
||||||
|
err := registryHTTPResponseToError(res)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
return nil, 0, err
|
return nil, 0, fmt.Errorf("fetching blob: %w", err)
|
||||||
}
|
}
|
||||||
cache.RecordKnownLocation(ref.Transport(), bicTransportScope(ref), info.Digest, newBICLocationReference(ref))
|
cache.RecordKnownLocation(ref.Transport(), bicTransportScope(ref), info.Digest, newBICLocationReference(ref))
|
||||||
return res.Body, getBlobSize(res), nil
|
return res.Body, getBlobSize(res), nil
|
||||||
|
|
@ -982,13 +989,8 @@ func (c *dockerClient) getOCIDescriptorContents(ctx context.Context, ref dockerR
|
||||||
|
|
||||||
// isManifestUnknownError returns true iff err from fetchManifest is a “manifest unknown” error.
|
// isManifestUnknownError returns true iff err from fetchManifest is a “manifest unknown” error.
|
||||||
func isManifestUnknownError(err error) bool {
|
func isManifestUnknownError(err error) bool {
|
||||||
var errs errcode.Errors
|
var ec errcode.ErrorCoder
|
||||||
if !errors.As(err, &errs) || len(errs) == 0 {
|
if !errors.As(err, &ec) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
err = errs[0]
|
|
||||||
ec, ok := err.(errcode.ErrorCoder)
|
|
||||||
if !ok {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return ec.ErrorCode() == v2.ErrorCodeManifestUnknown
|
return ec.ErrorCode() == v2.ErrorCodeManifestUnknown
|
||||||
|
|
@ -1037,9 +1039,8 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("downloading signatures for %s in %s: %w", manifestDigest, ref.ref.Name(), handleErrorResponse(res))
|
return nil, fmt.Errorf("downloading signatures for %s in %s: %w", manifestDigest, ref.ref.Name(), registryHTTPResponseToError(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize)
|
body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize)
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if err := httpResponseToError(res, "fetching tags list"); err != nil {
|
if res.StatusCode != http.StatusOK {
|
||||||
return nil, err
|
return nil, fmt.Errorf("fetching tags list: %w", registryHTTPResponseToError(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagsHolder struct {
|
var tagsHolder struct {
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.
|
||||||
logrus.Debugf("... not present")
|
logrus.Debugf("... not present")
|
||||||
return false, -1, nil
|
return false, -1, nil
|
||||||
default:
|
default:
|
||||||
return false, -1, fmt.Errorf("failed to read from destination repository %s: %d (%s)", reference.Path(d.ref.ref), res.StatusCode, http.StatusText(res.StatusCode))
|
return false, -1, fmt.Errorf("checking whether a blob %s exists in %s: %w", digest, repo.Name(), registryHTTPResponseToError(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -487,15 +487,10 @@ func successStatus(status int) bool {
|
||||||
return status >= 200 && status <= 399
|
return status >= 200 && status <= 399
|
||||||
}
|
}
|
||||||
|
|
||||||
// isManifestInvalidError returns true iff err from client.HandleErrorResponse is a “manifest invalid” error.
|
// isManifestInvalidError returns true iff err from registryHTTPResponseToError is a “manifest invalid” error.
|
||||||
func isManifestInvalidError(err error) bool {
|
func isManifestInvalidError(err error) bool {
|
||||||
errors, ok := err.(errcode.Errors)
|
var ec errcode.ErrorCoder
|
||||||
if !ok || len(errors) == 0 {
|
if ok := errors.As(err, &ec); !ok {
|
||||||
return false
|
|
||||||
}
|
|
||||||
err = errors[0]
|
|
||||||
ec, ok := err.(errcode.ErrorCoder)
|
|
||||||
if !ok {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maxLookasideSignatures is an arbitrary limit for the total number of signatures we would try to read from a lookaside server,
|
||||||
|
// even if it were broken or malicious and it continued serving an enormous number of items.
|
||||||
|
const maxLookasideSignatures = 128
|
||||||
|
|
||||||
type dockerImageSource struct {
|
type dockerImageSource struct {
|
||||||
impl.Compat
|
impl.Compat
|
||||||
impl.PropertyMethodsInitialize
|
impl.PropertyMethodsInitialize
|
||||||
|
|
@ -372,12 +376,9 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo,
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
return nil, nil, private.BadPartialRequestError{Status: res.Status}
|
return nil, nil, private.BadPartialRequestError{Status: res.Status}
|
||||||
default:
|
default:
|
||||||
err := httpResponseToError(res, "Error fetching partial blob")
|
err := registryHTTPResponseToError(res)
|
||||||
if err == nil {
|
|
||||||
err = fmt.Errorf("invalid status code returned when fetching blob %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
|
|
||||||
}
|
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
return nil, nil, err
|
return nil, nil, fmt.Errorf("fetching partial blob: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,6 +452,10 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst
|
||||||
// NOTE: Keep this in sync with docs/signature-protocols.md!
|
// NOTE: Keep this in sync with docs/signature-protocols.md!
|
||||||
signatures := []signature.Signature{}
|
signatures := []signature.Signature{}
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
|
if i >= maxLookasideSignatures {
|
||||||
|
return nil, fmt.Errorf("server provided %d signatures, assuming that's unreasonable and a server error", maxLookasideSignatures)
|
||||||
|
}
|
||||||
|
|
||||||
url := lookasideStorageURL(s.c.signatureBase, manifestDigest, i)
|
url := lookasideStorageURL(s.c.signatureBase, manifestDigest, i)
|
||||||
signature, missing, err := s.getOneSignature(ctx, url)
|
signature, missing, err := s.getOneSignature(ctx, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -496,10 +501,19 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode == http.StatusNotFound {
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
logrus.Debugf("... got status 404, as expected = end of signatures")
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
} else if res.StatusCode != http.StatusOK {
|
} else if res.StatusCode != http.StatusOK {
|
||||||
return nil, false, fmt.Errorf("reading signature from %s: status %d (%s)", url.Redacted(), res.StatusCode, http.StatusText(res.StatusCode))
|
return nil, false, fmt.Errorf("reading signature from %s: status %d (%s)", url.Redacted(), res.StatusCode, http.StatusText(res.StatusCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentType := res.Header.Get("Content-Type")
|
||||||
|
if mimeType := simplifyContentType(contentType); mimeType == "text/html" {
|
||||||
|
logrus.Warnf("Signature %q has Content-Type %q, unexpected for a signature", url.Redacted(), contentType)
|
||||||
|
// Don’t immediately fail; the lookaside spec does not place any requirements on Content-Type.
|
||||||
|
// If the content really is HTML, it’s going to fail in signature.FromBlob.
|
||||||
|
}
|
||||||
|
|
||||||
sigBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize)
|
sigBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
|
@ -605,16 +619,16 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer get.Body.Close()
|
defer get.Body.Close()
|
||||||
manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch get.StatusCode {
|
switch get.StatusCode {
|
||||||
case http.StatusOK:
|
case http.StatusOK:
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
return fmt.Errorf("Unable to delete %v. Image may not exist or is not stored with a v2 Schema in a v2 registry", ref.ref)
|
return fmt.Errorf("Unable to delete %v. Image may not exist or is not stored with a v2 Schema in a v2 registry", ref.ref)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Failed to delete %v: %s (%v)", ref.ref, manifestBody, get.Status)
|
return fmt.Errorf("deleting %v: %w", ref.ref, registryHTTPResponseToError(get))
|
||||||
|
}
|
||||||
|
manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestDigest, err := manifest.Digest(manifestBody)
|
manifestDigest, err := manifest.Digest(manifestBody)
|
||||||
|
|
@ -630,13 +644,8 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer delete.Body.Close()
|
defer delete.Body.Close()
|
||||||
|
|
||||||
body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if delete.StatusCode != http.StatusAccepted {
|
if delete.StatusCode != http.StatusAccepted {
|
||||||
return fmt.Errorf("Failed to delete %v: %s (%v)", deletePath, string(body), delete.Status)
|
return fmt.Errorf("deleting %v: %w", ref.ref, registryHTTPResponseToError(delete))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -33,7 +36,7 @@ func httpResponseToError(res *http.Response, context string) error {
|
||||||
case http.StatusTooManyRequests:
|
case http.StatusTooManyRequests:
|
||||||
return ErrTooManyRequests
|
return ErrTooManyRequests
|
||||||
case http.StatusUnauthorized:
|
case http.StatusUnauthorized:
|
||||||
err := handleErrorResponse(res)
|
err := registryHTTPResponseToError(res)
|
||||||
return ErrUnauthorizedForCredentials{Err: err}
|
return ErrUnauthorizedForCredentials{Err: err}
|
||||||
default:
|
default:
|
||||||
if context != "" {
|
if context != "" {
|
||||||
|
|
@ -47,12 +50,47 @@ func httpResponseToError(res *http.Response, context string) error {
|
||||||
// registry
|
// registry
|
||||||
func registryHTTPResponseToError(res *http.Response) error {
|
func registryHTTPResponseToError(res *http.Response) error {
|
||||||
err := handleErrorResponse(res)
|
err := handleErrorResponse(res)
|
||||||
if e, ok := err.(*unexpectedHTTPResponseError); ok {
|
// len(errs) == 0 should never be returned by handleErrorResponse; if it does, we don't modify it and let the caller report it as is.
|
||||||
|
if errs, ok := err.(errcode.Errors); ok && len(errs) > 0 {
|
||||||
|
// The docker/distribution registry implementation almost never returns
|
||||||
|
// more than one error in the HTTP body; it seems there is only one
|
||||||
|
// possible instance, where the second error reports a cleanup failure
|
||||||
|
// we don't really care about.
|
||||||
|
//
|
||||||
|
// The only _common_ case where a multi-element error is returned is
|
||||||
|
// created by the handleErrorResponse parser when OAuth authorization fails:
|
||||||
|
// the first element contains errors from a WWW-Authenticate header, the second
|
||||||
|
// element contains errors from the response body.
|
||||||
|
//
|
||||||
|
// In that case the first one is currently _slightly_ more informative (ErrorCodeUnauthorized
|
||||||
|
// for invalid tokens, ErrorCodeDenied for permission denied with a valid token
|
||||||
|
// for the first error, vs. ErrorCodeUnauthorized for both cases for the second error.)
|
||||||
|
//
|
||||||
|
// Also, docker/docker similarly only logs the other errors and returns the
|
||||||
|
// first one.
|
||||||
|
if len(errs) > 1 {
|
||||||
|
logrus.Debugf("Discarding non-primary errors:")
|
||||||
|
for _, err := range errs[1:] {
|
||||||
|
logrus.Debugf(" %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = errs[0]
|
||||||
|
}
|
||||||
|
switch e := err.(type) {
|
||||||
|
case *unexpectedHTTPResponseError:
|
||||||
response := string(e.Response)
|
response := string(e.Response)
|
||||||
if len(response) > 50 {
|
if len(response) > 50 {
|
||||||
response = response[:50] + "..."
|
response = response[:50] + "..."
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("StatusCode: %d, %s", e.StatusCode, response)
|
// %.0w makes e visible to error.Unwrap() without including any text
|
||||||
|
err = fmt.Errorf("StatusCode: %d, %s%.0w", e.StatusCode, response, e)
|
||||||
|
case errcode.Error:
|
||||||
|
// e.Error() is fmt.Sprintf("%s: %s", e.Code.Error(), e.Message, which is usually
|
||||||
|
// rather redundant. So reword it without using e.Code.Error() if e.Message is the default.
|
||||||
|
if e.Message == e.Code.Message() {
|
||||||
|
// %.0w makes e visible to error.Unwrap() without including any text
|
||||||
|
err = fmt.Errorf("%s%.0w", e.Message, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,17 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ImageNotFoundError is used when the OCI structure, in principle, exists and seems valid enough,
|
||||||
|
// but nothing matches the “image” part of the provided reference.
|
||||||
|
type ImageNotFoundError struct {
|
||||||
|
ref ociArchiveReference
|
||||||
|
// We may make members public, or add methods, in the future.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ImageNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("no descriptor found for reference %q", e.ref.image)
|
||||||
|
}
|
||||||
|
|
||||||
type ociArchiveImageSource struct {
|
type ociArchiveImageSource struct {
|
||||||
impl.Compat
|
impl.Compat
|
||||||
|
|
||||||
|
|
@ -35,6 +46,10 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref ociArchiv
|
||||||
|
|
||||||
unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, sys)
|
unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, sys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
var notFound ocilayout.ImageNotFoundError
|
||||||
|
if errors.As(err, ¬Found) {
|
||||||
|
err = ImageNotFoundError{ref: ref}
|
||||||
|
}
|
||||||
if err := tempDirRef.deleteTempDir(); err != nil {
|
if err := tempDirRef.deleteTempDir(); err != nil {
|
||||||
return nil, fmt.Errorf("deleting temp directory %q: %w", tempDirRef.tempDirectory, err)
|
return nil, fmt.Errorf("deleting temp directory %q: %w", tempDirRef.tempDirectory, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,17 @@ import (
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ImageNotFoundError is used when the OCI structure, in principle, exists and seems valid enough,
|
||||||
|
// but nothing matches the “image” part of the provided reference.
|
||||||
|
type ImageNotFoundError struct {
|
||||||
|
ref ociReference
|
||||||
|
// We may make members public, or add methods, in the future.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ImageNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("no descriptor found for reference %q", e.ref.image)
|
||||||
|
}
|
||||||
|
|
||||||
type ociImageSource struct {
|
type ociImageSource struct {
|
||||||
impl.Compat
|
impl.Compat
|
||||||
impl.PropertyMethodsInitialize
|
impl.PropertyMethodsInitialize
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if d == nil {
|
if d == nil {
|
||||||
return imgspecv1.Descriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.image)
|
return imgspecv1.Descriptor{}, ImageNotFoundError{ref}
|
||||||
}
|
}
|
||||||
return *d, nil
|
return *d, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ const (
|
||||||
// VersionMinor is for functionality in a backwards-compatible manner
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
VersionMinor = 23
|
VersionMinor = 23
|
||||||
// VersionPatch is for backwards-compatible bug fixes
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
VersionPatch = 0
|
VersionPatch = 1
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = ""
|
VersionDev = "-dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.43.0
|
1.43.1-dev
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,12 @@ type Container struct {
|
||||||
Flags map[string]interface{} `json:"flags,omitempty"`
|
Flags map[string]interface{} `json:"flags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStore provides bookkeeping for information about Containers.
|
// rwContainerStore provides bookkeeping for information about Containers.
|
||||||
type ContainerStore interface {
|
type rwContainerStore interface {
|
||||||
FileBasedStore
|
fileBasedStore
|
||||||
MetadataStore
|
metadataStore
|
||||||
ContainerBigDataStore
|
containerBigDataStore
|
||||||
FlaggableStore
|
flaggableStore
|
||||||
|
|
||||||
// Create creates a container that has a specified ID (or generates a
|
// Create creates a container that has a specified ID (or generates a
|
||||||
// random one if an empty value is supplied) and optional names,
|
// random one if an empty value is supplied) and optional names,
|
||||||
|
|
@ -221,7 +221,7 @@ func (r *containerStore) Load() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.containers = containers
|
r.containers = containers
|
||||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
r.idindex = truncindex.NewTruncIndex(idlist) // Invalid values in idlist are ignored: they are not a reason to refuse processing the whole store.
|
||||||
r.byid = ids
|
r.byid = ids
|
||||||
r.bylayer = layers
|
r.bylayer = layers
|
||||||
r.byname = names
|
r.byname = names
|
||||||
|
|
@ -243,11 +243,13 @@ func (r *containerStore) Save() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer r.Touch()
|
if err := ioutils.AtomicWriteFile(rpath, jdata, 0600); err != nil {
|
||||||
return ioutils.AtomicWriteFile(rpath, jdata, 0600)
|
return err
|
||||||
|
}
|
||||||
|
return r.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainerStore(dir string) (ContainerStore, error) {
|
func newContainerStore(dir string) (rwContainerStore, error) {
|
||||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -255,8 +257,6 @@ func newContainerStore(dir string) (ContainerStore, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lockfile.Lock()
|
|
||||||
defer lockfile.Unlock()
|
|
||||||
cstore := containerStore{
|
cstore := containerStore{
|
||||||
lockfile: lockfile,
|
lockfile: lockfile,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
|
|
@ -265,6 +265,8 @@ func newContainerStore(dir string) (ContainerStore, error) {
|
||||||
bylayer: make(map[string]*Container),
|
bylayer: make(map[string]*Container),
|
||||||
byname: make(map[string]*Container),
|
byname: make(map[string]*Container),
|
||||||
}
|
}
|
||||||
|
cstore.Lock()
|
||||||
|
defer cstore.Unlock()
|
||||||
if err := cstore.Load(); err != nil {
|
if err := cstore.Load(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +356,9 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||||
}
|
}
|
||||||
r.containers = append(r.containers, container)
|
r.containers = append(r.containers, container)
|
||||||
r.byid[id] = container
|
r.byid[id] = container
|
||||||
r.idindex.Add(id)
|
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||||
|
_ = r.idindex.Add(id)
|
||||||
r.bylayer[layer] = container
|
r.bylayer[layer] = container
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
r.byname[name] = container
|
r.byname[name] = container
|
||||||
|
|
@ -434,7 +438,9 @@ func (r *containerStore) Delete(id string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(r.byid, id)
|
delete(r.byid, id)
|
||||||
r.idindex.Delete(id)
|
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// The store’s Delete method is used on various paths to recover from failures, so this should be robust against partially missing data.
|
||||||
|
_ = r.idindex.Delete(id)
|
||||||
delete(r.bylayer, container.LayerID)
|
delete(r.bylayer, container.LayerID)
|
||||||
for _, name := range container.Names {
|
for _, name := range container.Names {
|
||||||
delete(r.byname, name)
|
delete(r.byname, name)
|
||||||
|
|
@ -617,10 +623,6 @@ func (r *containerStore) Lock() {
|
||||||
r.lockfile.Lock()
|
r.lockfile.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *containerStore) RecursiveLock() {
|
|
||||||
r.lockfile.RecursiveLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *containerStore) RLock() {
|
func (r *containerStore) RLock() {
|
||||||
r.lockfile.RLock()
|
r.lockfile.RLock()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
drivers "github.com/containers/storage/drivers"
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The type definitions in this file exist ONLY to maintain formal API compatibility.
|
||||||
|
// DO NOT ADD ANY NEW METHODS TO THESE INTERFACES.
|
||||||
|
|
||||||
|
// ROFileBasedStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROFileBasedStore interface {
|
||||||
|
Locker
|
||||||
|
Load() error
|
||||||
|
ReloadIfChanged() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWFileBasedStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type RWFileBasedStore interface {
|
||||||
|
Save() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileBasedStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type FileBasedStore interface {
|
||||||
|
ROFileBasedStore
|
||||||
|
RWFileBasedStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROMetadataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROMetadataStore interface {
|
||||||
|
Metadata(id string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWMetadataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type RWMetadataStore interface {
|
||||||
|
SetMetadata(id, metadata string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type MetadataStore interface {
|
||||||
|
ROMetadataStore
|
||||||
|
RWMetadataStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROBigDataStore interface {
|
||||||
|
BigData(id, key string) ([]byte, error)
|
||||||
|
BigDataSize(id, key string) (int64, error)
|
||||||
|
BigDataDigest(id, key string) (digest.Digest, error)
|
||||||
|
BigDataNames(id string) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWImageBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type RWImageBigDataStore interface {
|
||||||
|
SetBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ContainerBigDataStore interface {
|
||||||
|
ROBigDataStore
|
||||||
|
SetBigData(id, key string, data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROLayerBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROLayerBigDataStore interface {
|
||||||
|
BigData(id, key string) (io.ReadCloser, error)
|
||||||
|
BigDataNames(id string) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWLayerBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type RWLayerBigDataStore interface {
|
||||||
|
SetBigData(id, key string, data io.Reader) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerBigDataStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type LayerBigDataStore interface {
|
||||||
|
ROLayerBigDataStore
|
||||||
|
RWLayerBigDataStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlaggableStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type FlaggableStore interface {
|
||||||
|
ClearFlag(id string, flag string) error
|
||||||
|
SetFlag(id string, flag string, value interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ContainerStore interface {
|
||||||
|
FileBasedStore
|
||||||
|
MetadataStore
|
||||||
|
ContainerBigDataStore
|
||||||
|
FlaggableStore
|
||||||
|
Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error)
|
||||||
|
SetNames(id string, names []string) error
|
||||||
|
AddNames(id string, names []string) error
|
||||||
|
RemoveNames(id string, names []string) error
|
||||||
|
Get(id string) (*Container, error)
|
||||||
|
Exists(id string) bool
|
||||||
|
Delete(id string) error
|
||||||
|
Wipe() error
|
||||||
|
Lookup(name string) (string, error)
|
||||||
|
Containers() ([]Container, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROImageStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROImageStore interface {
|
||||||
|
ROFileBasedStore
|
||||||
|
ROMetadataStore
|
||||||
|
ROBigDataStore
|
||||||
|
Exists(id string) bool
|
||||||
|
Get(id string) (*Image, error)
|
||||||
|
Lookup(name string) (string, error)
|
||||||
|
Images() ([]Image, error)
|
||||||
|
ByDigest(d digest.Digest) ([]*Image, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ImageStore interface {
|
||||||
|
ROImageStore
|
||||||
|
RWFileBasedStore
|
||||||
|
RWMetadataStore
|
||||||
|
RWImageBigDataStore
|
||||||
|
FlaggableStore
|
||||||
|
Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error)
|
||||||
|
SetNames(id string, names []string) error
|
||||||
|
AddNames(id string, names []string) error
|
||||||
|
RemoveNames(id string, names []string) error
|
||||||
|
Delete(id string) error
|
||||||
|
Wipe() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROLayerStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type ROLayerStore interface {
|
||||||
|
ROFileBasedStore
|
||||||
|
ROMetadataStore
|
||||||
|
ROLayerBigDataStore
|
||||||
|
Exists(id string) bool
|
||||||
|
Get(id string) (*Layer, error)
|
||||||
|
Status() ([][2]string, error)
|
||||||
|
Changes(from, to string) ([]archive.Change, error)
|
||||||
|
Diff(from, to string, options *DiffOptions) (io.ReadCloser, error)
|
||||||
|
DiffSize(from, to string) (int64, error)
|
||||||
|
Size(name string) (int64, error)
|
||||||
|
Lookup(name string) (string, error)
|
||||||
|
LayersByCompressedDigest(d digest.Digest) ([]Layer, error)
|
||||||
|
LayersByUncompressedDigest(d digest.Digest) ([]Layer, error)
|
||||||
|
Layers() ([]Layer, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerStore is a deprecated interface with no documented way to use it from callers outside of c/storage.
|
||||||
|
//
|
||||||
|
// Deprecated: There is no way to use this from any external user of c/storage to invoke c/storage functionality.
|
||||||
|
type LayerStore interface {
|
||||||
|
ROLayerStore
|
||||||
|
RWFileBasedStore
|
||||||
|
RWMetadataStore
|
||||||
|
FlaggableStore
|
||||||
|
RWLayerBigDataStore
|
||||||
|
Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (*Layer, error)
|
||||||
|
CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error)
|
||||||
|
Put(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error)
|
||||||
|
SetNames(id string, names []string) error
|
||||||
|
AddNames(id string, names []string) error
|
||||||
|
RemoveNames(id string, names []string) error
|
||||||
|
Delete(id string) error
|
||||||
|
Wipe() error
|
||||||
|
Mount(id string, options drivers.MountOpts) (string, error)
|
||||||
|
Unmount(id string, force bool) (bool, error)
|
||||||
|
Mounted(id string) (int, error)
|
||||||
|
ParentOwners(id string) (uids, gids []int, err error)
|
||||||
|
ApplyDiff(to string, diff io.Reader) (int64, error)
|
||||||
|
ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error)
|
||||||
|
CleanupStagingDirectory(stagingDirectory string) error
|
||||||
|
ApplyDiffFromStagingDirectory(id, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error
|
||||||
|
DifferTarget(id string) (string, error)
|
||||||
|
LoadLocked() error
|
||||||
|
PutAdditionalLayer(id string, parentLayer *Layer, names []string, aLayer drivers.AdditionalLayer) (layer *Layer, err error)
|
||||||
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ var (
|
||||||
const defaultPerms = os.FileMode(0555)
|
const defaultPerms = os.FileMode(0555)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("aufs", Init)
|
graphdriver.MustRegister("aufs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driver contains information about the filesystem mounted.
|
// Driver contains information about the filesystem mounted.
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ import (
|
||||||
const defaultPerms = os.FileMode(0555)
|
const defaultPerms = os.FileMode(0555)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("btrfs", Init)
|
graphdriver.MustRegister("btrfs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
type btrfsOptions struct {
|
type btrfsOptions struct {
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func NewNaiveLayerIDMapUpdater(driver ProtoDriver) LayerIDMapUpdater {
|
||||||
// on-disk owner UIDs and GIDs which are "host" values in the first map with
|
// on-disk owner UIDs and GIDs which are "host" values in the first map with
|
||||||
// UIDs and GIDs for "host" values from the second map which correspond to the
|
// UIDs and GIDs for "host" values from the second map which correspond to the
|
||||||
// same "container" IDs.
|
// same "container" IDs.
|
||||||
func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
|
func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) (retErr error) {
|
||||||
driver := n.ProtoDriver
|
driver := n.ProtoDriver
|
||||||
options := MountOpts{
|
options := MountOpts{
|
||||||
MountLabel: mountLabel,
|
MountLabel: mountLabel,
|
||||||
|
|
@ -124,9 +124,7 @@ func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer driverPut(driver, id, &retErr)
|
||||||
driver.Put(id)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ChownPathByMaps(layerFs, toContainer, toHost)
|
return ChownPathByMaps(layerFs, toContainer, toHost)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContai
|
||||||
uid, gid = mappedPair.UID, mappedPair.GID
|
uid, gid = mappedPair.UID, mappedPair.GID
|
||||||
}
|
}
|
||||||
if uid != int(st.Uid) || gid != int(st.Gid) {
|
if uid != int(st.Uid) || gid != int(st.Gid) {
|
||||||
cap, err := system.Lgetxattr(path, "security.capability")
|
capability, err := system.Lgetxattr(path, "security.capability")
|
||||||
if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform {
|
if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform {
|
||||||
return fmt.Errorf("%s: %w", os.Args[0], err)
|
return fmt.Errorf("%s: %w", os.Args[0], err)
|
||||||
}
|
}
|
||||||
|
|
@ -98,8 +98,8 @@ func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContai
|
||||||
return fmt.Errorf("%s: %w", os.Args[0], err)
|
return fmt.Errorf("%s: %w", os.Args[0], err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cap != nil {
|
if capability != nil {
|
||||||
if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil {
|
if err := system.Lsetxattr(path, "security.capability", capability, 0); err != nil {
|
||||||
return fmt.Errorf("%s: %w", os.Args[0], err)
|
return fmt.Errorf("%s: %w", os.Args[0], err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
//go:build !linux || !cgo
|
||||||
// +build !linux !cgo
|
// +build !linux !cgo
|
||||||
|
|
||||||
package copy
|
package copy //nolint: predeclared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -24,7 +25,7 @@ func DirCopy(srcDir, dstDir string, _ Mode, _ bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyRegularToFile copies the content of a file to another
|
// CopyRegularToFile copies the content of a file to another
|
||||||
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error {
|
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint: revive // "func name will be used as copy.CopyRegularToFile by other packages, and that stutters"
|
||||||
f, err := os.Open(srcPath)
|
f, err := os.Open(srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -35,6 +36,6 @@ func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, c
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyRegular copies the content of a file to another
|
// CopyRegular copies the content of a file to another
|
||||||
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error {
|
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint:revive // "func name will be used as copy.CopyRegular by other packages, and that stutters"
|
||||||
return chrootarchive.NewArchiver(nil).CopyWithTar(srcPath, dstPath)
|
return chrootarchive.NewArchiver(nil).CopyWithTar(srcPath, dstPath)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
const defaultPerms = os.FileMode(0555)
|
const defaultPerms = os.FileMode(0555)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("devicemapper", Init)
|
graphdriver.MustRegister("devicemapper", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driver contains the device set mounted and the home directory
|
// Driver contains the device set mounted and the home directory
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ type MountOpts struct {
|
||||||
// Mount label is the MAC Labels to assign to mount point (SELINUX)
|
// Mount label is the MAC Labels to assign to mount point (SELINUX)
|
||||||
MountLabel string
|
MountLabel string
|
||||||
// UidMaps & GidMaps are the User Namespace mappings to be assigned to content in the mount point
|
// UidMaps & GidMaps are the User Namespace mappings to be assigned to content in the mount point
|
||||||
UidMaps []idtools.IDMap // nolint: golint
|
UidMaps []idtools.IDMap //nolint: golint,revive
|
||||||
GidMaps []idtools.IDMap //nolint: golint
|
GidMaps []idtools.IDMap //nolint: golint
|
||||||
Options []string
|
Options []string
|
||||||
|
|
||||||
|
|
@ -279,6 +279,14 @@ func init() {
|
||||||
drivers = make(map[string]InitFunc)
|
drivers = make(map[string]InitFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustRegister registers an InitFunc for the driver, or panics.
|
||||||
|
// It is suitable for package’s init() sections.
|
||||||
|
func MustRegister(name string, initFunc InitFunc) {
|
||||||
|
if err := Register(name, initFunc); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to register containers/storage graph driver %q: %v", name, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register registers an InitFunc for the driver.
|
// Register registers an InitFunc for the driver.
|
||||||
func Register(name string, initFunc InitFunc) error {
|
func Register(name string, initFunc InitFunc) error {
|
||||||
if _, exists := drivers[name]; exists {
|
if _, exists := drivers[name]; exists {
|
||||||
|
|
@ -405,3 +413,21 @@ func scanPriorDrivers(root string) map[string]bool {
|
||||||
}
|
}
|
||||||
return driversMap
|
return driversMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// driverPut is driver.Put, but errors are handled either by updating mainErr or just logging.
|
||||||
|
// Typical usage:
|
||||||
|
//
|
||||||
|
// func …(…) (err error) {
|
||||||
|
// …
|
||||||
|
// defer driverPut(driver, id, &err)
|
||||||
|
// }
|
||||||
|
func driverPut(driver ProtoDriver, id string, mainErr *error) {
|
||||||
|
if err := driver.Put(id); err != nil {
|
||||||
|
err = fmt.Errorf("unmounting layer %s: %w", id, err)
|
||||||
|
if *mainErr == nil {
|
||||||
|
*mainErr = err
|
||||||
|
} else {
|
||||||
|
logrus.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
driver.Put(id)
|
driverPut(driver, id, &err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
|
||||||
}
|
}
|
||||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
driver.Put(id)
|
driverPut(driver, id, &err)
|
||||||
return err
|
return err
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer driver.Put(parent)
|
defer driverPut(driver, parent, &err)
|
||||||
|
|
||||||
changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
|
changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -104,7 +104,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
|
||||||
|
|
||||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
driver.Put(id)
|
driverPut(driver, id, &err)
|
||||||
|
|
||||||
// NaiveDiffDriver compares file metadata with parent layers. Parent layers
|
// NaiveDiffDriver compares file metadata with parent layers. Parent layers
|
||||||
// are extracted from tar's with full second precision on modified time.
|
// are extracted from tar's with full second precision on modified time.
|
||||||
|
|
@ -117,7 +117,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
|
||||||
|
|
||||||
// Changes produces a list of changes between the specified layer
|
// Changes produces a list of changes between the specified layer
|
||||||
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||||
func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
|
func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (_ []archive.Change, retErr error) {
|
||||||
driver := gdw.ProtoDriver
|
driver := gdw.ProtoDriver
|
||||||
|
|
||||||
if idMappings == nil {
|
if idMappings == nil {
|
||||||
|
|
@ -134,7 +134,7 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer driver.Put(id)
|
defer driverPut(driver, id, &retErr)
|
||||||
|
|
||||||
parentFs := ""
|
parentFs := ""
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer driver.Put(parent)
|
defer driverPut(driver, parent, &retErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
|
return archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
|
||||||
|
|
@ -171,7 +171,7 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, options ApplyDiffOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer driver.Put(id)
|
defer driverPut(driver, id, &err)
|
||||||
|
|
||||||
defaultForceMask := os.FileMode(0700)
|
defaultForceMask := os.FileMode(0700)
|
||||||
var forceMask *os.FileMode = nil
|
var forceMask *os.FileMode = nil
|
||||||
|
|
@ -224,7 +224,7 @@ func (gdw *NaiveDiffDriver) DiffSize(id string, idMappings *idtools.IDMappings,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer driver.Put(id)
|
defer driverPut(driver, id, &err)
|
||||||
|
|
||||||
return archive.ChangesSize(layerFs, changes), nil
|
return archive.ChangesSize(layerFs, changes), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("overlay", Init)
|
graphdriver.MustRegister("overlay", Init)
|
||||||
graphdriver.Register("overlay2", Init)
|
graphdriver.MustRegister("overlay2", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasMetacopyOption(opts []string) bool {
|
func hasMetacopyOption(opts []string) bool {
|
||||||
|
|
@ -309,9 +309,11 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
|
fsName, ok := graphdriver.FsNames[fsMagic]
|
||||||
backingFs = fsName
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("filesystem type %#x reported for %s is not supported with 'overlay': %w", fsMagic, filepath.Dir(home), graphdriver.ErrIncompatibleFS)
|
||||||
}
|
}
|
||||||
|
backingFs = fsName
|
||||||
|
|
||||||
runhome := filepath.Join(options.RunRoot, filepath.Base(home))
|
runhome := filepath.Join(options.RunRoot, filepath.Base(home))
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
|
rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ var (
|
||||||
const defaultPerms = os.FileMode(0555)
|
const defaultPerms = os.FileMode(0555)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("vfs", Init)
|
graphdriver.MustRegister("vfs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init returns a new VFS driver.
|
// Init returns a new VFS driver.
|
||||||
|
|
@ -98,7 +98,7 @@ func (d *Driver) Status() [][2]string {
|
||||||
|
|
||||||
// Metadata is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any meta data.
|
// Metadata is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any meta data.
|
||||||
func (d *Driver) Metadata(id string) (map[string]string, error) {
|
func (d *Driver) Metadata(id string) (map[string]string, error) {
|
||||||
return nil, nil
|
return nil, nil //nolint: nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
|
// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ var (
|
||||||
|
|
||||||
// init registers the windows graph drivers to the register.
|
// init registers the windows graph drivers to the register.
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("windowsfilter", InitFilter)
|
graphdriver.MustRegister("windowsfilter", InitFilter)
|
||||||
// DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes
|
// DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes
|
||||||
// debugging issues in the re-exec codepath significantly easier.
|
// debugging issues in the re-exec codepath significantly easier.
|
||||||
if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" {
|
if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ type zfsOptions struct {
|
||||||
const defaultPerms = os.FileMode(0555)
|
const defaultPerms = os.FileMode(0555)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.Register("zfs", Init)
|
graphdriver.MustRegister("zfs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logger returns a zfs logger implementation.
|
// Logger returns a zfs logger implementation.
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,11 @@ type Image struct {
|
||||||
Flags map[string]interface{} `json:"flags,omitempty"`
|
Flags map[string]interface{} `json:"flags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROImageStore provides bookkeeping for information about Images.
|
// roImageStore provides bookkeeping for information about Images.
|
||||||
type ROImageStore interface {
|
type roImageStore interface {
|
||||||
ROFileBasedStore
|
roFileBasedStore
|
||||||
ROMetadataStore
|
roMetadataStore
|
||||||
ROBigDataStore
|
roBigDataStore
|
||||||
|
|
||||||
// Exists checks if there is an image with the given ID or name.
|
// Exists checks if there is an image with the given ID or name.
|
||||||
Exists(id string) bool
|
Exists(id string) bool
|
||||||
|
|
@ -106,10 +106,6 @@ type ROImageStore interface {
|
||||||
// Get retrieves information about an image given an ID or name.
|
// Get retrieves information about an image given an ID or name.
|
||||||
Get(id string) (*Image, error)
|
Get(id string) (*Image, error)
|
||||||
|
|
||||||
// Lookup attempts to translate a name to an ID. Most methods do this
|
|
||||||
// implicitly.
|
|
||||||
Lookup(name string) (string, error)
|
|
||||||
|
|
||||||
// Images returns a slice enumerating the known images.
|
// Images returns a slice enumerating the known images.
|
||||||
Images() ([]Image, error)
|
Images() ([]Image, error)
|
||||||
|
|
||||||
|
|
@ -120,13 +116,13 @@ type ROImageStore interface {
|
||||||
ByDigest(d digest.Digest) ([]*Image, error)
|
ByDigest(d digest.Digest) ([]*Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageStore provides bookkeeping for information about Images.
|
// rwImageStore provides bookkeeping for information about Images.
|
||||||
type ImageStore interface {
|
type rwImageStore interface {
|
||||||
ROImageStore
|
roImageStore
|
||||||
RWFileBasedStore
|
rwFileBasedStore
|
||||||
RWMetadataStore
|
rwMetadataStore
|
||||||
RWImageBigDataStore
|
rwImageBigDataStore
|
||||||
FlaggableStore
|
flaggableStore
|
||||||
|
|
||||||
// Create creates an image that has a specified ID (or a random one) and
|
// Create creates an image that has a specified ID (or a random one) and
|
||||||
// optional names, using the specified layer as its topmost (hopefully
|
// optional names, using the specified layer as its topmost (hopefully
|
||||||
|
|
@ -299,7 +295,7 @@ func (r *imageStore) Load() error {
|
||||||
return ErrDuplicateImageNames
|
return ErrDuplicateImageNames
|
||||||
}
|
}
|
||||||
r.images = images
|
r.images = images
|
||||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
r.idindex = truncindex.NewTruncIndex(idlist) // Invalid values in idlist are ignored: they are not a reason to refuse processing the whole store.
|
||||||
r.byid = ids
|
r.byid = ids
|
||||||
r.byname = names
|
r.byname = names
|
||||||
r.bydigest = digests
|
r.bydigest = digests
|
||||||
|
|
@ -324,11 +320,13 @@ func (r *imageStore) Save() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer r.Touch()
|
if err := ioutils.AtomicWriteFile(rpath, jdata, 0600); err != nil {
|
||||||
return ioutils.AtomicWriteFile(rpath, jdata, 0600)
|
return err
|
||||||
|
}
|
||||||
|
return r.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newImageStore(dir string) (ImageStore, error) {
|
func newImageStore(dir string) (rwImageStore, error) {
|
||||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -336,8 +334,6 @@ func newImageStore(dir string) (ImageStore, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lockfile.Lock()
|
|
||||||
defer lockfile.Unlock()
|
|
||||||
istore := imageStore{
|
istore := imageStore{
|
||||||
lockfile: lockfile,
|
lockfile: lockfile,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
|
|
@ -346,19 +342,19 @@ func newImageStore(dir string) (ImageStore, error) {
|
||||||
byname: make(map[string]*Image),
|
byname: make(map[string]*Image),
|
||||||
bydigest: make(map[digest.Digest][]*Image),
|
bydigest: make(map[digest.Digest][]*Image),
|
||||||
}
|
}
|
||||||
|
istore.Lock()
|
||||||
|
defer istore.Unlock()
|
||||||
if err := istore.Load(); err != nil {
|
if err := istore.Load(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &istore, nil
|
return &istore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newROImageStore(dir string) (ROImageStore, error) {
|
func newROImageStore(dir string) (roImageStore, error) {
|
||||||
lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock"))
|
lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lockfile.RLock()
|
|
||||||
defer lockfile.Unlock()
|
|
||||||
istore := imageStore{
|
istore := imageStore{
|
||||||
lockfile: lockfile,
|
lockfile: lockfile,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
|
|
@ -367,6 +363,8 @@ func newROImageStore(dir string) (ROImageStore, error) {
|
||||||
byname: make(map[string]*Image),
|
byname: make(map[string]*Image),
|
||||||
bydigest: make(map[digest.Digest][]*Image),
|
bydigest: make(map[digest.Digest][]*Image),
|
||||||
}
|
}
|
||||||
|
istore.RLock()
|
||||||
|
defer istore.Unlock()
|
||||||
if err := istore.Load(); err != nil {
|
if err := istore.Load(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -455,7 +453,9 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
|
||||||
return nil, fmt.Errorf("validating digests for new image: %w", err)
|
return nil, fmt.Errorf("validating digests for new image: %w", err)
|
||||||
}
|
}
|
||||||
r.images = append(r.images, image)
|
r.images = append(r.images, image)
|
||||||
r.idindex.Add(id)
|
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||||
|
_ = r.idindex.Add(id)
|
||||||
r.byid[id] = image
|
r.byid[id] = image
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
r.byname[name] = image
|
r.byname[name] = image
|
||||||
|
|
@ -572,7 +572,9 @@ func (r *imageStore) Delete(id string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(r.byid, id)
|
delete(r.byid, id)
|
||||||
r.idindex.Delete(id)
|
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// The store’s Delete method is used on various paths to recover from failures, so this should be robust against partially missing data.
|
||||||
|
_ = r.idindex.Delete(id)
|
||||||
for _, name := range image.Names {
|
for _, name := range image.Names {
|
||||||
delete(r.byname, name)
|
delete(r.byname, name)
|
||||||
}
|
}
|
||||||
|
|
@ -608,13 +610,6 @@ func (r *imageStore) Get(id string) (*Image, error) {
|
||||||
return nil, fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown)
|
return nil, fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageStore) Lookup(name string) (id string, err error) {
|
|
||||||
if image, ok := r.lookup(name); ok {
|
|
||||||
return image.ID, nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *imageStore) Exists(id string) bool {
|
func (r *imageStore) Exists(id string) bool {
|
||||||
_, ok := r.lookup(id)
|
_, ok := r.lookup(id)
|
||||||
return ok
|
return ok
|
||||||
|
|
@ -798,10 +793,6 @@ func (r *imageStore) Lock() {
|
||||||
r.lockfile.Lock()
|
r.lockfile.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageStore) RecursiveLock() {
|
|
||||||
r.lockfile.RecursiveLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *imageStore) RLock() {
|
func (r *imageStore) RLock() {
|
||||||
r.lockfile.RLock()
|
r.lockfile.RLock()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
"github.com/klauspost/pgzip"
|
"github.com/klauspost/pgzip"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vbatts/tar-split/archive/tar"
|
"github.com/vbatts/tar-split/archive/tar"
|
||||||
"github.com/vbatts/tar-split/tar/asm"
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
|
|
@ -137,13 +137,13 @@ type DiffOptions struct {
|
||||||
Compression *archive.Compression
|
Compression *archive.Compression
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROLayerStore wraps a graph driver, adding the ability to refer to layers by
|
// roLayerStore wraps a graph driver, adding the ability to refer to layers by
|
||||||
// name, and keeping track of parent-child relationships, along with a list of
|
// name, and keeping track of parent-child relationships, along with a list of
|
||||||
// all known layers.
|
// all known layers.
|
||||||
type ROLayerStore interface {
|
type roLayerStore interface {
|
||||||
ROFileBasedStore
|
roFileBasedStore
|
||||||
ROMetadataStore
|
roMetadataStore
|
||||||
ROLayerBigDataStore
|
roLayerBigDataStore
|
||||||
|
|
||||||
// Exists checks if a layer with the specified name or ID is known.
|
// Exists checks if a layer with the specified name or ID is known.
|
||||||
Exists(id string) bool
|
Exists(id string) bool
|
||||||
|
|
@ -177,10 +177,6 @@ type ROLayerStore interface {
|
||||||
// found, it returns an error.
|
// found, it returns an error.
|
||||||
Size(name string) (int64, error)
|
Size(name string) (int64, error)
|
||||||
|
|
||||||
// Lookup attempts to translate a name to an ID. Most methods do this
|
|
||||||
// implicitly.
|
|
||||||
Lookup(name string) (string, error)
|
|
||||||
|
|
||||||
// LayersByCompressedDigest returns a slice of the layers with the
|
// LayersByCompressedDigest returns a slice of the layers with the
|
||||||
// specified compressed digest value recorded for them.
|
// specified compressed digest value recorded for them.
|
||||||
LayersByCompressedDigest(d digest.Digest) ([]Layer, error)
|
LayersByCompressedDigest(d digest.Digest) ([]Layer, error)
|
||||||
|
|
@ -193,15 +189,15 @@ type ROLayerStore interface {
|
||||||
Layers() ([]Layer, error)
|
Layers() ([]Layer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LayerStore wraps a graph driver, adding the ability to refer to layers by
|
// rwLayerStore wraps a graph driver, adding the ability to refer to layers by
|
||||||
// name, and keeping track of parent-child relationships, along with a list of
|
// name, and keeping track of parent-child relationships, along with a list of
|
||||||
// all known layers.
|
// all known layers.
|
||||||
type LayerStore interface {
|
type rwLayerStore interface {
|
||||||
ROLayerStore
|
roLayerStore
|
||||||
RWFileBasedStore
|
rwFileBasedStore
|
||||||
RWMetadataStore
|
rwMetadataStore
|
||||||
FlaggableStore
|
flaggableStore
|
||||||
RWLayerBigDataStore
|
rwLayerBigDataStore
|
||||||
|
|
||||||
// Create creates a new layer, optionally giving it a specified ID rather than
|
// Create creates a new layer, optionally giving it a specified ID rather than
|
||||||
// a randomly-generated one, either inheriting data from another specified
|
// a randomly-generated one, either inheriting data from another specified
|
||||||
|
|
@ -270,10 +266,6 @@ type LayerStore interface {
|
||||||
// DifferTarget gets the location where files are stored for the layer.
|
// DifferTarget gets the location where files are stored for the layer.
|
||||||
DifferTarget(id string) (string, error)
|
DifferTarget(id string) (string, error)
|
||||||
|
|
||||||
// LoadLocked wraps Load in a locked state. This means it loads the store
|
|
||||||
// and cleans-up invalid layers if needed.
|
|
||||||
LoadLocked() error
|
|
||||||
|
|
||||||
// PutAdditionalLayer creates a layer using the diff contained in the additional layer
|
// PutAdditionalLayer creates a layer using the diff contained in the additional layer
|
||||||
// store.
|
// store.
|
||||||
// This API is experimental and can be changed without bumping the major version number.
|
// This API is experimental and can be changed without bumping the major version number.
|
||||||
|
|
@ -293,8 +285,6 @@ type layerStore struct {
|
||||||
bymount map[string]*Layer
|
bymount map[string]*Layer
|
||||||
bycompressedsum map[digest.Digest][]string
|
bycompressedsum map[digest.Digest][]string
|
||||||
byuncompressedsum map[digest.Digest][]string
|
byuncompressedsum map[digest.Digest][]string
|
||||||
uidMap []idtools.IDMap
|
|
||||||
gidMap []idtools.IDMap
|
|
||||||
loadMut sync.Mutex
|
loadMut sync.Mutex
|
||||||
layerspathModified time.Time
|
layerspathModified time.Time
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +352,7 @@ func (r *layerStore) Load() error {
|
||||||
compressedsums := make(map[digest.Digest][]string)
|
compressedsums := make(map[digest.Digest][]string)
|
||||||
uncompressedsums := make(map[digest.Digest][]string)
|
uncompressedsums := make(map[digest.Digest][]string)
|
||||||
if r.IsReadWrite() {
|
if r.IsReadWrite() {
|
||||||
label.ClearLabels()
|
selinux.ClearLabels()
|
||||||
}
|
}
|
||||||
if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil {
|
if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil {
|
||||||
idlist = make([]string, 0, len(layers))
|
idlist = make([]string, 0, len(layers))
|
||||||
|
|
@ -383,7 +373,7 @@ func (r *layerStore) Load() error {
|
||||||
uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID)
|
uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID)
|
||||||
}
|
}
|
||||||
if layer.MountLabel != "" {
|
if layer.MountLabel != "" {
|
||||||
label.ReserveLabel(layer.MountLabel)
|
selinux.ReserveLabel(layer.MountLabel)
|
||||||
}
|
}
|
||||||
layer.ReadOnly = !r.IsReadWrite()
|
layer.ReadOnly = !r.IsReadWrite()
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +383,7 @@ func (r *layerStore) Load() error {
|
||||||
return ErrDuplicateLayerNames
|
return ErrDuplicateLayerNames
|
||||||
}
|
}
|
||||||
r.layers = layers
|
r.layers = layers
|
||||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
r.idindex = truncindex.NewTruncIndex(idlist) // Invalid values in idlist are ignored: they are not a reason to refuse processing the whole store.
|
||||||
r.byid = ids
|
r.byid = ids
|
||||||
r.byname = names
|
r.byname = names
|
||||||
r.bycompressedsum = compressedsums
|
r.bycompressedsum = compressedsums
|
||||||
|
|
@ -433,12 +423,6 @@ func (r *layerStore) Load() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) LoadLocked() error {
|
|
||||||
r.lockfile.Lock()
|
|
||||||
defer r.lockfile.Unlock()
|
|
||||||
return r.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *layerStore) loadMounts() error {
|
func (r *layerStore) loadMounts() error {
|
||||||
mounts := make(map[string]*Layer)
|
mounts := make(map[string]*Layer)
|
||||||
mpath := r.mountspath()
|
mpath := r.mountspath()
|
||||||
|
|
@ -479,7 +463,6 @@ func (r *layerStore) loadMounts() error {
|
||||||
func (r *layerStore) Save() error {
|
func (r *layerStore) Save() error {
|
||||||
r.mountsLockfile.Lock()
|
r.mountsLockfile.Lock()
|
||||||
defer r.mountsLockfile.Unlock()
|
defer r.mountsLockfile.Unlock()
|
||||||
defer r.mountsLockfile.Touch()
|
|
||||||
if err := r.saveLayers(); err != nil {
|
if err := r.saveLayers(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -501,8 +484,10 @@ func (r *layerStore) saveLayers() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer r.Touch()
|
if err := ioutils.AtomicWriteFile(rpath, jldata, 0600); err != nil {
|
||||||
return ioutils.AtomicWriteFile(rpath, jldata, 0600)
|
return err
|
||||||
|
}
|
||||||
|
return r.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) saveMounts() error {
|
func (r *layerStore) saveMounts() error {
|
||||||
|
|
@ -533,10 +518,13 @@ func (r *layerStore) saveMounts() error {
|
||||||
if err = ioutils.AtomicWriteFile(mpath, jmdata, 0600); err != nil {
|
if err = ioutils.AtomicWriteFile(mpath, jmdata, 0600); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := r.mountsLockfile.Touch(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return r.loadMounts()
|
return r.loadMounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) newLayerStore(rundir string, layerdir string, driver drivers.Driver) (LayerStore, error) {
|
func (s *store) newLayerStore(rundir string, layerdir string, driver drivers.Driver) (rwLayerStore, error) {
|
||||||
if err := os.MkdirAll(rundir, 0700); err != nil {
|
if err := os.MkdirAll(rundir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -560,8 +548,6 @@ func (s *store) newLayerStore(rundir string, layerdir string, driver drivers.Dri
|
||||||
byid: make(map[string]*Layer),
|
byid: make(map[string]*Layer),
|
||||||
bymount: make(map[string]*Layer),
|
bymount: make(map[string]*Layer),
|
||||||
byname: make(map[string]*Layer),
|
byname: make(map[string]*Layer),
|
||||||
uidMap: copyIDMap(s.uidMap),
|
|
||||||
gidMap: copyIDMap(s.gidMap),
|
|
||||||
}
|
}
|
||||||
rlstore.Lock()
|
rlstore.Lock()
|
||||||
defer rlstore.Unlock()
|
defer rlstore.Unlock()
|
||||||
|
|
@ -571,7 +557,7 @@ func (s *store) newLayerStore(rundir string, layerdir string, driver drivers.Dri
|
||||||
return &rlstore, nil
|
return &rlstore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (ROLayerStore, error) {
|
func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (roLayerStore, error) {
|
||||||
lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock"))
|
lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -685,7 +671,9 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s
|
||||||
|
|
||||||
// TODO: check if necessary fields are filled
|
// TODO: check if necessary fields are filled
|
||||||
r.layers = append(r.layers, layer)
|
r.layers = append(r.layers, layer)
|
||||||
r.idindex.Add(id)
|
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||||
|
_ = r.idindex.Add(id)
|
||||||
r.byid[id] = layer
|
r.byid[id] = layer
|
||||||
for _, name := range names { // names got from the additional layer store won't be used
|
for _, name := range names { // names got from the additional layer store won't be used
|
||||||
r.byname[name] = layer
|
r.byname[name] = layer
|
||||||
|
|
@ -697,7 +685,9 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s
|
||||||
r.byuncompressedsum[layer.UncompressedDigest] = append(r.byuncompressedsum[layer.UncompressedDigest], layer.ID)
|
r.byuncompressedsum[layer.UncompressedDigest] = append(r.byuncompressedsum[layer.UncompressedDigest], layer.ID)
|
||||||
}
|
}
|
||||||
if err := r.Save(); err != nil {
|
if err := r.Save(); err != nil {
|
||||||
r.driver.Remove(id)
|
if err2 := r.driver.Remove(id); err2 != nil {
|
||||||
|
logrus.Errorf("While recovering from a failure to save layers, error deleting layer %#v: %v", id, err2)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return copyLayer(layer), nil
|
return copyLayer(layer), nil
|
||||||
|
|
@ -770,7 +760,7 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||||
parentMappings = &idtools.IDMappings{}
|
parentMappings = &idtools.IDMappings{}
|
||||||
}
|
}
|
||||||
if mountLabel != "" {
|
if mountLabel != "" {
|
||||||
label.ReserveLabel(mountLabel)
|
selinux.ReserveLabel(mountLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before actually creating the layer, make a persistent record of it with incompleteFlag,
|
// Before actually creating the layer, make a persistent record of it with incompleteFlag,
|
||||||
|
|
@ -795,7 +785,9 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||||
BigDataNames: []string{},
|
BigDataNames: []string{},
|
||||||
}
|
}
|
||||||
r.layers = append(r.layers, layer)
|
r.layers = append(r.layers, layer)
|
||||||
r.idindex.Add(id)
|
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// This is on various paths to recover from failures, so this should be robust against partially missing data.
|
||||||
|
_ = r.idindex.Add(id)
|
||||||
r.byid[id] = layer
|
r.byid[id] = layer
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
r.byname[name] = layer
|
r.byname[name] = layer
|
||||||
|
|
@ -947,7 +939,6 @@ func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer r.mountsLockfile.Touch()
|
|
||||||
layer, ok := r.lookup(id)
|
layer, ok := r.lookup(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", ErrLayerUnknown
|
return "", ErrLayerUnknown
|
||||||
|
|
@ -998,7 +989,6 @@ func (r *layerStore) Unmount(id string, force bool) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer r.mountsLockfile.Touch()
|
|
||||||
layer, ok := r.lookup(id)
|
layer, ok := r.lookup(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
||||||
|
|
@ -1279,7 +1269,9 @@ func (r *layerStore) deleteInternal(id string) error {
|
||||||
for _, name := range layer.Names {
|
for _, name := range layer.Names {
|
||||||
delete(r.byname, name)
|
delete(r.byname, name)
|
||||||
}
|
}
|
||||||
r.idindex.Delete(id)
|
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||||
|
// The store’s Delete method is used on various paths to recover from failures, so this should be robust against partially missing data.
|
||||||
|
_ = r.idindex.Delete(id)
|
||||||
mountLabel := layer.MountLabel
|
mountLabel := layer.MountLabel
|
||||||
if layer.MountPoint != "" {
|
if layer.MountPoint != "" {
|
||||||
delete(r.bymount, layer.MountPoint)
|
delete(r.bymount, layer.MountPoint)
|
||||||
|
|
@ -1309,7 +1301,7 @@ func (r *layerStore) deleteInternal(id string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
label.ReleaseLabel(mountLabel)
|
selinux.ReleaseLabel(mountLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1365,13 +1357,6 @@ func (r *layerStore) Delete(id string) error {
|
||||||
return r.Save()
|
return r.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) Lookup(name string) (id string, err error) {
|
|
||||||
if layer, ok := r.lookup(name); ok {
|
|
||||||
return layer.ID, nil
|
|
||||||
}
|
|
||||||
return "", ErrLayerUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *layerStore) Exists(id string) bool {
|
func (r *layerStore) Exists(id string) bool {
|
||||||
_, ok := r.lookup(id)
|
_, ok := r.lookup(id)
|
||||||
return ok
|
return ok
|
||||||
|
|
@ -1472,6 +1457,24 @@ func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeCompressedData copies data from source to compressor, which is on top of pwriter.
|
||||||
|
func writeCompressedData(compressor io.WriteCloser, source io.ReadCloser) error {
|
||||||
|
defer compressor.Close()
|
||||||
|
defer source.Close()
|
||||||
|
_, err := io.Copy(compressor, source)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCompressedDataGoroutine copies data from source to compressor, which is on top of pwriter.
|
||||||
|
// All error must be reported by updating pwriter.
|
||||||
|
func writeCompressedDataGoroutine(pwriter *io.PipeWriter, compressor io.WriteCloser, source io.ReadCloser) {
|
||||||
|
err := errors.New("internal error: unexpected panic in writeCompressedDataGoroutine")
|
||||||
|
defer func() { // Note that this is not the same as {defer dest.CloseWithError(err)}; we need err to be evaluated lazily.
|
||||||
|
_ = pwriter.CloseWithError(err) // CloseWithError(nil) is equivalent to Close(), always returns nil
|
||||||
|
}()
|
||||||
|
err = writeCompressedData(compressor, source)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) {
|
func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) {
|
||||||
var metadata storage.Unpacker
|
var metadata storage.Unpacker
|
||||||
|
|
||||||
|
|
@ -1503,12 +1506,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
|
||||||
preader.Close()
|
preader.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
go func() {
|
go writeCompressedDataGoroutine(pwriter, compressor, rc)
|
||||||
defer pwriter.Close()
|
|
||||||
defer compressor.Close()
|
|
||||||
defer rc.Close()
|
|
||||||
io.Copy(compressor, rc)
|
|
||||||
}()
|
|
||||||
return preader, nil
|
return preader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1825,7 +1823,9 @@ func (r *layerStore) ApplyDiffFromStagingDirectory(id, stagingDirectory string,
|
||||||
}
|
}
|
||||||
for k, v := range diffOutput.BigData {
|
for k, v := range diffOutput.BigData {
|
||||||
if err := r.SetBigData(id, k, bytes.NewReader(v)); err != nil {
|
if err := r.SetBigData(id, k, bytes.NewReader(v)); err != nil {
|
||||||
r.Delete(id)
|
if err2 := r.Delete(id); err2 != nil {
|
||||||
|
logrus.Errorf("While recovering from a failure to set big data, error deleting layer %#v: %v", id, err2)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1895,10 +1895,6 @@ func (r *layerStore) Lock() {
|
||||||
r.lockfile.Lock()
|
r.lockfile.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) RecursiveLock() {
|
|
||||||
r.lockfile.RecursiveLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *layerStore) RLock() {
|
func (r *layerStore) RLock() {
|
||||||
r.lockfile.RLock()
|
r.lockfile.RLock()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -874,7 +874,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
||||||
if err != nil || (!options.IncludeSourceDir && relFilePath == "." && d.IsDir()) {
|
if err != nil || (!options.IncludeSourceDir && relFilePath == "." && d.IsDir()) {
|
||||||
// Error getting relative path OR we are looking
|
// Error getting relative path OR we are looking
|
||||||
// at the source directory path. Skip in both situations.
|
// at the source directory path. Skip in both situations.
|
||||||
return nil
|
return nil //nolint: nilerr
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.IncludeSourceDir && include == "." && relFilePath != "." {
|
if options.IncludeSourceDir && include == "." && relFilePath != "." {
|
||||||
|
|
|
||||||
19
common/vendor/github.com/containers/storage/pkg/archive/archive_bsd.go
generated
vendored
Normal file
19
common/vendor/github.com/containers/storage/pkg/archive/archive_bsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
//go:build freebsd || darwin
|
||||||
|
// +build freebsd darwin
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo, forceMask *os.FileMode) error {
|
||||||
|
permissionsMask := hdrInfo.Mode()
|
||||||
|
if forceMask != nil {
|
||||||
|
permissionsMask = *forceMask
|
||||||
|
}
|
||||||
|
return unix.Fchmodat(unix.AT_FDCWD, path, uint32(permissionsMask), unix.AT_SYMLINK_NOFOLLOW)
|
||||||
|
}
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
//go:build freebsd
|
|
||||||
// +build freebsd
|
|
||||||
|
|
||||||
package archive
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/tar"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/idtools"
|
|
||||||
"github.com/containers/storage/pkg/system"
|
|
||||||
"github.com/containers/storage/pkg/unshare"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fixVolumePathPrefix does platform specific processing to ensure that if
|
|
||||||
// the path being passed in is not in a volume path format, convert it to one.
|
|
||||||
func fixVolumePathPrefix(srcPath string) string {
|
|
||||||
return srcPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// getWalkRoot calculates the root path when performing a TarWithOptions.
|
|
||||||
// We use a separate function as this is platform specific. On Linux, we
|
|
||||||
// can't use filepath.Join(srcPath,include) because this will clean away
|
|
||||||
// a trailing "." or "/" which may be important.
|
|
||||||
func getWalkRoot(srcPath string, include string) string {
|
|
||||||
return srcPath + string(filepath.Separator) + include
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanonicalTarNameForPath returns platform-specific filepath
|
|
||||||
// to canonical posix-style path for tar archival. p is relative
|
|
||||||
// path.
|
|
||||||
func CanonicalTarNameForPath(p string) (string, error) {
|
|
||||||
return p, nil // already unix-style
|
|
||||||
}
|
|
||||||
|
|
||||||
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
|
||||||
// on the platform the archival is done.
|
|
||||||
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
|
||||||
return perm // noop for unix as golang APIs provide perm bits correctly
|
|
||||||
}
|
|
||||||
|
|
||||||
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
|
|
||||||
s, ok := stat.(*syscall.Stat_t)
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
// Currently go does not fill in the major/minors
|
|
||||||
if s.Mode&unix.S_IFBLK != 0 ||
|
|
||||||
s.Mode&unix.S_IFCHR != 0 {
|
|
||||||
hdr.Devmajor = int64(major(uint64(s.Rdev))) // nolint: unconvert
|
|
||||||
hdr.Devminor = int64(minor(uint64(s.Rdev))) // nolint: unconvert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
|
||||||
s, ok := stat.(*syscall.Stat_t)
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
inode = s.Ino
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
|
||||||
s, ok := stat.(*syscall.Stat_t)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t")
|
|
||||||
}
|
|
||||||
return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func major(device uint64) uint64 {
|
|
||||||
return (device >> 8) & 0xfff
|
|
||||||
}
|
|
||||||
|
|
||||||
func minor(device uint64) uint64 {
|
|
||||||
return (device & 0xff) | ((device >> 12) & 0xfff00)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
|
||||||
// createTarFile to handle the following types of header: Block; Char; Fifo
|
|
||||||
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
|
||||||
if unshare.IsRootless() {
|
|
||||||
// cannot create a device if running in user namespace
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := uint32(hdr.Mode & 07777)
|
|
||||||
switch hdr.Typeflag {
|
|
||||||
case tar.TypeBlock:
|
|
||||||
mode |= unix.S_IFBLK
|
|
||||||
case tar.TypeChar:
|
|
||||||
mode |= unix.S_IFCHR
|
|
||||||
case tar.TypeFifo:
|
|
||||||
mode |= unix.S_IFIFO
|
|
||||||
}
|
|
||||||
|
|
||||||
return system.Mknod(path, mode, uint64(system.Mkdev(hdr.Devmajor, hdr.Devminor)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo, forceMask *os.FileMode) error {
|
|
||||||
permissionsMask := hdrInfo.Mode()
|
|
||||||
if forceMask != nil {
|
|
||||||
permissionsMask = *forceMask
|
|
||||||
}
|
|
||||||
p, err := unix.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _, e1 := unix.Syscall(unix.SYS_LCHMOD, uintptr(unsafe.Pointer(p)), uintptr(permissionsMask), 0)
|
|
||||||
if e1 != 0 {
|
|
||||||
return e1
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardlink without following symlinks
|
|
||||||
func handleLLink(targetPath string, path string) error {
|
|
||||||
return unix.Linkat(unix.AT_FDCWD, targetPath, unix.AT_FDCWD, path, 0)
|
|
||||||
}
|
|
||||||
|
|
@ -189,3 +189,22 @@ func GetFileOwner(path string) (uint32, uint32, uint32, error) {
|
||||||
}
|
}
|
||||||
return 0, 0, uint32(f.Mode()), nil
|
return 0, 0, uint32(f.Mode()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo, forceMask *os.FileMode) error {
|
||||||
|
permissionsMask := hdrInfo.Mode()
|
||||||
|
if forceMask != nil {
|
||||||
|
permissionsMask = *forceMask
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeLink {
|
||||||
|
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||||
|
if err := os.Chmod(path, permissionsMask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if hdr.Typeflag != tar.TypeSymlink {
|
||||||
|
if err := os.Chmod(path, permissionsMask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build !windows && !freebsd
|
//go:build !windows
|
||||||
// +build !windows,!freebsd
|
// +build !windows
|
||||||
|
|
||||||
package archive
|
package archive
|
||||||
|
|
||||||
|
|
@ -101,25 +101,6 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||||
return system.Mknod(path, mode, system.Mkdev(hdr.Devmajor, hdr.Devminor))
|
return system.Mknod(path, mode, system.Mkdev(hdr.Devmajor, hdr.Devminor))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo, forceMask *os.FileMode) error {
|
|
||||||
permissionsMask := hdrInfo.Mode()
|
|
||||||
if forceMask != nil {
|
|
||||||
permissionsMask = *forceMask
|
|
||||||
}
|
|
||||||
if hdr.Typeflag == tar.TypeLink {
|
|
||||||
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
|
||||||
if err := os.Chmod(path, permissionsMask); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if hdr.Typeflag != tar.TypeSymlink {
|
|
||||||
if err := os.Chmod(path, permissionsMask); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardlink without symlinks
|
// Hardlink without symlinks
|
||||||
func handleLLink(targetPath, path string) error {
|
func handleLLink(targetPath, path string) error {
|
||||||
// Note: on Linux, the link syscall will not follow symlinks.
|
// Note: on Linux, the link syscall will not follow symlinks.
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func (change *Change) String() string {
|
||||||
return fmt.Sprintf("%s %s", change.Kind, change.Path)
|
return fmt.Sprintf("%s %s", change.Kind, change.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// for sort.Sort
|
// changesByPath implements sort.Interface.
|
||||||
type changesByPath []Change
|
type changesByPath []Change
|
||||||
|
|
||||||
func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path }
|
func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path }
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,9 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform
|
defer func() {
|
||||||
|
_, _ = system.Umask(oldmask) // Ignore err. This can only fail with ErrNotSupportedPlatform, in which case we would have failed above.
|
||||||
|
}()
|
||||||
|
|
||||||
if decompress {
|
if decompress {
|
||||||
layer, err = DecompressStream(layer)
|
layer, err = DecompressStream(layer)
|
||||||
|
|
|
||||||
6
common/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go
generated
vendored
6
common/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go
generated
vendored
|
|
@ -78,7 +78,7 @@ func (f *holesFinder) ReadByte() (int64, byte, error) {
|
||||||
f.state = holesFinderStateFound
|
f.state = holesFinderStateFound
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if f.reader.UnreadByte(); err != nil {
|
if err := f.reader.UnreadByte(); err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
f.state = holesFinderStateRead
|
f.state = holesFinderStateRead
|
||||||
|
|
@ -95,7 +95,7 @@ func (f *holesFinder) ReadByte() (int64, byte, error) {
|
||||||
return holeLen, 0, nil
|
return holeLen, 0, nil
|
||||||
}
|
}
|
||||||
if b != 0 {
|
if b != 0 {
|
||||||
if f.reader.UnreadByte(); err != nil {
|
if err := f.reader.UnreadByte(); err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
f.state = holesFinderStateRead
|
f.state = holesFinderStateRead
|
||||||
|
|
@ -429,7 +429,7 @@ func zstdChunkedWriterWithLevel(out io.Writer, metadata map[string]string, level
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ch <- writeZstdChunkedStream(out, metadata, r, level)
|
ch <- writeZstdChunkedStream(out, metadata, r, level)
|
||||||
io.Copy(io.Discard, r)
|
_, _ = io.Copy(io.Discard, r) // Ordinarily writeZstdChunkedStream consumes all of r. If it fails, ensure the write end never blocks and eventually terminates.
|
||||||
r.Close()
|
r.Close()
|
||||||
close(ch)
|
close(ch)
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ type ImageSourceSeekable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrBadRequest is returned when the request is not valid
|
// ErrBadRequest is returned when the request is not valid
|
||||||
type ErrBadRequest struct {
|
type ErrBadRequest struct { //nolint: errname
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrBadRequest) Error() string {
|
func (e ErrBadRequest) Error() string {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func StickRuntimeDirContents(files []string) ([]string, error) {
|
||||||
runtimeDir, err := GetRuntimeDir()
|
runtimeDir, err := GetRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignore error if runtimeDir is empty
|
// ignore error if runtimeDir is empty
|
||||||
return nil, nil
|
return nil, nil //nolint: nilerr
|
||||||
}
|
}
|
||||||
runtimeDir, err = filepath.Abs(runtimeDir)
|
runtimeDir, err = filepath.Abs(runtimeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ func SetDefaultOptions(opts AtomicFileWriterOptions) {
|
||||||
// temporary file and closing it atomically changes the temporary file to
|
// temporary file and closing it atomically changes the temporary file to
|
||||||
// destination path. Writing and closing concurrently is not allowed.
|
// destination path. Writing and closing concurrently is not allowed.
|
||||||
func NewAtomicFileWriterWithOpts(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (io.WriteCloser, error) {
|
func NewAtomicFileWriterWithOpts(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (io.WriteCloser, error) {
|
||||||
|
return newAtomicFileWriter(filename, perm, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAtomicFileWriter returns WriteCloser so that writing to it writes to a
|
||||||
|
// temporary file and closing it atomically changes the temporary file to
|
||||||
|
// destination path. Writing and closing concurrently is not allowed.
|
||||||
|
func newAtomicFileWriter(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (*atomicFileWriter, error) {
|
||||||
f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
|
f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -55,14 +62,14 @@ func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, err
|
||||||
|
|
||||||
// AtomicWriteFile atomically writes data to a file named by filename.
|
// AtomicWriteFile atomically writes data to a file named by filename.
|
||||||
func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
|
func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||||
f, err := NewAtomicFileWriter(filename, perm)
|
f, err := newAtomicFileWriter(filename, perm, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n, err := f.Write(data)
|
n, err := f.Write(data)
|
||||||
if err == nil && n < len(data) {
|
if err == nil && n < len(data) {
|
||||||
err = io.ErrShortWrite
|
err = io.ErrShortWrite
|
||||||
f.(*atomicFileWriter).writeErr = err
|
f.writeErr = err
|
||||||
}
|
}
|
||||||
if err1 := f.Close(); err == nil {
|
if err1 := f.Close(); err == nil {
|
||||||
err = err1
|
err = err1
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,6 @@ type Locker interface {
|
||||||
// - tried to lock a read-only lock-file
|
// - tried to lock a read-only lock-file
|
||||||
Lock()
|
Lock()
|
||||||
|
|
||||||
// Acquire a writer lock recursively, allowing for recursive acquisitions
|
|
||||||
// within the same process space.
|
|
||||||
RecursiveLock()
|
|
||||||
|
|
||||||
// Unlock the lock.
|
// Unlock the lock.
|
||||||
// The default unix implementation panics if:
|
// The default unix implementation panics if:
|
||||||
// - unlocking an unlocked lock
|
// - unlocking an unlocked lock
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ type lockfile struct {
|
||||||
locktype int16
|
locktype int16
|
||||||
locked bool
|
locked bool
|
||||||
ro bool
|
ro bool
|
||||||
recursive bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastWriterIDSize = 64 // This must be the same as len(stringid.GenerateRandomID)
|
const lastWriterIDSize = 64 // This must be the same as len(stringid.GenerateRandomID)
|
||||||
|
|
@ -131,7 +130,7 @@ func createLockerForPath(path string, ro bool) (Locker, error) {
|
||||||
|
|
||||||
// lock locks the lockfile via FCTNL(2) based on the specified type and
|
// lock locks the lockfile via FCTNL(2) based on the specified type and
|
||||||
// command.
|
// command.
|
||||||
func (l *lockfile) lock(lType int16, recursive bool) {
|
func (l *lockfile) lock(lType int16) {
|
||||||
lk := unix.Flock_t{
|
lk := unix.Flock_t{
|
||||||
Type: lType,
|
Type: lType,
|
||||||
Whence: int16(os.SEEK_SET),
|
Whence: int16(os.SEEK_SET),
|
||||||
|
|
@ -142,13 +141,7 @@ func (l *lockfile) lock(lType int16, recursive bool) {
|
||||||
case unix.F_RDLCK:
|
case unix.F_RDLCK:
|
||||||
l.rwMutex.RLock()
|
l.rwMutex.RLock()
|
||||||
case unix.F_WRLCK:
|
case unix.F_WRLCK:
|
||||||
if recursive {
|
|
||||||
// NOTE: that's okay as recursive is only set in RecursiveLock(), so
|
|
||||||
// there's no need to protect against hypothetical RDLCK cases.
|
|
||||||
l.rwMutex.RLock()
|
|
||||||
} else {
|
|
||||||
l.rwMutex.Lock()
|
l.rwMutex.Lock()
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("attempted to acquire a file lock of unrecognized type %d", lType))
|
panic(fmt.Sprintf("attempted to acquire a file lock of unrecognized type %d", lType))
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +164,6 @@ func (l *lockfile) lock(lType int16, recursive bool) {
|
||||||
}
|
}
|
||||||
l.locktype = lType
|
l.locktype = lType
|
||||||
l.locked = true
|
l.locked = true
|
||||||
l.recursive = recursive
|
|
||||||
l.counter++
|
l.counter++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,24 +172,13 @@ func (l *lockfile) Lock() {
|
||||||
if l.ro {
|
if l.ro {
|
||||||
panic("can't take write lock on read-only lock file")
|
panic("can't take write lock on read-only lock file")
|
||||||
} else {
|
} else {
|
||||||
l.lock(unix.F_WRLCK, false)
|
l.lock(unix.F_WRLCK)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecursiveLock locks the lockfile as a writer but allows for recursive
|
|
||||||
// acquisitions within the same process space. Note that RLock() will be called
|
|
||||||
// if it's a lockTypReader lock.
|
|
||||||
func (l *lockfile) RecursiveLock() {
|
|
||||||
if l.ro {
|
|
||||||
l.RLock()
|
|
||||||
} else {
|
|
||||||
l.lock(unix.F_WRLCK, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockRead locks the lockfile as a reader.
|
// LockRead locks the lockfile as a reader.
|
||||||
func (l *lockfile) RLock() {
|
func (l *lockfile) RLock() {
|
||||||
l.lock(unix.F_RDLCK, false)
|
l.lock(unix.F_RDLCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock unlocks the lockfile.
|
// Unlock unlocks the lockfile.
|
||||||
|
|
@ -224,7 +205,7 @@ func (l *lockfile) Unlock() {
|
||||||
// file lock.
|
// file lock.
|
||||||
unix.Close(int(l.fd))
|
unix.Close(int(l.fd))
|
||||||
}
|
}
|
||||||
if l.locktype == unix.F_RDLCK || l.recursive {
|
if l.locktype == unix.F_RDLCK {
|
||||||
l.rwMutex.RUnlock()
|
l.rwMutex.RUnlock()
|
||||||
} else {
|
} else {
|
||||||
l.rwMutex.Unlock()
|
l.rwMutex.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package lockfile
|
package lockfile
|
||||||
|
|
@ -36,12 +37,6 @@ func (l *lockfile) Lock() {
|
||||||
l.locked = true
|
l.locked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lockfile) RecursiveLock() {
|
|
||||||
// We don't support Windows but a recursive writer-lock in one process-space
|
|
||||||
// is really a writer lock, so just panic.
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lockfile) RLock() {
|
func (l *lockfile) RLock() {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
l.locked = true
|
l.locked = true
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,30 @@
|
||||||
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package mount
|
package mount
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
func unmount(target string, flags int) error {
|
func unmount(target string, flags int) error {
|
||||||
err := unix.Unmount(target, flags)
|
var err error
|
||||||
if err == nil || err == unix.EINVAL {
|
for i := 0; i < 50; i++ {
|
||||||
|
err = unix.Unmount(target, flags)
|
||||||
|
switch err {
|
||||||
|
case unix.EBUSY:
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
case unix.EINVAL, nil:
|
||||||
// Ignore "not mounted" error here. Note the same error
|
// Ignore "not mounted" error here. Note the same error
|
||||||
// can be returned if flags are invalid, so this code
|
// can be returned if flags are invalid, so this code
|
||||||
// assumes that the flags value is always correct.
|
// assumes that the flags value is always correct.
|
||||||
return nil
|
return nil
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mountError{
|
return &mountError{
|
||||||
|
|
|
||||||
2
common/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go
generated
vendored
2
common/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go
generated
vendored
|
|
@ -43,7 +43,7 @@ func getRelease() (string, error) {
|
||||||
|
|
||||||
prettyNames, err := shellwords.Parse(content[1])
|
prettyNames, err := shellwords.Parse(content[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("kernel version is invalid: %s", err.Error())
|
return "", fmt.Errorf("kernel version is invalid: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(prettyNames) != 2 {
|
if len(prettyNames) != 2 {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Used by chtimes
|
// maxTime is used by chtimes.
|
||||||
var maxTime time.Time
|
var maxTime time.Time
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package system
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/mount"
|
"github.com/containers/storage/pkg/mount"
|
||||||
|
|
@ -65,7 +64,7 @@ func EnsureRemoveAll(dir string) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if pe.Err != syscall.EBUSY {
|
if !IsEBUSY(pe.Err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ var (
|
||||||
|
|
||||||
// ErrAmbiguousPrefix is returned if the prefix was ambiguous
|
// ErrAmbiguousPrefix is returned if the prefix was ambiguous
|
||||||
// (multiple ids for the prefix).
|
// (multiple ids for the prefix).
|
||||||
type ErrAmbiguousPrefix struct {
|
type ErrAmbiguousPrefix struct { //nolint: errname
|
||||||
prefix string
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,6 +42,7 @@ type TruncIndex struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTruncIndex creates a new TruncIndex and initializes with a list of IDs.
|
// NewTruncIndex creates a new TruncIndex and initializes with a list of IDs.
|
||||||
|
// Invalid IDs are _silently_ ignored.
|
||||||
func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
||||||
idx = &TruncIndex{
|
idx = &TruncIndex{
|
||||||
ids: make(map[string]struct{}),
|
ids: make(map[string]struct{}),
|
||||||
|
|
@ -51,7 +52,7 @@ func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
||||||
trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)),
|
trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)),
|
||||||
}
|
}
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
idx.addID(id)
|
_ = idx.addID(id) // Ignore invalid IDs. Duplicate IDs are not a problem.
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +133,8 @@ func (idx *TruncIndex) Get(s string) (string, error) {
|
||||||
func (idx *TruncIndex) Iterate(handler func(id string)) {
|
func (idx *TruncIndex) Iterate(handler func(id string)) {
|
||||||
idx.Lock()
|
idx.Lock()
|
||||||
defer idx.Unlock()
|
defer idx.Unlock()
|
||||||
idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
|
// Ignore the error from Visit: it can only fail if the provided visitor fails, and ours never does.
|
||||||
|
_ = idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
|
||||||
handler(string(prefix))
|
handler(string(prefix))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -336,7 +336,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
fmt.Printf("Failed to read %s %v\n", configFile, err.Error())
|
logrus.Warningf("Failed to read %s %v\n", configFile, err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +399,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
|
||||||
if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup != "" {
|
if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup != "" {
|
||||||
mappings, err := idtools.NewIDMappings(config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup)
|
mappings, err := idtools.NewIDMappings(config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error initializing ID mappings for %s:%s %v\n", config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup, err)
|
logrus.Warningf("Error initializing ID mappings for %s:%s %v\n", config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
storeOptions.UIDMap = mappings.UIDs()
|
storeOptions.UIDMap = mappings.UIDs()
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ func reloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio
|
||||||
fi, err := os.Stat(configFile)
|
fi, err := os.Stat(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
fmt.Printf("Failed to read %s %v\n", configFile, err.Error())
|
logrus.Warningf("Failed to read %s %v\n", configFile, err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,8 @@ func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 {
|
||||||
|
|
||||||
// getMaxSizeFromImage returns the maximum ID used by the specified image.
|
// getMaxSizeFromImage returns the maximum ID used by the specified image.
|
||||||
// The layer stores must be already locked.
|
// The layer stores must be already locked.
|
||||||
func (s *store) getMaxSizeFromImage(image *Image, passwdFile, groupFile string) (uint32, error) {
|
func (s *store) getMaxSizeFromImage(image *Image, passwdFile, groupFile string) (_ uint32, retErr error) {
|
||||||
lstore, err := s.LayerStore()
|
layerStores, err := s.allLayerStores()
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
lstores, err := s.ROLayerStores()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +136,7 @@ func (s *store) getMaxSizeFromImage(image *Image, passwdFile, groupFile string)
|
||||||
layerName := image.TopLayer
|
layerName := image.TopLayer
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
for _, ls := range append([]ROLayerStore{lstore}, lstores...) {
|
for _, ls := range layerStores {
|
||||||
layer, err := ls.Get(layerName)
|
layer, err := ls.Get(layerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -167,7 +163,7 @@ outer:
|
||||||
return 0, fmt.Errorf("cannot find layer %q", layerName)
|
return 0, fmt.Errorf("cannot find layer %q", layerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
rlstore, err := s.LayerStore()
|
rlstore, err := s.getLayerStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +183,15 @@ outer:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer rlstore.Delete(clayer.ID)
|
defer func() {
|
||||||
|
if err2 := rlstore.Delete(clayer.ID); err2 != nil {
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = fmt.Errorf("deleting temporary layer %#v: %w", clayer.ID, err2)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Error deleting temporary layer %#v: %v", clayer.ID, err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
mountOptions := drivers.MountOpts{
|
mountOptions := drivers.MountOpts{
|
||||||
MountLabel: "",
|
MountLabel: "",
|
||||||
|
|
@ -200,7 +204,15 @@ outer:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer rlstore.Unmount(clayer.ID, true)
|
defer func() {
|
||||||
|
if _, err2 := rlstore.Unmount(clayer.ID, true); err2 != nil {
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = fmt.Errorf("unmounting temporary layer %#v: %w", clayer.ID, err2)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Error unmounting temporary layer %#v: %v", clayer.ID, err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
userFilesSize := parseMountedFiles(mountpoint, passwdFile, groupFile)
|
userFilesSize := parseMountedFiles(mountpoint, passwdFile, groupFile)
|
||||||
if userFilesSize > size {
|
if userFilesSize > size {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
|
||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- ppc64le
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
os:
|
os:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
MIT License
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014 Klaus Post
|
Copyright (c) 2014 Klaus Post
|
||||||
|
|
||||||
|
|
@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,12 @@ Content is [Matt Mahoneys 10GB corpus](http://mattmahoney.net/dc/10gb.html). Com
|
||||||
|
|
||||||
Compressor | MB/sec | speedup | size | size overhead (lower=better)
|
Compressor | MB/sec | speedup | size | size overhead (lower=better)
|
||||||
------------|----------|---------|------|---------
|
------------|----------|---------|------|---------
|
||||||
[gzip](http://golang.org/pkg/compress/gzip) (golang) | 15.44MB/s (1 thread) | 1.0x | 4781329307 | 0%
|
[gzip](http://golang.org/pkg/compress/gzip) (golang) | 16.91MB/s (1 thread) | 1.0x | 4781329307 | 0%
|
||||||
[gzip](http://github.com/klauspost/compress/gzip) (klauspost) | 135.04MB/s (1 thread) | 8.74x | 4894858258 | +2.37%
|
[gzip](http://github.com/klauspost/compress/gzip) (klauspost) | 127.10MB/s (1 thread) | 7.52x | 4885366806 | +2.17%
|
||||||
[pgzip](https://github.com/klauspost/pgzip) (klauspost) | 1573.23MB/s| 101.9x | 4902285651 | +2.53%
|
[pgzip](https://github.com/klauspost/pgzip) (klauspost) | 2085.35MB/s| 123.34x | 4886132566 | +2.19%
|
||||||
[bgzf](https://godoc.org/github.com/biogo/hts/bgzf) (biogo) | 361.40MB/s | 23.4x | 4869686090 | +1.85%
|
[pargzip](https://godoc.org/github.com/golang/build/pargzip) (builder) | 334.04MB/s | 19.76x | 4786890417 | +0.12%
|
||||||
[pargzip](https://godoc.org/github.com/golang/build/pargzip) (builder) | 306.01MB/s | 19.8x | 4786890417 | +0.12%
|
|
||||||
|
|
||||||
pgzip also contains a [linear time compression](https://github.com/klauspost/compress#linear-time-compression-huffman-only) mode, that will allow compression at ~250MB per core per second, independent of the content.
|
pgzip also contains a [huffman only compression](https://github.com/klauspost/compress#linear-time-compression-huffman-only) mode, that will allow compression at ~450MB per core per second, largely independent of the content.
|
||||||
|
|
||||||
See the [complete sheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing) for different content types and compression settings.
|
See the [complete sheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing) for different content types and compression settings.
|
||||||
|
|
||||||
|
|
@ -123,7 +122,7 @@ In the example above, the numbers are as follows on a 4 CPU machine:
|
||||||
Decompressor | Time | Speedup
|
Decompressor | Time | Speedup
|
||||||
-------------|------|--------
|
-------------|------|--------
|
||||||
[gzip](http://golang.org/pkg/compress/gzip) (golang) | 1m28.85s | 0%
|
[gzip](http://golang.org/pkg/compress/gzip) (golang) | 1m28.85s | 0%
|
||||||
[pgzip](https://github.com/klauspost/pgzip) (golang) | 43.48s | 104%
|
[pgzip](https://github.com/klauspost/pgzip) (klauspost) | 43.48s | 104%
|
||||||
|
|
||||||
But wait, since gzip decompression is inherently singlethreaded (aside from CRC calculation) how can it be more than 100% faster? Because pgzip due to its design also acts as a buffer. When using unbuffered gzip, you are also waiting for io when you are decompressing. If the gzip decoder can keep up, it will always have data ready for your reader, and you will not be waiting for input to the gzip decompressor to complete.
|
But wait, since gzip decompression is inherently singlethreaded (aside from CRC calculation) how can it be more than 100% faster? Because pgzip due to its design also acts as a buffer. When using unbuffered gzip, you are also waiting for io when you are decompressing. If the gzip decoder can keep up, it will always have data ready for your reader, and you will not be waiting for input to the gzip decompressor to complete.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -513,6 +513,19 @@ func (z *Reader) Read(p []byte) (n int, err error) {
|
||||||
|
|
||||||
func (z *Reader) WriteTo(w io.Writer) (n int64, err error) {
|
func (z *Reader) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
total := int64(0)
|
total := int64(0)
|
||||||
|
avail := z.current[z.roff:]
|
||||||
|
if len(avail) != 0 {
|
||||||
|
n, err := w.Write(avail)
|
||||||
|
if n != len(avail) {
|
||||||
|
return total, io.ErrShortWrite
|
||||||
|
}
|
||||||
|
total += int64(n)
|
||||||
|
if err != nil {
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
z.blockPool <- z.current
|
||||||
|
z.current = nil
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if z.err != nil {
|
if z.err != nil {
|
||||||
return total, z.err
|
return total, z.err
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,12 @@ type Spec struct {
|
||||||
Root *Root `json:"root,omitempty"`
|
Root *Root `json:"root,omitempty"`
|
||||||
// Hostname configures the container's hostname.
|
// Hostname configures the container's hostname.
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
// Domainname configures the container's domainname.
|
||||||
|
Domainname string `json:"domainname,omitempty"`
|
||||||
// Mounts configures additional mounts (on top of Root).
|
// Mounts configures additional mounts (on top of Root).
|
||||||
Mounts []Mount `json:"mounts,omitempty"`
|
Mounts []Mount `json:"mounts,omitempty"`
|
||||||
// Hooks configures callbacks for container lifecycle events.
|
// Hooks configures callbacks for container lifecycle events.
|
||||||
Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris"`
|
Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris,zos"`
|
||||||
// Annotations contains arbitrary metadata for the container.
|
// Annotations contains arbitrary metadata for the container.
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|
||||||
|
|
@ -27,6 +29,8 @@ type Spec struct {
|
||||||
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
||||||
// VM specifies configuration for virtual-machine-based containers.
|
// VM specifies configuration for virtual-machine-based containers.
|
||||||
VM *VM `json:"vm,omitempty" platform:"vm"`
|
VM *VM `json:"vm,omitempty" platform:"vm"`
|
||||||
|
// ZOS is platform-specific configuration for z/OS based containers.
|
||||||
|
ZOS *ZOS `json:"zos,omitempty" platform:"zos"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process contains information to start a specific application inside the container.
|
// Process contains information to start a specific application inside the container.
|
||||||
|
|
@ -49,7 +53,7 @@ type Process struct {
|
||||||
// Capabilities are Linux capabilities that are kept for the process.
|
// Capabilities are Linux capabilities that are kept for the process.
|
||||||
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
|
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
|
||||||
// Rlimits specifies rlimit options to apply to the process.
|
// Rlimits specifies rlimit options to apply to the process.
|
||||||
Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris"`
|
Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris,zos"`
|
||||||
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
|
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
|
||||||
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
|
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
|
||||||
// ApparmorProfile specifies the apparmor profile for the container.
|
// ApparmorProfile specifies the apparmor profile for the container.
|
||||||
|
|
@ -86,11 +90,11 @@ type Box struct {
|
||||||
// User specifies specific user (and group) information for the container process.
|
// User specifies specific user (and group) information for the container process.
|
||||||
type User struct {
|
type User struct {
|
||||||
// UID is the user id.
|
// UID is the user id.
|
||||||
UID uint32 `json:"uid" platform:"linux,solaris"`
|
UID uint32 `json:"uid" platform:"linux,solaris,zos"`
|
||||||
// GID is the group id.
|
// GID is the group id.
|
||||||
GID uint32 `json:"gid" platform:"linux,solaris"`
|
GID uint32 `json:"gid" platform:"linux,solaris,zos"`
|
||||||
// Umask is the umask for the init process.
|
// Umask is the umask for the init process.
|
||||||
Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris"`
|
Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris,zos"`
|
||||||
// AdditionalGids are additional group ids set for the container's process.
|
// AdditionalGids are additional group ids set for the container's process.
|
||||||
AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"`
|
AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"`
|
||||||
// Username is the user name.
|
// Username is the user name.
|
||||||
|
|
@ -110,11 +114,16 @@ type Mount struct {
|
||||||
// Destination is the absolute path where the mount will be placed in the container.
|
// Destination is the absolute path where the mount will be placed in the container.
|
||||||
Destination string `json:"destination"`
|
Destination string `json:"destination"`
|
||||||
// Type specifies the mount kind.
|
// Type specifies the mount kind.
|
||||||
Type string `json:"type,omitempty" platform:"linux,solaris"`
|
Type string `json:"type,omitempty" platform:"linux,solaris,zos"`
|
||||||
// Source specifies the source path of the mount.
|
// Source specifies the source path of the mount.
|
||||||
Source string `json:"source,omitempty"`
|
Source string `json:"source,omitempty"`
|
||||||
// Options are fstab style mount options.
|
// Options are fstab style mount options.
|
||||||
Options []string `json:"options,omitempty"`
|
Options []string `json:"options,omitempty"`
|
||||||
|
|
||||||
|
// UID/GID mappings used for changing file owners w/o calling chown, fs should support it.
|
||||||
|
// Every mount point could have its own mapping.
|
||||||
|
UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty" platform:"linux"`
|
||||||
|
GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty" platform:"linux"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hook specifies a command that is run at a particular event in the lifecycle of a container
|
// Hook specifies a command that is run at a particular event in the lifecycle of a container
|
||||||
|
|
@ -178,7 +187,7 @@ type Linux struct {
|
||||||
// MountLabel specifies the selinux context for the mounts in the container.
|
// MountLabel specifies the selinux context for the mounts in the container.
|
||||||
MountLabel string `json:"mountLabel,omitempty"`
|
MountLabel string `json:"mountLabel,omitempty"`
|
||||||
// IntelRdt contains Intel Resource Director Technology (RDT) information for
|
// IntelRdt contains Intel Resource Director Technology (RDT) information for
|
||||||
// handling resource constraints (e.g., L3 cache, memory bandwidth) for the container
|
// handling resource constraints and monitoring metrics (e.g., L3 cache, memory bandwidth) for the container
|
||||||
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
|
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
|
||||||
// Personality contains configuration for the Linux personality syscall
|
// Personality contains configuration for the Linux personality syscall
|
||||||
Personality *LinuxPersonality `json:"personality,omitempty"`
|
Personality *LinuxPersonality `json:"personality,omitempty"`
|
||||||
|
|
@ -250,8 +259,8 @@ type LinuxInterfacePriority struct {
|
||||||
Priority uint32 `json:"priority"`
|
Priority uint32 `json:"priority"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// linuxBlockIODevice holds major:minor format supported in blkio cgroup
|
// LinuxBlockIODevice holds major:minor format supported in blkio cgroup
|
||||||
type linuxBlockIODevice struct {
|
type LinuxBlockIODevice struct {
|
||||||
// Major is the device's major number.
|
// Major is the device's major number.
|
||||||
Major int64 `json:"major"`
|
Major int64 `json:"major"`
|
||||||
// Minor is the device's minor number.
|
// Minor is the device's minor number.
|
||||||
|
|
@ -260,7 +269,7 @@ type linuxBlockIODevice struct {
|
||||||
|
|
||||||
// LinuxWeightDevice struct holds a `major:minor weight` pair for weightDevice
|
// LinuxWeightDevice struct holds a `major:minor weight` pair for weightDevice
|
||||||
type LinuxWeightDevice struct {
|
type LinuxWeightDevice struct {
|
||||||
linuxBlockIODevice
|
LinuxBlockIODevice
|
||||||
// Weight is the bandwidth rate for the device.
|
// Weight is the bandwidth rate for the device.
|
||||||
Weight *uint16 `json:"weight,omitempty"`
|
Weight *uint16 `json:"weight,omitempty"`
|
||||||
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only
|
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only
|
||||||
|
|
@ -269,7 +278,7 @@ type LinuxWeightDevice struct {
|
||||||
|
|
||||||
// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair
|
// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair
|
||||||
type LinuxThrottleDevice struct {
|
type LinuxThrottleDevice struct {
|
||||||
linuxBlockIODevice
|
LinuxBlockIODevice
|
||||||
// Rate is the IO rate limit per cgroup per device
|
// Rate is the IO rate limit per cgroup per device
|
||||||
Rate uint64 `json:"rate"`
|
Rate uint64 `json:"rate"`
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +337,8 @@ type LinuxCPU struct {
|
||||||
Cpus string `json:"cpus,omitempty"`
|
Cpus string `json:"cpus,omitempty"`
|
||||||
// List of memory nodes in the cpuset. Default is to use any available memory node.
|
// List of memory nodes in the cpuset. Default is to use any available memory node.
|
||||||
Mems string `json:"mems,omitempty"`
|
Mems string `json:"mems,omitempty"`
|
||||||
|
// cgroups are configured with minimum weight, 0: default behavior, 1: SCHED_IDLE.
|
||||||
|
Idle *int64 `json:"idle,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3)
|
// LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3)
|
||||||
|
|
@ -522,11 +533,21 @@ type WindowsMemoryResources struct {
|
||||||
|
|
||||||
// WindowsCPUResources contains CPU resource management settings.
|
// WindowsCPUResources contains CPU resource management settings.
|
||||||
type WindowsCPUResources struct {
|
type WindowsCPUResources struct {
|
||||||
// Number of CPUs available to the container.
|
// Count is the number of CPUs available to the container. It represents the
|
||||||
|
// fraction of the configured processor `count` in a container in relation
|
||||||
|
// to the processors available in the host. The fraction ultimately
|
||||||
|
// determines the portion of processor cycles that the threads in a
|
||||||
|
// container can use during each scheduling interval, as the number of
|
||||||
|
// cycles per 10,000 cycles.
|
||||||
Count *uint64 `json:"count,omitempty"`
|
Count *uint64 `json:"count,omitempty"`
|
||||||
// CPU shares (relative weight to other containers with cpu shares).
|
// Shares limits the share of processor time given to the container relative
|
||||||
|
// to other workloads on the processor. The processor `shares` (`weight` at
|
||||||
|
// the platform level) is a value between 0 and 10000.
|
||||||
Shares *uint16 `json:"shares,omitempty"`
|
Shares *uint16 `json:"shares,omitempty"`
|
||||||
// Specifies the portion of processor cycles that this container can use as a percentage times 100.
|
// Maximum determines the portion of processor cycles that the threads in a
|
||||||
|
// container can use during each scheduling interval, as the number of
|
||||||
|
// cycles per 10,000 cycles. Set processor `maximum` to a percentage times
|
||||||
|
// 100.
|
||||||
Maximum *uint16 `json:"maximum,omitempty"`
|
Maximum *uint16 `json:"maximum,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -613,6 +634,19 @@ type Arch string
|
||||||
// LinuxSeccompFlag is a flag to pass to seccomp(2).
|
// LinuxSeccompFlag is a flag to pass to seccomp(2).
|
||||||
type LinuxSeccompFlag string
|
type LinuxSeccompFlag string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LinuxSeccompFlagLog is a seccomp flag to request all returned
|
||||||
|
// actions except SECCOMP_RET_ALLOW to be logged. An administrator may
|
||||||
|
// override this filter flag by preventing specific actions from being
|
||||||
|
// logged via the /proc/sys/kernel/seccomp/actions_logged file. (since
|
||||||
|
// Linux 4.14)
|
||||||
|
LinuxSeccompFlagLog LinuxSeccompFlag = "SECCOMP_FILTER_FLAG_LOG"
|
||||||
|
|
||||||
|
// LinuxSeccompFlagSpecAllow can be used to disable Speculative Store
|
||||||
|
// Bypass mitigation. (since Linux 4.17)
|
||||||
|
LinuxSeccompFlagSpecAllow LinuxSeccompFlag = "SECCOMP_FILTER_FLAG_SPEC_ALLOW"
|
||||||
|
)
|
||||||
|
|
||||||
// Additional architectures permitted to be used for system calls
|
// Additional architectures permitted to be used for system calls
|
||||||
// By default only the native architecture of the kernel is permitted
|
// By default only the native architecture of the kernel is permitted
|
||||||
const (
|
const (
|
||||||
|
|
@ -683,8 +717,9 @@ type LinuxSyscall struct {
|
||||||
Args []LinuxSeccompArg `json:"args,omitempty"`
|
Args []LinuxSeccompArg `json:"args,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxIntelRdt has container runtime resource constraints for Intel RDT
|
// LinuxIntelRdt has container runtime resource constraints for Intel RDT CAT and MBA
|
||||||
// CAT and MBA features which introduced in Linux 4.10 and 4.12 kernel
|
// features and flags enabling Intel RDT CMT and MBM features.
|
||||||
|
// Intel RDT features are available in Linux 4.14 and newer kernel versions.
|
||||||
type LinuxIntelRdt struct {
|
type LinuxIntelRdt struct {
|
||||||
// The identity for RDT Class of Service
|
// The identity for RDT Class of Service
|
||||||
ClosID string `json:"closID,omitempty"`
|
ClosID string `json:"closID,omitempty"`
|
||||||
|
|
@ -697,4 +732,36 @@ type LinuxIntelRdt struct {
|
||||||
// The unit of memory bandwidth is specified in "percentages" by
|
// The unit of memory bandwidth is specified in "percentages" by
|
||||||
// default, and in "MBps" if MBA Software Controller is enabled.
|
// default, and in "MBps" if MBA Software Controller is enabled.
|
||||||
MemBwSchema string `json:"memBwSchema,omitempty"`
|
MemBwSchema string `json:"memBwSchema,omitempty"`
|
||||||
|
|
||||||
|
// EnableCMT is the flag to indicate if the Intel RDT CMT is enabled. CMT (Cache Monitoring Technology) supports monitoring of
|
||||||
|
// the last-level cache (LLC) occupancy for the container.
|
||||||
|
EnableCMT bool `json:"enableCMT,omitempty"`
|
||||||
|
|
||||||
|
// EnableMBM is the flag to indicate if the Intel RDT MBM is enabled. MBM (Memory Bandwidth Monitoring) supports monitoring of
|
||||||
|
// total and local memory bandwidth for the container.
|
||||||
|
EnableMBM bool `json:"enableMBM,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZOS contains platform-specific configuration for z/OS based containers.
|
||||||
|
type ZOS struct {
|
||||||
|
// Devices are a list of device nodes that are created for the container
|
||||||
|
Devices []ZOSDevice `json:"devices,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZOSDevice represents the mknod information for a z/OS special device file
|
||||||
|
type ZOSDevice struct {
|
||||||
|
// Path to the device.
|
||||||
|
Path string `json:"path"`
|
||||||
|
// Device type, block, char, etc.
|
||||||
|
Type string `json:"type"`
|
||||||
|
// Major is the device's major number.
|
||||||
|
Major int64 `json:"major"`
|
||||||
|
// Minor is the device's minor number.
|
||||||
|
Minor int64 `json:"minor"`
|
||||||
|
// FileMode permission bits for the device.
|
||||||
|
FileMode *os.FileMode `json:"fileMode,omitempty"`
|
||||||
|
// UID of the device.
|
||||||
|
UID *uint32 `json:"uid,omitempty"`
|
||||||
|
// Gid of the device.
|
||||||
|
GID *uint32 `json:"gid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1621,6 +1621,12 @@ func (g *Generator) SetDefaultSeccompActionForce(action string) error {
|
||||||
return seccomp.ParseDefaultActionForce(action, g.Config.Linux.Seccomp)
|
return seccomp.ParseDefaultActionForce(action, g.Config.Linux.Seccomp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDomainName sets g.Config.Domainname
|
||||||
|
func (g *Generator) SetDomainName(domain string) {
|
||||||
|
g.initConfig()
|
||||||
|
g.Config.Domainname = domain
|
||||||
|
}
|
||||||
|
|
||||||
// SetSeccompArchitecture sets the supported seccomp architectures
|
// SetSeccompArchitecture sets the supported seccomp architectures
|
||||||
func (g *Generator) SetSeccompArchitecture(architecture string) error {
|
func (g *Generator) SetSeccompArchitecture(architecture string) error {
|
||||||
g.initConfigLinuxSeccomp()
|
g.initConfigLinuxSeccomp()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/blang/semver"
|
"github.com/blang/semver/v4"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
osFilepath "github.com/opencontainers/runtime-tools/filepath"
|
osFilepath "github.com/opencontainers/runtime-tools/filepath"
|
||||||
|
|
@ -170,8 +170,8 @@ func (v *Validator) CheckJSONSchema() (errs error) {
|
||||||
func (v *Validator) CheckRoot() (errs error) {
|
func (v *Validator) CheckRoot() (errs error) {
|
||||||
logrus.Debugf("check root")
|
logrus.Debugf("check root")
|
||||||
|
|
||||||
if v.platform == "windows" && v.spec.Windows != nil {
|
if v.platform == "windows" {
|
||||||
if v.spec.Windows.HyperV != nil {
|
if v.spec.Windows != nil && v.spec.Windows.HyperV != nil {
|
||||||
if v.spec.Root != nil {
|
if v.spec.Root != nil {
|
||||||
errs = multierror.Append(errs,
|
errs = multierror.Append(errs,
|
||||||
specerror.NewError(specerror.RootOnHyperVNotSet, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
|
specerror.NewError(specerror.RootOnHyperVNotSet, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
|
||||||
|
|
@ -179,12 +179,12 @@ func (v *Validator) CheckRoot() (errs error) {
|
||||||
return
|
return
|
||||||
} else if v.spec.Root == nil {
|
} else if v.spec.Root == nil {
|
||||||
errs = multierror.Append(errs,
|
errs = multierror.Append(errs,
|
||||||
specerror.NewError(specerror.RootOnWindowsRequired, fmt.Errorf("on Windows, for Windows Server Containers, this field is REQUIRED"), rspec.Version))
|
specerror.NewError(specerror.RootOnWindowsRequired, fmt.Errorf("on Windows, for Windows Server Containers, Root is REQUIRED"), rspec.Version))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if v.platform != "windows" && v.spec.Root == nil {
|
} else if v.spec.Root == nil {
|
||||||
errs = multierror.Append(errs,
|
errs = multierror.Append(errs,
|
||||||
specerror.NewError(specerror.RootOnNonWindowsRequired, fmt.Errorf("on all other platforms, this field is REQUIRED"), rspec.Version))
|
specerror.NewError(specerror.RootOnNonWindowsRequired, fmt.Errorf("on all other platforms, Root is REQUIRED"), rspec.Version))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ github.com/VividCortex/ewma
|
||||||
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||||
## explicit
|
## explicit
|
||||||
github.com/acarl005/stripansi
|
github.com/acarl005/stripansi
|
||||||
# github.com/blang/semver v3.5.1+incompatible
|
# github.com/blang/semver/v4 v4.0.0
|
||||||
## explicit
|
## explicit; go 1.14
|
||||||
github.com/blang/semver
|
github.com/blang/semver/v4
|
||||||
# github.com/chzyer/readline v1.5.1
|
# github.com/chzyer/readline v1.5.1
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/chzyer/readline
|
github.com/chzyer/readline
|
||||||
|
|
@ -72,7 +72,7 @@ github.com/containernetworking/cni/pkg/version
|
||||||
# github.com/containernetworking/plugins v1.1.1
|
# github.com/containernetworking/plugins v1.1.1
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/containernetworking/plugins/pkg/ns
|
github.com/containernetworking/plugins/pkg/ns
|
||||||
# github.com/containers/image/v5 v5.23.0
|
# github.com/containers/image/v5 v5.23.1-0.20221013202101-87afcefe9766
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/containers/image/v5/copy
|
github.com/containers/image/v5/copy
|
||||||
github.com/containers/image/v5/directory
|
github.com/containers/image/v5/directory
|
||||||
|
|
@ -151,7 +151,7 @@ github.com/containers/ocicrypt/keywrap/pkcs7
|
||||||
github.com/containers/ocicrypt/spec
|
github.com/containers/ocicrypt/spec
|
||||||
github.com/containers/ocicrypt/utils
|
github.com/containers/ocicrypt/utils
|
||||||
github.com/containers/ocicrypt/utils/keyprovider
|
github.com/containers/ocicrypt/utils/keyprovider
|
||||||
# github.com/containers/storage v1.43.0
|
# github.com/containers/storage v1.43.1-0.20221014072257-a144fee6f51c
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/containers/storage
|
github.com/containers/storage
|
||||||
github.com/containers/storage/drivers
|
github.com/containers/storage/drivers
|
||||||
|
|
@ -321,7 +321,7 @@ github.com/klauspost/compress/internal/cpuinfo
|
||||||
github.com/klauspost/compress/internal/snapref
|
github.com/klauspost/compress/internal/snapref
|
||||||
github.com/klauspost/compress/zstd
|
github.com/klauspost/compress/zstd
|
||||||
github.com/klauspost/compress/zstd/internal/xxhash
|
github.com/klauspost/compress/zstd/internal/xxhash
|
||||||
# github.com/klauspost/pgzip v1.2.5
|
# github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8
|
||||||
## explicit
|
## explicit
|
||||||
github.com/klauspost/pgzip
|
github.com/klauspost/pgzip
|
||||||
# github.com/kr/fs v0.1.0
|
# github.com/kr/fs v0.1.0
|
||||||
|
|
@ -407,10 +407,10 @@ github.com/opencontainers/runc/libcontainer/devices
|
||||||
github.com/opencontainers/runc/libcontainer/user
|
github.com/opencontainers/runc/libcontainer/user
|
||||||
github.com/opencontainers/runc/libcontainer/userns
|
github.com/opencontainers/runc/libcontainer/userns
|
||||||
github.com/opencontainers/runc/libcontainer/utils
|
github.com/opencontainers/runc/libcontainer/utils
|
||||||
# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
# github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb
|
||||||
## explicit
|
## explicit
|
||||||
github.com/opencontainers/runtime-spec/specs-go
|
github.com/opencontainers/runtime-spec/specs-go
|
||||||
# github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7
|
# github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/opencontainers/runtime-tools/error
|
github.com/opencontainers/runtime-tools/error
|
||||||
github.com/opencontainers/runtime-tools/filepath
|
github.com/opencontainers/runtime-tools/filepath
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue