From 383ac12c1f4e0159d59fe69d379d2bac24115fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:15:05 +0000 Subject: [PATCH 1/4] Bump github.com/prometheus/common from 0.39.0 to 0.40.0 Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.39.0 to 0.40.0. - [Release notes](https://github.com/prometheus/common/releases) - [Commits](https://github.com/prometheus/common/compare/v0.39.0...v0.40.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../prometheus/common/expfmt/decode.go | 36 ++- .../prometheus/common/expfmt/text_parse.go | 10 +- .../prometheus/common/model/value.go | 246 +++++++----------- .../prometheus/common/model/value_float.go | 109 ++++++++ .../common/model/value_histogram.go | 174 +++++++++++++ .../prometheus/common/model/value_marshal.go | 131 ++++++++++ .../prometheus/common/model/value_type.go | 83 ++++++ vendor/modules.txt | 2 +- 10 files changed, 622 insertions(+), 175 deletions(-) create mode 100644 vendor/github.com/prometheus/common/model/value_float.go create mode 100644 vendor/github.com/prometheus/common/model/value_histogram.go create mode 100644 vendor/github.com/prometheus/common/model/value_marshal.go create mode 100644 vendor/github.com/prometheus/common/model/value_type.go diff --git a/go.mod b/go.mod index 59ac2cc0..12af2612 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-kit/log v0.2.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 - github.com/prometheus/common v0.39.0 + github.com/prometheus/common v0.40.0 github.com/prometheus/exporter-toolkit v0.8.2 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 diff --git a/go.sum b/go.sum index d98c10ae..845ecdf6 100644 --- a/go.sum +++ b/go.sum @@ -1480,8 +1480,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/common v0.40.0 h1:Afz7EVRqGg2Mqqf4JuF9vdvp1pi220m55Pi9T2JnO4Q= +github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE= github.com/prometheus/exporter-toolkit v0.8.2 h1:sbJAfBXQFkG6sUkbwBun8MNdzW9+wd5YfPYofbmj0YM= github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= diff --git a/vendor/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/prometheus/common/expfmt/decode.go index 7657f841..f4fc8845 100644 --- a/vendor/github.com/prometheus/common/expfmt/decode.go +++ b/vendor/github.com/prometheus/common/expfmt/decode.go @@ -115,32 +115,28 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error { // textDecoder implements the Decoder interface for the text protocol. type textDecoder struct { r io.Reader - p TextParser - fams []*dto.MetricFamily + fams map[string]*dto.MetricFamily + err error } // Decode implements the Decoder interface. func (d *textDecoder) Decode(v *dto.MetricFamily) error { - // TODO(fabxc): Wrap this as a line reader to make streaming safer. - if len(d.fams) == 0 { - // No cached metric families, read everything and parse metrics. - fams, err := d.p.TextToMetricFamilies(d.r) - if err != nil { - return err - } - if len(fams) == 0 { - return io.EOF - } - d.fams = make([]*dto.MetricFamily, 0, len(fams)) - for _, f := range fams { - d.fams = append(d.fams, f) + if d.err == nil { + // Read all metrics in one shot. + var p TextParser + d.fams, d.err = p.TextToMetricFamilies(d.r) + // If we don't get an error, store io.EOF for the end. + if d.err == nil { + d.err = io.EOF } } - - *v = *d.fams[0] - d.fams = d.fams[1:] - - return nil + // Pick off one MetricFamily per Decode until there's nothing left. + for key, fam := range d.fams { + *v = *fam + delete(d.fams, key) + return nil + } + return d.err } // SampleDecoder wraps a Decoder to extract samples from the metric families diff --git a/vendor/github.com/prometheus/common/expfmt/text_parse.go b/vendor/github.com/prometheus/common/expfmt/text_parse.go index 84be0643..ac248278 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_parse.go +++ b/vendor/github.com/prometheus/common/expfmt/text_parse.go @@ -142,9 +142,13 @@ func (p *TextParser) reset(in io.Reader) { func (p *TextParser) startOfLine() stateFn { p.lineCount++ if p.skipBlankTab(); p.err != nil { - // End of input reached. This is the only case where - // that is not an error but a signal that we are done. - p.err = nil + // This is the only place that we expect to see io.EOF, + // which is not an error but the signal that we are done. + // Any other error that happens to align with the start of + // a line is still an error. + if p.err == io.EOF { + p.err = nil + } return nil } switch p.currentByte { diff --git a/vendor/github.com/prometheus/common/model/value.go b/vendor/github.com/prometheus/common/model/value.go index c9d8fb1a..9eb44041 100644 --- a/vendor/github.com/prometheus/common/model/value.go +++ b/vendor/github.com/prometheus/common/model/value.go @@ -16,20 +16,12 @@ package model import ( "encoding/json" "fmt" - "math" "sort" "strconv" "strings" ) var ( - // ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a - // non-existing sample pair. It is a SamplePair with timestamp Earliest and - // value 0.0. Note that the natural zero value of SamplePair has a timestamp - // of 0, which is possible to appear in a real SamplePair and thus not - // suitable to signal a non-existing SamplePair. - ZeroSamplePair = SamplePair{Timestamp: Earliest} - // ZeroSample is the pseudo zero-value of Sample used to signal a // non-existing sample. It is a Sample with timestamp Earliest, value 0.0, // and metric nil. Note that the natural zero value of Sample has a timestamp @@ -38,82 +30,14 @@ var ( ZeroSample = Sample{Timestamp: Earliest} ) -// A SampleValue is a representation of a value for a given sample at a given -// time. -type SampleValue float64 - -// MarshalJSON implements json.Marshaler. -func (v SampleValue) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *SampleValue) UnmarshalJSON(b []byte) error { - if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("sample value must be a quoted string") - } - f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) - if err != nil { - return err - } - *v = SampleValue(f) - return nil -} - -// Equal returns true if the value of v and o is equal or if both are NaN. Note -// that v==o is false if both are NaN. If you want the conventional float -// behavior, use == to compare two SampleValues. -func (v SampleValue) Equal(o SampleValue) bool { - if v == o { - return true - } - return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) -} - -func (v SampleValue) String() string { - return strconv.FormatFloat(float64(v), 'f', -1, 64) -} - -// SamplePair pairs a SampleValue with a Timestamp. -type SamplePair struct { - Timestamp Time - Value SampleValue -} - -// MarshalJSON implements json.Marshaler. -func (s SamplePair) MarshalJSON() ([]byte, error) { - t, err := json.Marshal(s.Timestamp) - if err != nil { - return nil, err - } - v, err := json.Marshal(s.Value) - if err != nil { - return nil, err - } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (s *SamplePair) UnmarshalJSON(b []byte) error { - v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} - return json.Unmarshal(b, &v) -} - -// Equal returns true if this SamplePair and o have equal Values and equal -// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. -func (s *SamplePair) Equal(o *SamplePair) bool { - return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) -} - -func (s SamplePair) String() string { - return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) -} - -// Sample is a sample pair associated with a metric. +// Sample is a sample pair associated with a metric. A single sample must either +// define Value or Histogram but not both. Histogram == nil implies the Value +// field is used, otherwise it should be ignored. type Sample struct { - Metric Metric `json:"metric"` - Value SampleValue `json:"value"` - Timestamp Time `json:"timestamp"` + Metric Metric `json:"metric"` + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` + Histogram *SampleHistogram `json:"histogram"` } // Equal compares first the metrics, then the timestamp, then the value. The @@ -129,11 +53,19 @@ func (s *Sample) Equal(o *Sample) bool { if !s.Timestamp.Equal(o.Timestamp) { return false } - + if s.Histogram != nil { + return s.Histogram.Equal(o.Histogram) + } return s.Value.Equal(o.Value) } func (s Sample) String() string { + if s.Histogram != nil { + return fmt.Sprintf("%s => %s", s.Metric, SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }) + } return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ Timestamp: s.Timestamp, Value: s.Value, @@ -142,6 +74,19 @@ func (s Sample) String() string { // MarshalJSON implements json.Marshaler. func (s Sample) MarshalJSON() ([]byte, error) { + if s.Histogram != nil { + v := struct { + Metric Metric `json:"metric"` + Histogram SampleHistogramPair `json:"histogram"` + }{ + Metric: s.Metric, + Histogram: SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }, + } + return json.Marshal(&v) + } v := struct { Metric Metric `json:"metric"` Value SamplePair `json:"value"` @@ -152,21 +97,25 @@ func (s Sample) MarshalJSON() ([]byte, error) { Value: s.Value, }, } - return json.Marshal(&v) } // UnmarshalJSON implements json.Unmarshaler. func (s *Sample) UnmarshalJSON(b []byte) error { v := struct { - Metric Metric `json:"metric"` - Value SamplePair `json:"value"` + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + Histogram SampleHistogramPair `json:"histogram"` }{ Metric: s.Metric, Value: SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }, + Histogram: SampleHistogramPair{ + Timestamp: s.Timestamp, + Histogram: s.Histogram, + }, } if err := json.Unmarshal(b, &v); err != nil { @@ -174,8 +123,13 @@ func (s *Sample) UnmarshalJSON(b []byte) error { } s.Metric = v.Metric - s.Timestamp = v.Value.Timestamp - s.Value = v.Value.Value + if v.Histogram.Histogram != nil { + s.Timestamp = v.Histogram.Timestamp + s.Histogram = v.Histogram.Histogram + } else { + s.Timestamp = v.Value.Timestamp + s.Value = v.Value.Value + } return nil } @@ -221,80 +175,76 @@ func (s Samples) Equal(o Samples) bool { // SampleStream is a stream of Values belonging to an attached COWMetric. type SampleStream struct { - Metric Metric `json:"metric"` - Values []SamplePair `json:"values"` + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` } func (ss SampleStream) String() string { - vals := make([]string, len(ss.Values)) + valuesLength := len(ss.Values) + vals := make([]string, valuesLength+len(ss.Histograms)) for i, v := range ss.Values { vals[i] = v.String() } + for i, v := range ss.Histograms { + vals[i+valuesLength] = v.String() + } return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) } -// Value is a generic interface for values resulting from a query evaluation. -type Value interface { - Type() ValueType - String() string +func (ss SampleStream) MarshalJSON() ([]byte, error) { + if len(ss.Histograms) > 0 && len(ss.Values) > 0 { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Values: ss.Values, + Histograms: ss.Histograms, + } + return json.Marshal(&v) + } else if len(ss.Histograms) > 0 { + v := struct { + Metric Metric `json:"metric"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Histograms: ss.Histograms, + } + return json.Marshal(&v) + } else { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + }{ + Metric: ss.Metric, + Values: ss.Values, + } + return json.Marshal(&v) + } } -func (Matrix) Type() ValueType { return ValMatrix } -func (Vector) Type() ValueType { return ValVector } -func (*Scalar) Type() ValueType { return ValScalar } -func (*String) Type() ValueType { return ValString } - -type ValueType int - -const ( - ValNone ValueType = iota - ValScalar - ValVector - ValMatrix - ValString -) - -// MarshalJSON implements json.Marshaler. -func (et ValueType) MarshalJSON() ([]byte, error) { - return json.Marshal(et.String()) -} +func (ss *SampleStream) UnmarshalJSON(b []byte) error { + v := struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` + Histograms []SampleHistogramPair `json:"histograms"` + }{ + Metric: ss.Metric, + Values: ss.Values, + Histograms: ss.Histograms, + } -func (et *ValueType) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { + if err := json.Unmarshal(b, &v); err != nil { return err } - switch s { - case "": - *et = ValNone - case "scalar": - *et = ValScalar - case "vector": - *et = ValVector - case "matrix": - *et = ValMatrix - case "string": - *et = ValString - default: - return fmt.Errorf("unknown value type %q", s) - } - return nil -} -func (e ValueType) String() string { - switch e { - case ValNone: - return "" - case ValScalar: - return "scalar" - case ValVector: - return "vector" - case ValMatrix: - return "matrix" - case ValString: - return "string" - } - panic("ValueType.String: unhandled value type") + ss.Metric = v.Metric + ss.Values = v.Values + ss.Histograms = v.Histograms + + return nil } // Scalar is a scalar value evaluated at the set timestamp. diff --git a/vendor/github.com/prometheus/common/model/value_float.go b/vendor/github.com/prometheus/common/model/value_float.go new file mode 100644 index 00000000..8b59571a --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_float.go @@ -0,0 +1,109 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" + "math" + "strconv" + "unsafe" + + jsoniter "github.com/json-iterator/go" +) + +func init() { + jsoniter.RegisterTypeEncoderFunc("model.SamplePair", marshalSamplePairJSON, marshalJSONIsEmpty) +} + +var ( + // ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a + // non-existing sample pair. It is a SamplePair with timestamp Earliest and + // value 0.0. Note that the natural zero value of SamplePair has a timestamp + // of 0, which is possible to appear in a real SamplePair and thus not + // suitable to signal a non-existing SamplePair. + ZeroSamplePair = SamplePair{Timestamp: Earliest} +) + +// A SampleValue is a representation of a value for a given sample at a given +// time. +type SampleValue float64 + +// MarshalJSON implements json.Marshaler. +func (v SampleValue) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *SampleValue) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("sample value must be a quoted string") + } + f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) + if err != nil { + return err + } + *v = SampleValue(f) + return nil +} + +// Equal returns true if the value of v and o is equal or if both are NaN. Note +// that v==o is false if both are NaN. If you want the conventional float +// behavior, use == to compare two SampleValues. +func (v SampleValue) Equal(o SampleValue) bool { + if v == o { + return true + } + return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) +} + +func (v SampleValue) String() string { + return strconv.FormatFloat(float64(v), 'f', -1, 64) +} + +// SamplePair pairs a SampleValue with a Timestamp. +type SamplePair struct { + Timestamp Time + Value SampleValue +} + +// marshalSamplePairJSON writes `[ts, "val"]`. +func marshalSamplePairJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + p := *((*SamplePair)(ptr)) + stream.WriteArrayStart() + MarshalTimestamp(int64(p.Timestamp), stream) + stream.WriteMore() + MarshalValue(float64(p.Value), stream) + stream.WriteArrayEnd() +} + +func (s SamplePair) MarshalJSON() ([]byte, error) { + return jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(s) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *SamplePair) UnmarshalJSON(b []byte) error { + v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) +} + +// Equal returns true if this SamplePair and o have equal Values and equal +// Timestamps. The semantics of Value equality is defined by SampleValue.Equal. +func (s *SamplePair) Equal(o *SamplePair) bool { + return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) +} + +func (s SamplePair) String() string { + return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) +} diff --git a/vendor/github.com/prometheus/common/model/value_histogram.go b/vendor/github.com/prometheus/common/model/value_histogram.go new file mode 100644 index 00000000..cc221a88 --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_histogram.go @@ -0,0 +1,174 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "unsafe" + + jsoniter "github.com/json-iterator/go" +) + +func init() { + jsoniter.RegisterTypeEncoderFunc("model.HistogramBucket", marshalHistogramBucketJSON, marshalJSONIsEmpty) + jsoniter.RegisterTypeEncoderFunc("model.SampleHistogramPair", marshalSampleHistogramPairJSON, marshalJSONIsEmpty) +} + +type FloatString float64 + +func (v FloatString) String() string { + return strconv.FormatFloat(float64(v), 'f', -1, 64) +} + +func (v FloatString) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +func (v *FloatString) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("float value must be a quoted string") + } + f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) + if err != nil { + return err + } + *v = FloatString(f) + return nil +} + +type HistogramBucket struct { + Boundaries int32 + Lower FloatString + Upper FloatString + Count FloatString +} + +// marshalHistogramBucketJSON writes fmt.Sprintf("[%s,%s,%s,%s]", b.Boundaries, b.Lower, b.Upper, b.Count). +func marshalHistogramBucketJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + b := *((*HistogramBucket)(ptr)) + MarshalHistogramBucket(b, stream) +} + +func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} + wantLen := len(tmp) + if err := json.Unmarshal(buf, &tmp); err != nil { + return err + } + if gotLen := len(tmp); gotLen != wantLen { + return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) + } + return nil +} + +func (s *HistogramBucket) Equal(o *HistogramBucket) bool { + return s == o || (s.Boundaries == o.Boundaries && s.Lower == o.Lower && s.Upper == o.Upper && s.Count == o.Count) +} + +func (b HistogramBucket) String() string { + var sb strings.Builder + lowerInclusive := b.Boundaries == 1 || b.Boundaries == 3 + upperInclusive := b.Boundaries == 0 || b.Boundaries == 3 + if lowerInclusive { + sb.WriteRune('[') + } else { + sb.WriteRune('(') + } + fmt.Fprintf(&sb, "%g,%g", b.Lower, b.Upper) + if upperInclusive { + sb.WriteRune(']') + } else { + sb.WriteRune(')') + } + fmt.Fprintf(&sb, ":%v", b.Count) + return sb.String() +} + +type HistogramBuckets []*HistogramBucket + +func (s HistogramBuckets) Equal(o HistogramBuckets) bool { + if len(s) != len(o) { + return false + } + + for i, bucket := range s { + if !bucket.Equal(o[i]) { + return false + } + } + return true +} + +type SampleHistogram struct { + Count FloatString `json:"count"` + Sum FloatString `json:"sum"` + Buckets HistogramBuckets `json:"buckets"` +} + +func (s SampleHistogram) String() string { + return fmt.Sprintf("Count: %f, Sum: %f, Buckets: %v", s.Count, s.Sum, s.Buckets) +} + +func (s *SampleHistogram) Equal(o *SampleHistogram) bool { + return s == o || (s.Count == o.Count && s.Sum == o.Sum && s.Buckets.Equal(o.Buckets)) +} + +type SampleHistogramPair struct { + Timestamp Time + // Histogram should never be nil, it's only stored as pointer for efficiency. + Histogram *SampleHistogram +} + +// marshalSampleHistogramPairJSON writes `[ts, "val"]`. +func marshalSampleHistogramPairJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + p := *((*SampleHistogramPair)(ptr)) + stream.WriteArrayStart() + MarshalTimestamp(int64(p.Timestamp), stream) + stream.WriteMore() + MarshalHistogram(*p.Histogram, stream) + stream.WriteArrayEnd() +} + +func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { + if s.Histogram == nil { + return nil, fmt.Errorf("histogram is nil") + } + return jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(s) +} + +func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&s.Timestamp, &s.Histogram} + wantLen := len(tmp) + if err := json.Unmarshal(buf, &tmp); err != nil { + return err + } + if gotLen := len(tmp); gotLen != wantLen { + return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) + } + if s.Histogram == nil { + return fmt.Errorf("histogram is null") + } + return nil +} + +func (s SampleHistogramPair) String() string { + return fmt.Sprintf("%s @[%s]", s.Histogram, s.Timestamp) +} + +func (s *SampleHistogramPair) Equal(o *SampleHistogramPair) bool { + return s == o || (s.Histogram.Equal(o.Histogram) && s.Timestamp.Equal(o.Timestamp)) +} diff --git a/vendor/github.com/prometheus/common/model/value_marshal.go b/vendor/github.com/prometheus/common/model/value_marshal.go new file mode 100644 index 00000000..df193bcb --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_marshal.go @@ -0,0 +1,131 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "math" + "strconv" + "unsafe" + + jsoniter "github.com/json-iterator/go" +) + +func marshalJSONIsEmpty(ptr unsafe.Pointer) bool { + return false +} + +// MarshalTimestamp marshals a point timestamp using the passed jsoniter stream. +func MarshalTimestamp(t int64, stream *jsoniter.Stream) { + // Write out the timestamp as a float divided by 1000. + // This is ~3x faster than converting to a float. + if t < 0 { + stream.WriteRaw(`-`) + t = -t + } + stream.WriteInt64(t / 1000) + fraction := t % 1000 + if fraction != 0 { + stream.WriteRaw(`.`) + if fraction < 100 { + stream.WriteRaw(`0`) + } + if fraction < 10 { + stream.WriteRaw(`0`) + } + stream.WriteInt64(fraction) + } +} + +// MarshalValue marshals a point value using the passed jsoniter stream. +func MarshalValue(v float64, stream *jsoniter.Stream) { + stream.WriteRaw(`"`) + // Taken from https://github.com/json-iterator/go/blob/master/stream_float.go#L71 as a workaround + // to https://github.com/json-iterator/go/issues/365 (jsoniter, to follow json standard, doesn't allow inf/nan). + buf := stream.Buffer() + abs := math.Abs(v) + fmt := byte('f') + // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. + if abs != 0 { + if abs < 1e-6 || abs >= 1e21 { + fmt = 'e' + } + } + buf = strconv.AppendFloat(buf, v, fmt, -1, 64) + stream.SetBuffer(buf) + stream.WriteRaw(`"`) +} + +// MarshalHistogramBucket writes something like: [ 3, "-0.25", "0.25", "3"] +// See MarshalHistogram to understand what the numbers mean +func MarshalHistogramBucket(b HistogramBucket, stream *jsoniter.Stream) { + stream.WriteArrayStart() + stream.WriteInt32(b.Boundaries) + stream.WriteMore() + MarshalValue(float64(b.Lower), stream) + stream.WriteMore() + MarshalValue(float64(b.Upper), stream) + stream.WriteMore() + MarshalValue(float64(b.Count), stream) + stream.WriteArrayEnd() +} + +// MarshalHistogram writes something like: +// +// { +// "count": "42", +// "sum": "34593.34", +// "buckets": [ +// [ 3, "-0.25", "0.25", "3"], +// [ 0, "0.25", "0.5", "12"], +// [ 0, "0.5", "1", "21"], +// [ 0, "2", "4", "6"] +// ] +// } +// +// The 1st element in each bucket array determines if the boundaries are +// inclusive (AKA closed) or exclusive (AKA open): +// +// 0: lower exclusive, upper inclusive +// 1: lower inclusive, upper exclusive +// 2: both exclusive +// 3: both inclusive +// +// The 2nd and 3rd elements are the lower and upper boundary. The 4th element is +// the bucket count. +func MarshalHistogram(h SampleHistogram, stream *jsoniter.Stream) { + stream.WriteObjectStart() + stream.WriteObjectField(`count`) + MarshalValue(float64(h.Count), stream) + stream.WriteMore() + stream.WriteObjectField(`sum`) + MarshalValue(float64(h.Sum), stream) + + bucketFound := false + for _, bucket := range h.Buckets { + if bucket.Count == 0 { + continue // No need to expose empty buckets in JSON. + } + stream.WriteMore() + if !bucketFound { + stream.WriteObjectField(`buckets`) + stream.WriteArrayStart() + } + bucketFound = true + MarshalHistogramBucket(*bucket, stream) + } + if bucketFound { + stream.WriteArrayEnd() + } + stream.WriteObjectEnd() +} diff --git a/vendor/github.com/prometheus/common/model/value_type.go b/vendor/github.com/prometheus/common/model/value_type.go new file mode 100644 index 00000000..726c50ee --- /dev/null +++ b/vendor/github.com/prometheus/common/model/value_type.go @@ -0,0 +1,83 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" +) + +// Value is a generic interface for values resulting from a query evaluation. +type Value interface { + Type() ValueType + String() string +} + +func (Matrix) Type() ValueType { return ValMatrix } +func (Vector) Type() ValueType { return ValVector } +func (*Scalar) Type() ValueType { return ValScalar } +func (*String) Type() ValueType { return ValString } + +type ValueType int + +const ( + ValNone ValueType = iota + ValScalar + ValVector + ValMatrix + ValString +) + +// MarshalJSON implements json.Marshaler. +func (et ValueType) MarshalJSON() ([]byte, error) { + return json.Marshal(et.String()) +} + +func (et *ValueType) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + switch s { + case "": + *et = ValNone + case "scalar": + *et = ValScalar + case "vector": + *et = ValVector + case "matrix": + *et = ValMatrix + case "string": + *et = ValString + default: + return fmt.Errorf("unknown value type %q", s) + } + return nil +} + +func (e ValueType) String() string { + switch e { + case ValNone: + return "" + case ValScalar: + return "scalar" + case ValVector: + return "vector" + case ValMatrix: + return "matrix" + case ValString: + return "string" + } + panic("ValueType.String: unhandled value type") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5cebfeee..0efbe5b1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -691,7 +691,7 @@ github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.3.0 ## explicit; go 1.9 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.39.0 +# github.com/prometheus/common v0.40.0 ## explicit; go 1.17 github.com/prometheus/common/config github.com/prometheus/common/expfmt From ff330cf05ebb5b4598034f13e7a7afd0a562683a Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sat, 25 Feb 2023 18:44:43 +1100 Subject: [PATCH 2/4] Bump podman/v4 to v4.4.2 Signed-off-by: Navid Yaghoobi --- go.mod | 109 +- go.sum | 1452 +------ vendor/github.com/BurntSushi/toml/decode.go | 42 +- .../BurntSushi/toml/decode_go116.go | 4 +- vendor/github.com/BurntSushi/toml/doc.go | 22 +- vendor/github.com/BurntSushi/toml/encode.go | 40 +- vendor/github.com/BurntSushi/toml/error.go | 67 +- vendor/github.com/BurntSushi/toml/lex.go | 2 +- vendor/github.com/BurntSushi/toml/meta.go | 4 +- .../Microsoft/go-winio/.gitattributes | 1 + .../github.com/Microsoft/go-winio/.gitignore | 9 + .../Microsoft/go-winio/.golangci.yml | 144 + .../github.com/Microsoft/go-winio/README.md | 72 +- .../github.com/Microsoft/go-winio/SECURITY.md | 41 + .../github.com/Microsoft/go-winio/backup.go | 48 +- .../go-winio/backuptar/{noop.go => doc.go} | 1 - .../Microsoft/go-winio/backuptar/strconv.go | 2 + .../Microsoft/go-winio/backuptar/tar.go | 146 +- vendor/github.com/Microsoft/go-winio/doc.go | 22 + vendor/github.com/Microsoft/go-winio/ea.go | 8 +- vendor/github.com/Microsoft/go-winio/file.go | 66 +- .../github.com/Microsoft/go-winio/fileinfo.go | 29 +- .../github.com/Microsoft/go-winio/hvsock.go | 345 +- .../go-winio/internal/socket/rawaddr.go | 20 + .../go-winio/internal/socket/socket.go | 179 + .../internal/socket/zsyscall_windows.go | 72 + vendor/github.com/Microsoft/go-winio/pipe.go | 124 +- .../Microsoft/go-winio/pkg/guid/guid.go | 16 +- .../go-winio/pkg/guid/guid_nonwindows.go | 1 + .../go-winio/pkg/guid/guid_windows.go | 3 + .../go-winio/pkg/guid/variant_string.go | 27 + .../pkg/security/grantvmgroupaccess.go | 33 +- .../go-winio/pkg/security/syscall_windows.go | 2 +- .../go-winio/pkg/security/zsyscall_windows.go | 4 +- .../Microsoft/go-winio/privilege.go | 32 +- .../github.com/Microsoft/go-winio/reparse.go | 11 +- vendor/github.com/Microsoft/go-winio/sd.go | 64 +- .../github.com/Microsoft/go-winio/syscall.go | 4 +- vendor/github.com/Microsoft/go-winio/tools.go | 5 + .../github.com/Microsoft/go-winio/vhd/vhd.go | 59 +- .../Microsoft/go-winio/vhd/zvhd_windows.go | 4 +- .../Microsoft/go-winio/zsyscall_windows.go | 45 +- .../Microsoft/hcsshim/internal/cow/cow.go | 6 + .../Microsoft/hcsshim/internal/hcs/errors.go | 6 +- .../Microsoft/hcsshim/internal/hcs/system.go | 12 +- .../asaskevich/govalidator/.gitignore | 15 + .../asaskevich/govalidator/.travis.yml | 12 + .../asaskevich/govalidator/CODE_OF_CONDUCT.md | 43 + .../asaskevich/govalidator/CONTRIBUTING.md | 63 + .../github.com/asaskevich/govalidator/LICENSE | 21 + .../asaskevich/govalidator/README.md | 622 +++ .../asaskevich/govalidator/arrays.go | 87 + .../asaskevich/govalidator/converter.go | 81 + .../github.com/asaskevich/govalidator/doc.go | 3 + .../asaskevich/govalidator/error.go | 47 + .../asaskevich/govalidator/numerics.go | 100 + .../asaskevich/govalidator/patterns.go | 113 + .../asaskevich/govalidator/types.go | 656 +++ .../asaskevich/govalidator/utils.go | 270 ++ .../asaskevich/govalidator/validator.go | 1769 +++++++++ .../asaskevich/govalidator/wercker.yml | 15 + vendor/github.com/blang/semver/.travis.yml | 21 - vendor/github.com/blang/semver/README.md | 194 - vendor/github.com/blang/semver/json.go | 23 - vendor/github.com/blang/semver/package.json | 17 - vendor/github.com/blang/semver/range.go | 416 -- vendor/github.com/blang/semver/semver.go | 418 -- vendor/github.com/blang/semver/sort.go | 28 - vendor/github.com/blang/semver/sql.go | 30 - .../checkpoint-restore/go-criu/v5/.gitignore | 6 - .../checkpoint-restore/go-criu/v5/Makefile | 107 - .../go-criu/v5/stats/types.go | 6 - .../checkpoint-restore/go-criu/v6/.gitignore | 13 + .../go-criu/{v5 => v6}/.golangci.yml | 10 +- .../go-criu/{v5 => v6}/LICENSE | 0 .../checkpoint-restore/go-criu/v6/Makefile | 41 + .../go-criu/{v5 => v6}/README.md | 25 +- .../go-criu/{v5 => v6}/features.go | 2 +- .../go-criu/{v5 => v6}/main.go | 2 +- .../go-criu/{v5 => v6}/notify.go | 0 .../go-criu/{v5 => v6}/rpc/rpc.pb.go | 492 ++- .../go-criu/{v5 => v6}/rpc/rpc.proto | 9 + .../go-criu/{v5 => v6}/stats/stats.pb.go | 4 +- .../go-criu/{v5 => v6}/stats/stats.proto | 0 .../go-criu/{v5/magic => v6/stats}/types.go | 6 +- .../go-criu/{v5 => v6}/stats/utils.go | 9 +- .../internal/multierror/multierror.go | 82 + .../pkg/cdi/cache.go | 77 +- .../container-device-interface/pkg/cdi/doc.go | 119 +- .../pkg/cdi/registry.go | 1 + .../pkg/cdi/spec.go | 92 +- .../containerd/platforms/platforms.go | 38 +- .../stargz-snapshotter/estargz/build.go | 48 +- .../stargz-snapshotter/estargz/estargz.go | 224 +- .../stargz-snapshotter/estargz/gzip.go | 2 +- .../stargz-snapshotter/estargz/testutil.go | 629 ++- .../stargz-snapshotter/estargz/types.go | 31 +- .../github.com/containers/buildah/.cirrus.yml | 48 +- .../containers/buildah/CHANGELOG.md | 126 + .../containers/buildah/CONTRIBUTING.md | 17 +- vendor/github.com/containers/buildah/Makefile | 8 +- .../github.com/containers/buildah/README.md | 2 +- vendor/github.com/containers/buildah/add.go | 3 +- .../github.com/containers/buildah/buildah.go | 14 +- .../containers/buildah/buildah.spec.rpkg | 2 +- .../containers/buildah/changelog.txt | 125 + .../containers/buildah/chroot/run_common.go | 3 +- .../containers/buildah/chroot/run_linux.go | 14 +- .../containers/buildah/chroot/seccomp.go | 4 +- .../github.com/containers/buildah/commit.go | 3 +- .../containers/buildah/copier/copier.go | 39 +- .../containers/buildah/define/build.go | 10 +- .../buildah/define/mount_unsupported.go | 17 + .../containers/buildah/define/types.go | 10 +- vendor/github.com/containers/buildah/image.go | 3 +- .../containers/buildah/imagebuildah/build.go | 80 +- .../buildah/imagebuildah/executor.go | 93 +- .../buildah/imagebuildah/stage_executor.go | 197 +- .../github.com/containers/buildah/install.md | 4 +- .../buildah/internal/parse/parse.go | 182 +- .../containers/buildah/internal/types.go | 1 + vendor/github.com/containers/buildah/new.go | 1 + .../containers/buildah/pkg/overlay/overlay.go | 5 +- .../containers/buildah/pkg/parse/parse.go | 49 +- .../buildah/pkg/sshagent/sshagent.go | 18 +- .../buildah/pkg/util/uptime_linux.go | 4 +- .../containers/buildah/pkg/util/util.go | 5 +- vendor/github.com/containers/buildah/run.go | 7 +- .../containers/buildah/run_common.go | 99 +- .../containers/buildah/run_freebsd.go | 14 +- .../containers/buildah/run_linux.go | 29 +- .../github.com/containers/buildah/seccomp.go | 4 +- .../containers/buildah/util/util.go | 11 +- .../containers/common/libimage/copier.go | 5 + .../containers/common/libimage/disk_usage.go | 35 +- .../containers/common/libimage/filters.go | 13 +- .../containers/common/libimage/image.go | 11 +- .../containers/common/libimage/layer_tree.go | 11 + .../common/libimage/manifest_list.go | 1 + .../common/libimage/manifests/manifests.go | 3 + .../containers/common/libimage/platform.go | 8 +- .../containers/common/libimage/pull.go | 2 +- .../common/libnetwork/cni/cni_conversion.go | 6 +- .../common/libnetwork/cni/cni_types.go | 2 +- .../common/libnetwork/cni/config.go | 14 +- .../common/libnetwork/cni/network.go | 32 +- .../common/libnetwork/netavark/config.go | 98 +- .../common/libnetwork/netavark/const.go | 2 + .../common/libnetwork/netavark/network.go | 12 +- .../common/libnetwork/netavark/run.go | 6 + .../common/libnetwork/network/interface.go | 10 +- .../libnetwork/resolvconf/resolvconf.go | 23 +- .../common/libnetwork/types/const.go | 1 + .../common/libnetwork/types/network.go | 26 +- .../containers/common/pkg/cgroups/cgroups.go | 13 +- .../common/pkg/cgroups/cgroups_linux.go | 9 +- .../common/pkg/cgroups/cgroups_supported.go | 7 +- .../containers/common/pkg/cgroups/pids.go | 4 +- .../containers/common/pkg/cgroups/systemd.go | 26 +- .../common/pkg/cgroups/systemd_linux.go | 24 +- .../containers/common/pkg/cgroups/utils.go | 13 +- .../containers/common/pkg/config/config.go | 60 +- .../common/pkg/config/config_darwin.go | 4 + .../common/pkg/config/config_freebsd.go | 4 + .../common/pkg/config/config_linux.go | 4 + .../common/pkg/config/config_local.go | 4 + .../common/pkg/config/config_windows.go | 4 + .../common/pkg/config/containers.conf | 55 +- .../common/pkg/config/containers.conf-freebsd | 15 +- .../containers/common/pkg/config/default.go | 125 +- .../common/pkg/config/default_darwin.go | 6 +- .../common/pkg/config/default_freebsd.go | 2 +- .../common/pkg/config/default_linux.go | 3 +- .../containers/common/pkg/config/systemd.go | 8 +- .../common/pkg/download/download.go | 4 +- .../containers/common/pkg/filters/filters.go | 1 + .../containers/common/pkg/hooks/hooks.go | 4 +- .../containers/common/pkg/hooks/monitor.go | 20 +- .../containers/common/pkg/hooks/read.go | 5 +- .../containers/common/pkg/machine/machine.go | 2 + .../common/pkg/netns/netns_linux.go | 5 +- .../containers/common/pkg/parse/parse.go | 9 +- .../pkg/secrets/filedriver/filedriver.go | 10 +- .../pkg/secrets/passdriver/passdriver.go | 9 +- .../containers/common/pkg/secrets/secrets.go | 8 +- .../common/pkg/secrets/secretsdb.go | 8 +- .../common/pkg/ssh/connection_golang.go | 22 +- .../containers/common/pkg/ssh/utils.go | 3 +- .../common/pkg/subscriptions/subscriptions.go | 21 +- .../common/pkg/sysinfo/sysinfo_linux.go | 7 +- .../common/pkg/timetype/timestamp.go | 6 +- .../containers/common/version/version.go | 2 +- .../containers/image/v5/copy/compression.go | 2 +- .../containers/image/v5/copy/copy.go | 78 +- .../containers/image/v5/copy/sign.go | 96 +- .../image/v5/directory/directory_dest.go | 2 +- .../image/v5/docker/archive/writer.go | 2 +- .../containers/image/v5/docker/body_reader.go | 234 ++ .../image/v5/docker/daemon/client.go | 6 +- .../image/v5/docker/docker_client.go | 115 +- .../image/v5/docker/docker_image.go | 4 +- .../image/v5/docker/docker_image_dest.go | 59 +- .../image/v5/docker/docker_image_src.go | 75 +- .../containers/image/v5/docker/errors.go | 41 +- .../v5/docker/internal/tarfile/reader.go | 20 +- .../v5/docker/internal/tarfile/writer.go | 4 +- .../image/v5/docker/reference/regexp.go | 152 +- .../image/v5/docker/registries_d.go | 30 +- .../v5/internal/imagesource/stubs/stubs.go | 2 +- .../image/v5/internal/signature/sigstore.go | 6 + .../image/v5/internal/signer/signer.go | 47 + .../image/v5/internal/useragent/useragent.go | 6 + .../image/v5/manifest/docker_schema1.go | 5 +- .../containers/image/v5/manifest/oci.go | 1 + .../image/v5/oci/archive/oci_src.go | 15 + .../containers/image/v5/oci/layout/oci_src.go | 13 +- .../image/v5/oci/layout/oci_transport.go | 20 +- .../image/v5/openshift/openshift.go | 8 +- .../image/v5/openshift/openshift_transport.go | 4 +- .../image/v5/ostree/ostree_transport.go | 4 +- .../containers/image/v5/pkg/blobcache/src.go | 2 +- .../image/v5/pkg/docker/config/config.go | 180 +- .../image/v5/pkg/shortnames/shortnames.go | 4 +- .../v5/pkg/sysregistriesv2/shortnames.go | 4 +- .../sysregistriesv2/system_registries_v2.go | 31 +- .../v5/pkg/tlsclientconfig/tlsclientconfig.go | 8 +- .../image/v5/signature/fulcio_cert.go | 174 + .../image/v5/signature/internal/rekor_set.go | 237 ++ .../v5/signature/internal/sigstore_payload.go | 3 +- .../image/v5/signature/policy_config.go | 109 +- .../v5/signature/policy_config_sigstore.go | 343 ++ .../v5/signature/policy_eval_sigstore.go | 175 +- .../image/v5/signature/policy_types.go | 37 +- .../image/v5/signature/signer/signer.go | 9 + .../image/v5/signature/sigstore/copied.go | 29 + .../image/v5/signature/sigstore/generate.go | 35 + .../v5/signature/sigstore/internal/signer.go | 95 + .../image/v5/signature/sigstore/sign.go | 65 - .../image/v5/signature/sigstore/signer.go | 60 + .../containers/image/v5/signature/simple.go | 3 +- .../v5/signature/simplesigning/signer.go | 105 + .../image/v5/storage/storage_src.go | 77 +- .../containers/image/v5/types/types.go | 8 + .../containers/image/v5/version/version.go | 2 +- .../containers/libtrust/CODE-OF-CONDUCT.md | 2 +- .../containers/libtrust/SECURITY.md | 2 +- .../github.com/containers/ocicrypt/.gitignore | 1 + .../containers/ocicrypt/.golangci.yml | 31 + .../containers/ocicrypt/.travis.yml | 29 - .../ocicrypt/blockcipher/blockcipher.go | 11 +- .../blockcipher/blockcipher_aes_ctr.go | 10 +- .../ocicrypt/config/constructors.go | 9 +- .../config/keyprovider-config/config.go | 7 +- .../ocicrypt/config/pkcs11config/config.go | 13 +- .../ocicrypt/crypto/pkcs11/common.go | 14 +- .../ocicrypt/crypto/pkcs11/pkcs11helpers.go | 148 +- .../crypto/pkcs11/pkcs11helpers_nocgo.go | 9 +- .../ocicrypt/crypto/pkcs11/utils.go | 5 +- .../containers/ocicrypt/encryption.go | 20 +- vendor/github.com/containers/ocicrypt/gpg.go | 23 +- .../containers/ocicrypt/gpgvault.go | 8 +- .../ocicrypt/helpers/parse_helpers.go | 31 +- .../ocicrypt/keywrap/jwe/keywrapper_jwe.go | 7 +- .../keywrap/keyprovider/keyprovider.go | 18 +- .../ocicrypt/keywrap/pgp/keywrapper_gpg.go | 13 +- .../keywrap/pkcs11/keywrapper_pkcs11.go | 9 +- .../keywrap/pkcs7/keywrapper_pkcs7.go | 5 +- .../containers/ocicrypt/utils/ioutils.go | 6 +- .../containers/ocicrypt/utils/testing.go | 21 +- .../containers/ocicrypt/utils/utils.go | 16 +- .../podman/v4/cmd/podman/parse/json.go | 10 - .../podman/v4/cmd/podman/parse/net.go | 7 +- .../podman/v4/cmd/podman/registry/config.go | 12 +- .../podman/v4/libpod/boltdb_state.go | 101 +- .../podman/v4/libpod/boltdb_state_freebsd.go | 25 - .../podman/v4/libpod/boltdb_state_internal.go | 42 +- .../podman/v4/libpod/boltdb_state_linux.go | 57 - .../containers/podman/v4/libpod/container.go | 60 +- .../podman/v4/libpod/container_api.go | 85 +- .../podman/v4/libpod/container_commit.go | 3 +- .../podman/v4/libpod/container_config.go | 6 +- .../podman/v4/libpod/container_freebsd.go | 19 +- .../podman/v4/libpod/container_inspect.go | 6 +- .../v4/libpod/container_inspect_linux.go | 26 +- .../podman/v4/libpod/container_internal.go | 55 +- .../v4/libpod/container_internal_common.go | 129 +- .../v4/libpod/container_internal_freebsd.go | 47 +- .../v4/libpod/container_internal_linux.go | 13 +- .../podman/v4/libpod/container_linux.go | 8 - .../podman/v4/libpod/container_log.go | 8 + .../podman/v4/libpod/container_log_linux.go | 104 +- .../podman/v4/libpod/container_validate.go | 9 +- .../podman/v4/libpod/define/annotations.go | 2 +- .../podman/v4/libpod/define/config.go | 3 - .../v4/libpod/define/container_inspect.go | 4 +- .../podman/v4/libpod/define/containerstate.go | 2 +- .../podman/v4/libpod/define/healthchecks.go | 14 + .../podman/v4/libpod/define/info.go | 1 + .../podman/v4/libpod/define/pod_inspect.go | 2 +- .../containers/podman/v4/libpod/events.go | 30 +- .../podman/v4/libpod/events/config.go | 4 + .../podman/v4/libpod/events/filters.go | 3 + .../podman/v4/libpod/events/journal_linux.go | 33 +- .../podman/v4/libpod/events/logfile.go | 2 +- .../podman/v4/libpod/healthcheck.go | 167 +- .../podman/v4/libpod/healthcheck_linux.go | 59 +- .../v4/libpod/healthcheck_nosystemd_linux.go | 24 + .../v4/libpod/healthcheck_unsupported.go | 6 +- .../containers/podman/v4/libpod/info.go | 1 + .../containers/podman/v4/libpod/kube.go | 169 +- .../podman/v4/libpod/lock/file/file_lock.go | 6 +- .../podman/v4/libpod/lock/shm/shm_lock.go | 18 +- .../logs/reversereader/reversereader.go | 2 +- .../podman/v4/libpod/networking_common.go | 77 +- .../podman/v4/libpod/networking_freebsd.go | 57 +- .../podman/v4/libpod/networking_linux.go | 90 +- .../v4/libpod/networking_pasta_linux.go | 107 + .../v4/libpod/networking_slirp4netns.go | 4 +- .../v4/libpod/networking_unsupported.go | 10 +- .../podman/v4/libpod/oci_conmon_common.go | 62 +- .../podman/v4/libpod/oci_conmon_linux.go | 3 +- .../containers/podman/v4/libpod/options.go | 67 +- .../containers/podman/v4/libpod/pod_api.go | 6 +- .../podman/v4/libpod/pod_internal.go | 2 +- .../containers/podman/v4/libpod/reset.go | 23 +- .../containers/podman/v4/libpod/runtime.go | 68 +- .../podman/v4/libpod/runtime_ctr.go | 73 +- .../podman/v4/libpod/runtime_img.go | 2 +- .../podman/v4/libpod/runtime_migrate.go | 2 +- .../podman/v4/libpod/runtime_renumber.go | 2 +- .../podman/v4/libpod/runtime_volume_common.go | 21 +- .../containers/podman/v4/libpod/service.go | 52 +- .../containers/podman/v4/libpod/state.go | 5 +- .../podman/v4/libpod/stats_freebsd.go | 10 +- .../podman/v4/libpod/stats_linux.go | 10 +- .../containers/podman/v4/libpod/util.go | 18 +- .../containers/podman/v4/libpod/volume.go | 13 +- .../podman/v4/pkg/annotations/annotations.go | 12 + .../containers/podman/v4/pkg/auth/auth.go | 4 +- .../podman/v4/pkg/bindings/connection.go | 52 +- .../podman/v4/pkg/bindings/images/build.go | 65 +- .../podman/v4/pkg/bindings/kube/kube.go | 51 +- .../podman/v4/pkg/bindings/kube/types.go | 28 + .../pkg/bindings/kube/types_apply_options.go | 93 + .../pkg/bindings/kube/types_down_options.go | 33 + .../pkg/bindings/kube/types_play_options.go | 30 + .../v4/pkg/bindings/manifests/manifests.go | 52 +- .../podman/v4/pkg/bindings/manifests/types.go | 1 + .../manifests/types_inspect_options.go | 15 + .../podman/v4/pkg/bindings/network/network.go | 45 +- .../podman/v4/pkg/bindings/network/types.go | 19 +- .../network/types_extracreate_options.go | 33 + .../bindings/network/types_update_options.go | 48 + .../podman/v4/pkg/bindings/play/play.go | 8 +- .../podman/v4/pkg/bindings/pods/pods.go | 2 +- .../podman/v4/pkg/bindings/system/types.go | 7 +- .../bindings/system/types_prune_options.go | 15 + .../v4/pkg/checkpoint/checkpoint_restore.go | 16 +- .../crutils/checkpoint_restore_utils.go | 6 +- .../podman/v4/pkg/criu/criu_linux.go | 4 +- .../podman/v4/pkg/domain/entities/apply.go | 21 + .../v4/pkg/domain/entities/containers.go | 8 +- .../podman/v4/pkg/domain/entities/engine.go | 47 +- .../pkg/domain/entities/engine_container.go | 4 +- .../v4/pkg/domain/entities/engine_image.go | 2 +- .../podman/v4/pkg/domain/entities/images.go | 18 + .../podman/v4/pkg/domain/entities/manifest.go | 8 + .../podman/v4/pkg/domain/entities/network.go | 27 +- .../podman/v4/pkg/domain/entities/play.go | 16 +- .../podman/v4/pkg/domain/entities/pods.go | 241 +- .../pkg/domain/entities/reports/containers.go | 2 +- .../podman/v4/pkg/domain/entities/system.go | 14 +- .../podman/v4/pkg/domain/entities/volumes.go | 2 + .../v4/pkg/domain/filters/containers.go | 36 +- .../podman/v4/pkg/domain/infra/abi/apply.go | 194 + .../v4/pkg/domain/infra/abi/containers.go | 594 ++- .../domain/infra/abi/containers_freebsd.go | 3 + .../pkg/domain/infra/abi/containers_linux.go | 3 + .../v4/pkg/domain/infra/abi/healthcheck.go | 4 +- .../podman/v4/pkg/domain/infra/abi/images.go | 10 +- .../v4/pkg/domain/infra/abi/manifest.go | 20 +- .../podman/v4/pkg/domain/infra/abi/network.go | 26 +- .../podman/v4/pkg/domain/infra/abi/play.go | 351 +- .../podman/v4/pkg/domain/infra/abi/system.go | 18 +- .../infra/abi/terminal/terminal_common.go | 14 +- .../podman/v4/pkg/domain/infra/abi/volumes.go | 5 + .../v4/pkg/domain/infra/runtime_libpod.go | 27 +- .../v4/pkg/domain/infra/tunnel/containers.go | 79 +- .../v4/pkg/domain/infra/tunnel/generate.go | 51 - .../v4/pkg/domain/infra/tunnel/images.go | 11 + .../podman/v4/pkg/domain/infra/tunnel/kube.go | 86 + .../v4/pkg/domain/infra/tunnel/manifest.go | 46 +- .../v4/pkg/domain/infra/tunnel/network.go | 13 +- .../podman/v4/pkg/domain/infra/tunnel/play.go | 36 - .../v4/pkg/domain/infra/tunnel/system.go | 2 +- .../podman/v4/pkg/env/env_windows.go | 2 +- .../podman/v4/pkg/k8s.io/api/apps/v1/types.go | 6 +- .../api/core/v1/annotation_key_constants.go | 2 +- .../podman/v4/pkg/k8s.io/api/core/v1/types.go | 255 +- .../apimachinery/pkg/apis/meta/v1/types.go | 2 +- .../containers/podman/v4/pkg/lookup/lookup.go | 2 +- .../podman/v4/pkg/namespaces/namespaces.go | 8 +- .../containers/podman/v4/pkg/ps/ps.go | 47 +- .../containers/podman/v4/pkg/rctl/rctl.go | 3 - .../podman/v4/pkg/rootless/rootless.go | 8 +- .../podman/v4/pkg/rootless/rootless_freebsd.c | 63 + .../v4/pkg/rootless/rootless_freebsd.go | 69 + .../podman/v4/pkg/rootless/rootless_linux.c | 223 +- .../podman/v4/pkg/rootless/rootless_linux.go | 17 +- .../v4/pkg/rootless/rootless_unsupported.go | 6 +- .../v4/pkg/specgen/generate/config_linux.go | 14 + .../v4/pkg/specgen/generate/container.go | 3 +- .../pkg/specgen/generate/container_create.go | 30 +- .../v4/pkg/specgen/generate/kube/kube.go | 177 +- .../v4/pkg/specgen/generate/kube/volume.go | 43 +- .../v4/pkg/specgen/generate/namespaces.go | 11 +- .../pkg/specgen/generate/namespaces_linux.go | 2 - .../v4/pkg/specgen/generate/oci_freebsd.go | 6 - .../v4/pkg/specgen/generate/oci_linux.go | 20 +- .../v4/pkg/specgen/generate/pod_create.go | 22 +- .../podman/v4/pkg/specgen/generate/ports.go | 4 +- .../podman/v4/pkg/specgen/generate/storage.go | 37 +- .../podman/v4/pkg/specgen/namespaces.go | 40 +- .../podman/v4/pkg/specgen/pod_validate.go | 4 +- .../podman/v4/pkg/specgen/podspecgen.go | 2 +- .../podman/v4/pkg/specgen/specgen.go | 10 +- .../podman/v4/pkg/specgen/volumes.go | 6 +- .../podman/v4/pkg/specgenutil/specgen.go | 43 +- .../podman/v4/pkg/specgenutil/util.go | 1 + .../podman/v4/pkg/specgenutil/volumes.go | 28 +- .../containers/podman/v4/pkg/systemd/dbus.go | 6 +- .../v4/pkg/systemd/generate/containers.go | 16 +- .../podman/v4/pkg/systemd/generate/pods.go | 16 +- .../v4/pkg/systemd/notifyproxy/notifyproxy.go | 138 +- .../containers/podman/v4/pkg/trust/policy.go | 3 +- .../containers/podman/v4/pkg/util/kube.go | 2 + .../containers/podman/v4/pkg/util/utils.go | 301 +- .../podman/v4/pkg/util/utils_linux.go | 46 +- .../podman/v4/pkg/util/utils_supported.go | 6 + .../containers/podman/v4/utils/utils.go | 31 +- .../podman/v4/utils/utils_freebsd.go | 22 - .../podman/v4/utils/utils_supported.go | 4 +- .../containers/podman/v4/version/version.go | 2 +- vendor/github.com/containers/psgo/README.md | 2 + vendor/github.com/containers/psgo/psgo.go | 33 + .../github.com/containers/storage/.cirrus.yml | 23 +- vendor/github.com/containers/storage/Makefile | 8 +- vendor/github.com/containers/storage/VERSION | 2 +- .../containers/storage/containers.go | 627 ++- .../containers/storage/deprecated.go | 216 + .../containers/storage/drivers/aufs/aufs.go | 7 +- .../containers/storage/drivers/aufs/mount.go | 1 + .../containers/storage/drivers/btrfs/btrfs.go | 12 +- .../drivers/btrfs/dummy_unsupported.go | 1 + .../storage/drivers/btrfs/version.go | 1 + .../containers/storage/drivers/chown.go | 6 +- .../storage/drivers/chown_darwin.go | 6 +- .../storage/drivers/copy/copy_unsupported.go | 7 +- .../containers/storage/drivers/counter.go | 5 + .../drivers/devmapper/devmapper_doc.go | 1 + .../storage/drivers/devmapper/driver.go | 7 +- .../storage/drivers/devmapper/mount.go | 1 + .../containers/storage/drivers/driver.go | 58 +- .../storage/drivers/driver_solaris.go | 1 + .../storage/drivers/driver_unsupported.go | 1 + .../containers/storage/drivers/fsdiff.go | 20 +- .../storage/drivers/overlay/idmapped_utils.go | 10 +- .../storage/drivers/overlay/mount.go | 97 +- .../storage/drivers/overlay/overlay.go | 104 +- .../storage/drivers/overlay/overlay_cgo.go | 1 + .../storage/drivers/overlay/overlay_nocgo.go | 1 + .../drivers/overlay/overlay_unsupported.go | 1 + .../storage/drivers/quota/projectquota.go | 62 +- .../storage/drivers/register/register_aufs.go | 1 + .../drivers/register/register_btrfs.go | 1 + .../drivers/register/register_devicemapper.go | 1 + .../drivers/register/register_overlay.go | 1 + .../storage/drivers/register/register_zfs.go | 1 + .../containers/storage/drivers/template.go | 3 +- .../storage/drivers/vfs/copy_unsupported.go | 1 + .../containers/storage/drivers/vfs/driver.go | 41 +- .../storage/drivers/windows/windows.go | 7 +- .../containers/storage/drivers/zfs/zfs.go | 11 +- .../storage/drivers/zfs/zfs_unsupported.go | 9 +- vendor/github.com/containers/storage/idset.go | 4 +- .../github.com/containers/storage/images.go | 529 ++- .../github.com/containers/storage/layers.go | 1185 ++++-- .../containers/storage/lockfile_compat.go | 5 +- .../containers/storage/pkg/archive/archive.go | 27 +- .../storage/pkg/archive/archive_110.go | 1 + .../storage/pkg/archive/archive_19.go | 1 + .../storage/pkg/archive/archive_bsd.go | 19 + .../storage/pkg/archive/archive_freebsd.go | 129 - .../storage/pkg/archive/archive_linux.go | 19 + .../storage/pkg/archive/archive_other.go | 1 + .../storage/pkg/archive/archive_unix.go | 27 +- .../containers/storage/pkg/archive/changes.go | 2 +- .../storage/pkg/archive/changes_other.go | 13 +- .../storage/pkg/archive/changes_unix.go | 2 + .../storage/pkg/archive/copy_unix.go | 1 + .../containers/storage/pkg/archive/diff.go | 21 +- .../storage/pkg/archive/fflags_bsd.go | 167 + .../storage/pkg/archive/fflags_unsupported.go | 21 + .../storage/pkg/archive/time_unsupported.go | 1 + .../storage/pkg/chrootarchive/archive.go | 2 +- .../pkg/chrootarchive/archive_darwin.go | 6 +- .../pkg/chrootarchive/archive_windows.go | 2 +- .../storage/pkg/chrootarchive/chroot_unix.go | 1 + .../storage/pkg/chrootarchive/diff_darwin.go | 4 +- .../storage/pkg/chrootarchive/jsoniter.go | 3 + .../storage/pkg/chunked/compression.go | 252 -- .../storage/pkg/chunked/compression_linux.go | 259 ++ .../pkg/chunked/compressor/compressor.go | 12 +- .../pkg/chunked/internal/compression.go | 8 +- .../containers/storage/pkg/chunked/storage.go | 2 +- .../storage/pkg/devicemapper/devmapper_log.go | 1 + .../pkg/devicemapper/devmapper_wrapper.go | 1 + .../devmapper_wrapper_deferred_remove.go | 1 + .../devicemapper/devmapper_wrapper_dynamic.go | 1 + .../devmapper_wrapper_no_deferred_remove.go | 1 + .../devicemapper/devmapper_wrapper_static.go | 1 + .../storage/pkg/devicemapper/ioctl.go | 1 + .../storage/pkg/dmesg/dmesg_linux.go | 1 + .../storage/pkg/fileutils/fileutils.go | 6 +- .../storage/pkg/homedir/homedir_others.go | 1 + .../storage/pkg/homedir/homedir_unix.go | 2 +- .../containers/storage/pkg/idtools/idtools.go | 4 +- .../pkg/idtools/idtools_unsupported.go | 1 + .../storage/pkg/idtools/idtools_windows.go | 1 + .../storage/pkg/idtools/usergroupadd_linux.go | 5 +- .../storage/pkg/ioutils/fswriters.go | 45 +- .../pkg/ioutils/fswriters_unsupported.go | 1 + .../storage/pkg/lockfile/lockfile.go | 74 +- .../storage/pkg/lockfile/lockfile_unix.go | 279 +- .../storage/pkg/lockfile/lockfile_windows.go | 118 +- .../storage/pkg/loopback/attach_loopback.go | 1 + .../containers/storage/pkg/loopback/ioctl.go | 1 + .../storage/pkg/loopback/loop_wrapper.go | 1 + .../storage/pkg/loopback/loopback.go | 1 + .../storage/pkg/mount/unmount_unix.go | 27 +- .../storage/pkg/mount/unmount_unsupported.go | 1 + .../storage/pkg/parsers/kernel/kernel.go | 1 + .../pkg/parsers/kernel/kernel_darwin.go | 2 +- .../storage/pkg/parsers/kernel/kernel_unix.go | 1 + .../pkg/parsers/kernel/kernel_windows.go | 1 + .../pkg/parsers/kernel/uname_freebsd.go | 17 + .../pkg/parsers/kernel/uname_unsupported.go | 16 +- .../parsers/kernel/uname_unsupported_type.go | 11 + .../storage/pkg/reexec/command_freebsd.go | 1 + .../storage/pkg/reexec/command_linux.go | 1 + .../storage/pkg/reexec/command_unsupported.go | 1 + .../storage/pkg/reexec/command_windows.go | 1 + .../containers/storage/pkg/regexp/regexp.go | 214 + .../pkg/regexp/regexp_dontprecompile.go | 6 + .../storage/pkg/regexp/regexp_precompile.go | 6 + .../storage/pkg/stringid/stringid.go | 7 +- .../storage/pkg/system/chtimes_unix.go | 1 + .../storage/pkg/system/chtimes_windows.go | 1 + .../containers/storage/pkg/system/init.go | 2 +- .../storage/pkg/system/lchflags_bsd.go | 56 + .../storage/pkg/system/lcow_unix.go | 1 + .../storage/pkg/system/lstat_unix.go | 3 +- .../storage/pkg/system/mknod_windows.go | 1 + .../storage/pkg/system/path_unix.go | 1 + .../storage/pkg/system/process_unix.go | 1 + .../containers/storage/pkg/system/rm.go | 19 +- .../storage/pkg/system/rm_freebsd.go | 17 +- .../storage/pkg/system/stat_common.go | 13 + .../storage/pkg/system/stat_freebsd.go | 17 +- .../storage/pkg/system/stat_linux.go | 3 +- .../storage/pkg/system/stat_unix.go | 8 + .../storage/pkg/system/stat_windows.go | 6 + .../containers/storage/pkg/system/umask.go | 1 + .../storage/pkg/system/umask_windows.go | 1 + .../storage/pkg/system/utimes_unsupported.go | 1 + .../storage/pkg/system/xattrs_unsupported.go | 1 + .../storage/pkg/truncindex/truncindex.go | 10 +- .../storage/pkg/unshare/getenv_linux_cgo.go | 1 + .../storage/pkg/unshare/getenv_linux_nocgo.go | 1 + .../containers/storage/pkg/unshare/unshare.go | 17 - .../storage/pkg/unshare/unshare_darwin.go | 1 + .../storage/pkg/unshare/unshare_gccgo.go | 1 + .../storage/pkg/unshare/unshare_linux.go | 19 +- .../containers/storage/storage.conf | 4 + vendor/github.com/containers/storage/store.go | 2889 ++++++-------- .../containers/storage/types/errors.go | 2 + .../containers/storage/types/options.go | 59 +- .../storage/types/options_darwin.go | 6 +- .../storage/types/options_freebsd.go | 5 +- .../containers/storage/types/options_linux.go | 5 +- .../storage/types/options_windows.go | 5 +- .../containers/storage/types/utils.go | 5 +- .../github.com/containers/storage/userns.go | 57 +- vendor/github.com/containers/storage/utils.go | 13 + .../v22/internal/dlopen/dlopen_example.go | 57 + .../go-systemd/v22/journal/journal_unix.go | 52 + .../go-systemd/v22/journal/journal_windows.go | 8 + .../cyberphone/json-canonicalization/LICENSE | 13 + .../webpki.org/jsoncanonicalizer/es6numfmt.go | 71 + .../jsoncanonicalizer/jsoncanonicalizer.go | 378 ++ .../github.com/docker/docker/api/swagger.yaml | 2 +- .../docker/docker/api/types/filters/parse.go | 5 +- .../github.com/fsouza/go-dockerclient/auth.go | 13 +- .../fsouza/go-dockerclient/client.go | 14 +- .../fsouza/go-dockerclient/client_unix.go | 1 - .../github.com/fsouza/go-dockerclient/env.go | 8 +- .../fsouza/go-dockerclient/image.go | 2 +- .../fsouza/go-dockerclient/network.go | 28 +- .../fsouza/go-dockerclient/volume.go | 2 +- .../go-openapi/analysis/.codecov.yml | 5 + .../go-openapi/analysis/.gitattributes | 2 + .../github.com/go-openapi/analysis/.gitignore | 5 + .../go-openapi/analysis/.golangci.yml | 56 + .../go-openapi/analysis/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/analysis/LICENSE | 202 + .../github.com/go-openapi/analysis/README.md | 31 + .../go-openapi/analysis/analyzer.go | 1064 +++++ .../go-openapi/analysis/appveyor.yml | 32 + .../github.com/go-openapi/analysis/debug.go | 23 + vendor/github.com/go-openapi/analysis/doc.go | 43 + .../github.com/go-openapi/analysis/fixer.go | 79 + .../github.com/go-openapi/analysis/flatten.go | 802 ++++ .../go-openapi/analysis/flatten_name.go | 293 ++ .../go-openapi/analysis/flatten_options.go | 78 + .../analysis/internal/debug/debug.go | 41 + .../internal/flatten/normalize/normalize.go | 87 + .../internal/flatten/operations/operations.go | 90 + .../internal/flatten/replace/replace.go | 434 ++ .../flatten/schutils/flatten_schema.go | 29 + .../analysis/internal/flatten/sortref/keys.go | 201 + .../internal/flatten/sortref/sort_ref.go | 141 + .../github.com/go-openapi/analysis/mixin.go | 515 +++ .../github.com/go-openapi/analysis/schema.go | 256 ++ .../go-openapi/errors/.gitattributes | 1 + .../github.com/go-openapi/errors/.gitignore | 2 + .../go-openapi/errors/.golangci.yml | 48 + .../go-openapi/errors/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/errors/LICENSE | 202 + vendor/github.com/go-openapi/errors/README.md | 11 + vendor/github.com/go-openapi/errors/api.go | 182 + vendor/github.com/go-openapi/errors/auth.go | 22 + vendor/github.com/go-openapi/errors/doc.go | 26 + .../github.com/go-openapi/errors/headers.go | 103 + .../go-openapi/errors/middleware.go | 50 + .../github.com/go-openapi/errors/parsing.go | 78 + vendor/github.com/go-openapi/errors/schema.go | 611 +++ .../go-openapi/jsonpointer/.editorconfig | 26 + .../go-openapi/jsonpointer/.gitignore | 1 + .../go-openapi/jsonpointer/.travis.yml | 15 + .../go-openapi/jsonpointer/CODE_OF_CONDUCT.md | 74 + .../github.com/go-openapi/jsonpointer/LICENSE | 202 + .../go-openapi/jsonpointer/README.md | 15 + .../go-openapi/jsonpointer/pointer.go | 390 ++ .../go-openapi/jsonreference/.gitignore | 1 + .../go-openapi/jsonreference/.golangci.yml | 41 + .../go-openapi/jsonreference/.travis.yml | 24 + .../jsonreference/CODE_OF_CONDUCT.md | 74 + .../go-openapi/jsonreference/LICENSE | 202 + .../go-openapi/jsonreference/README.md | 15 + .../jsonreference/internal/normalize_url.go | 63 + .../go-openapi/jsonreference/reference.go | 158 + .../github.com/go-openapi/loads/.editorconfig | 26 + vendor/github.com/go-openapi/loads/.gitignore | 4 + .../github.com/go-openapi/loads/.golangci.yml | 44 + .../github.com/go-openapi/loads/.travis.yml | 25 + .../go-openapi/loads/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/loads/LICENSE | 202 + vendor/github.com/go-openapi/loads/README.md | 6 + vendor/github.com/go-openapi/loads/doc.go | 21 + vendor/github.com/go-openapi/loads/loaders.go | 134 + vendor/github.com/go-openapi/loads/options.go | 61 + vendor/github.com/go-openapi/loads/spec.go | 266 ++ .../go-openapi/runtime/.editorconfig | 26 + .../go-openapi/runtime/.gitattributes | 1 + .../github.com/go-openapi/runtime/.gitignore | 5 + .../go-openapi/runtime/.golangci.yml | 44 + .../go-openapi/runtime/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/runtime/LICENSE | 202 + .../github.com/go-openapi/runtime/README.md | 7 + .../go-openapi/runtime/bytestream.go | 169 + .../go-openapi/runtime/client_auth_info.go | 30 + .../go-openapi/runtime/client_operation.go | 41 + .../go-openapi/runtime/client_request.go | 153 + .../go-openapi/runtime/client_response.go | 110 + .../go-openapi/runtime/constants.go | 49 + vendor/github.com/go-openapi/runtime/csv.go | 77 + .../github.com/go-openapi/runtime/discard.go | 9 + vendor/github.com/go-openapi/runtime/file.go | 19 + .../github.com/go-openapi/runtime/headers.go | 45 + .../go-openapi/runtime/interfaces.go | 112 + vendor/github.com/go-openapi/runtime/json.go | 38 + .../github.com/go-openapi/runtime/request.go | 139 + .../github.com/go-openapi/runtime/statuses.go | 90 + vendor/github.com/go-openapi/runtime/text.go | 116 + .../github.com/go-openapi/runtime/values.go | 19 + vendor/github.com/go-openapi/runtime/xml.go | 36 + .../github.com/go-openapi/spec/.editorconfig | 26 + vendor/github.com/go-openapi/spec/.gitignore | 2 + .../github.com/go-openapi/spec/.golangci.yml | 42 + .../go-openapi/spec/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/spec/LICENSE | 202 + vendor/github.com/go-openapi/spec/README.md | 34 + .../github.com/go-openapi/spec/appveyor.yml | 32 + vendor/github.com/go-openapi/spec/bindata.go | 297 ++ vendor/github.com/go-openapi/spec/cache.go | 98 + .../go-openapi/spec/contact_info.go | 57 + vendor/github.com/go-openapi/spec/debug.go | 49 + vendor/github.com/go-openapi/spec/errors.go | 19 + vendor/github.com/go-openapi/spec/expander.go | 594 +++ .../go-openapi/spec/external_docs.go | 24 + vendor/github.com/go-openapi/spec/header.go | 203 + vendor/github.com/go-openapi/spec/info.go | 165 + vendor/github.com/go-openapi/spec/items.go | 234 ++ vendor/github.com/go-openapi/spec/license.go | 56 + .../github.com/go-openapi/spec/normalizer.go | 202 + .../go-openapi/spec/normalizer_nonwindows.go | 44 + .../go-openapi/spec/normalizer_windows.go | 154 + .../github.com/go-openapi/spec/operation.go | 397 ++ .../github.com/go-openapi/spec/parameter.go | 326 ++ .../github.com/go-openapi/spec/path_item.go | 87 + vendor/github.com/go-openapi/spec/paths.go | 97 + .../github.com/go-openapi/spec/properties.go | 91 + vendor/github.com/go-openapi/spec/ref.go | 193 + vendor/github.com/go-openapi/spec/resolver.go | 127 + vendor/github.com/go-openapi/spec/response.go | 152 + .../github.com/go-openapi/spec/responses.go | 127 + vendor/github.com/go-openapi/spec/schema.go | 645 +++ .../go-openapi/spec/schema_loader.go | 338 ++ .../go-openapi/spec/security_scheme.go | 170 + vendor/github.com/go-openapi/spec/spec.go | 78 + vendor/github.com/go-openapi/spec/swagger.go | 448 +++ vendor/github.com/go-openapi/spec/tag.go | 75 + vendor/github.com/go-openapi/spec/url_go18.go | 8 + vendor/github.com/go-openapi/spec/url_go19.go | 14 + .../github.com/go-openapi/spec/validations.go | 215 + .../github.com/go-openapi/spec/xml_object.go | 68 + .../go-openapi/strfmt/.editorconfig | 26 + .../go-openapi/strfmt/.gitattributes | 2 + .../github.com/go-openapi/strfmt/.gitignore | 2 + .../go-openapi/strfmt/.golangci.yml | 50 + .../go-openapi/strfmt/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/strfmt/LICENSE | 202 + vendor/github.com/go-openapi/strfmt/README.md | 88 + vendor/github.com/go-openapi/strfmt/bson.go | 165 + vendor/github.com/go-openapi/strfmt/date.go | 187 + .../github.com/go-openapi/strfmt/default.go | 2035 ++++++++++ vendor/github.com/go-openapi/strfmt/doc.go | 18 + .../github.com/go-openapi/strfmt/duration.go | 211 + vendor/github.com/go-openapi/strfmt/format.go | 326 ++ vendor/github.com/go-openapi/strfmt/time.go | 294 ++ vendor/github.com/go-openapi/strfmt/ulid.go | 225 ++ .../github.com/go-openapi/swag/.editorconfig | 26 + .../github.com/go-openapi/swag/.gitattributes | 2 + vendor/github.com/go-openapi/swag/.gitignore | 4 + .../github.com/go-openapi/swag/.golangci.yml | 54 + .../go-openapi/swag/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/swag/LICENSE | 202 + vendor/github.com/go-openapi/swag/README.md | 21 + vendor/github.com/go-openapi/swag/convert.go | 208 + .../go-openapi/swag/convert_types.go | 730 ++++ vendor/github.com/go-openapi/swag/doc.go | 31 + vendor/github.com/go-openapi/swag/file.go | 33 + vendor/github.com/go-openapi/swag/json.go | 312 ++ vendor/github.com/go-openapi/swag/loading.go | 121 + .../github.com/go-openapi/swag/name_lexem.go | 87 + vendor/github.com/go-openapi/swag/net.go | 38 + vendor/github.com/go-openapi/swag/path.go | 59 + .../github.com/go-openapi/swag/post_go18.go | 24 + .../github.com/go-openapi/swag/post_go19.go | 68 + vendor/github.com/go-openapi/swag/pre_go18.go | 24 + vendor/github.com/go-openapi/swag/pre_go19.go | 70 + vendor/github.com/go-openapi/swag/split.go | 262 ++ vendor/github.com/go-openapi/swag/util.go | 386 ++ vendor/github.com/go-openapi/swag/yaml.go | 450 +++ .../go-openapi/validate/.editorconfig | 26 + .../go-openapi/validate/.gitattributes | 2 + .../github.com/go-openapi/validate/.gitignore | 5 + .../go-openapi/validate/.golangci.yml | 50 + .../go-openapi/validate/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/validate/LICENSE | 202 + .../github.com/go-openapi/validate/README.md | 38 + .../go-openapi/validate/appveyor.yml | 32 + .../github.com/go-openapi/validate/context.go | 56 + .../github.com/go-openapi/validate/debug.go | 47 + .../go-openapi/validate/default_validator.go | 281 ++ vendor/github.com/go-openapi/validate/doc.go | 85 + .../go-openapi/validate/example_validator.go | 270 ++ .../github.com/go-openapi/validate/formats.go | 69 + .../github.com/go-openapi/validate/helpers.go | 324 ++ .../go-openapi/validate/object_validator.go | 279 ++ .../github.com/go-openapi/validate/options.go | 43 + .../github.com/go-openapi/validate/result.go | 486 +++ vendor/github.com/go-openapi/validate/rexp.go | 71 + .../github.com/go-openapi/validate/schema.go | 260 ++ .../go-openapi/validate/schema_messages.go | 78 + .../go-openapi/validate/schema_option.go | 54 + .../go-openapi/validate/schema_props.go | 240 ++ .../go-openapi/validate/slice_validator.go | 105 + vendor/github.com/go-openapi/validate/spec.go | 804 ++++ .../go-openapi/validate/spec_messages.go | 360 ++ vendor/github.com/go-openapi/validate/type.go | 177 + .../go-openapi/validate/update-fixtures.sh | 15 + .../go-openapi/validate/validator.go | 645 +++ .../github.com/go-openapi/validate/values.go | 446 +++ .../github.com/godbus/dbus/v5/.golangci.yml | 7 + vendor/github.com/godbus/dbus/v5/README.md | 1 + vendor/github.com/godbus/dbus/v5/auth.go | 44 +- vendor/github.com/godbus/dbus/v5/call.go | 3 - vendor/github.com/godbus/dbus/v5/conn.go | 25 +- .../github.com/godbus/dbus/v5/conn_darwin.go | 1 - .../github.com/godbus/dbus/v5/conn_other.go | 2 +- vendor/github.com/godbus/dbus/v5/conn_unix.go | 3 +- .../github.com/godbus/dbus/v5/conn_windows.go | 2 - vendor/github.com/godbus/dbus/v5/dbus.go | 17 +- vendor/github.com/godbus/dbus/v5/decoder.go | 6 +- .../godbus/dbus/v5/default_handler.go | 6 +- vendor/github.com/godbus/dbus/v5/export.go | 4 +- vendor/github.com/godbus/dbus/v5/match.go | 8 +- vendor/github.com/godbus/dbus/v5/message.go | 12 +- vendor/github.com/godbus/dbus/v5/object.go | 4 +- .../godbus/dbus/v5/sequential_handler.go | 2 +- .../godbus/dbus/v5/server_interfaces.go | 2 +- vendor/github.com/godbus/dbus/v5/sig.go | 3 + .../godbus/dbus/v5/transport_nonce_tcp.go | 3 +- .../godbus/dbus/v5/transport_unix.go | 16 +- .../dbus/v5/transport_unixcred_freebsd.go | 38 +- .../godbus/dbus/v5/variant_parser.go | 2 - .../go-containerregistry/pkg/name/errors.go | 6 + vendor/github.com/josharian/intern/README.md | 5 + vendor/github.com/josharian/intern/intern.go | 44 + vendor/github.com/josharian/intern/license.md | 21 + .../klauspost/compress/.goreleaser.yml | 2 +- .../github.com/klauspost/compress/README.md | 24 +- .../klauspost/compress/flate/deflate.go | 123 +- .../klauspost/compress/flate/stateless.go | 19 +- .../klauspost/compress/fse/compress.go | 31 +- .../klauspost/compress/huff0/bitreader.go | 8 +- .../klauspost/compress/huff0/compress.go | 114 +- .../compress/huff0/decompress_amd64.s | 584 ++- .../klauspost/compress/zstd/blockdec.go | 16 +- .../klauspost/compress/zstd/decodeheader.go | 9 +- .../klauspost/compress/zstd/decoder.go | 95 +- .../compress/zstd/decoder_options.go | 26 +- .../klauspost/compress/zstd/dict.go | 8 +- .../klauspost/compress/zstd/enc_base.go | 26 +- .../klauspost/compress/zstd/enc_best.go | 63 +- .../klauspost/compress/zstd/enc_better.go | 12 +- .../klauspost/compress/zstd/enc_dfast.go | 16 +- .../klauspost/compress/zstd/enc_fast.go | 14 +- .../klauspost/compress/zstd/encoder.go | 35 + .../compress/zstd/encoder_options.go | 36 +- .../klauspost/compress/zstd/framedec.go | 58 +- .../compress/zstd/internal/xxhash/README.md | 49 +- .../compress/zstd/internal/xxhash/xxhash.go | 47 +- .../zstd/internal/xxhash/xxhash_amd64.s | 336 +- .../zstd/internal/xxhash/xxhash_arm64.s | 140 +- .../zstd/internal/xxhash/xxhash_asm.go | 2 +- .../zstd/internal/xxhash/xxhash_other.go | 19 +- .../klauspost/compress/zstd/seqdec_amd64.s | 28 +- .../klauspost/compress/zstd/zstd.go | 31 +- .../letsencrypt/boulder/core/interfaces.go | 1 - .../letsencrypt/boulder/core/objects.go | 42 +- .../letsencrypt/boulder/core/util.go | 12 +- .../letsencrypt/boulder/errors/errors.go | 41 +- .../boulder/features/featureflag_string.go | 53 +- .../letsencrypt/boulder/features/features.go | 32 +- .../letsencrypt/boulder/goodkey/blocked.go | 14 +- .../letsencrypt/boulder/goodkey/weak.go | 4 +- .../letsencrypt/boulder/sa/proto/sa.pb.go | 1512 ++++--- .../letsencrypt/boulder/sa/proto/sa.proto | 99 +- .../boulder/sa/proto/sa_grpc.pb.go | 2497 +++++++++--- .../letsencrypt/boulder/sa/proto/subsets.go | 1 + vendor/github.com/mailru/easyjson/LICENSE | 7 + .../github.com/mailru/easyjson/buffer/pool.go | 278 ++ .../mailru/easyjson/jlexer/bytestostr.go | 24 + .../easyjson/jlexer/bytestostr_nounsafe.go | 13 + .../mailru/easyjson/jlexer/error.go | 15 + .../mailru/easyjson/jlexer/lexer.go | 1244 ++++++ .../mailru/easyjson/jwriter/writer.go | 405 ++ .../github.com/mattn/go-runewidth/.travis.yml | 16 - .../github.com/mattn/go-runewidth/README.md | 2 +- .../github.com/mattn/go-runewidth/go.test.sh | 12 - .../mattn/go-runewidth/runewidth.go | 93 +- .../mattn/go-runewidth/runewidth_appengine.go | 1 + .../mattn/go-runewidth/runewidth_js.go | 4 +- .../mattn/go-runewidth/runewidth_posix.go | 5 +- .../mattn/go-runewidth/runewidth_windows.go | 4 +- .../mitchellh/mapstructure/CHANGELOG.md | 96 + .../semver => mitchellh/mapstructure}/LICENSE | 5 +- .../mitchellh/mapstructure/README.md | 46 + .../mitchellh/mapstructure/decode_hooks.go | 279 ++ .../mitchellh/mapstructure/error.go | 50 + .../mitchellh/mapstructure/mapstructure.go | 1540 ++++++++ vendor/github.com/oklog/ulid/.gitignore | 29 + vendor/github.com/oklog/ulid/.travis.yml | 16 + vendor/github.com/oklog/ulid/AUTHORS.md | 2 + vendor/github.com/oklog/ulid/CHANGELOG.md | 33 + vendor/github.com/oklog/ulid/CONTRIBUTING.md | 17 + vendor/github.com/oklog/ulid/Gopkg.lock | 15 + vendor/github.com/oklog/ulid/Gopkg.toml | 26 + vendor/github.com/oklog/ulid/LICENSE | 201 + vendor/github.com/oklog/ulid/README.md | 150 + vendor/github.com/oklog/ulid/ulid.go | 614 +++ .../runtime-spec/specs-go/config.go | 46 +- .../runtime-tools/generate/generate.go | 6 + .../runtime-tools/validate/validate.go | 12 +- vendor/github.com/rivo/uniseg/README.md | 139 +- vendor/github.com/rivo/uniseg/doc.go | 108 +- .../github.com/rivo/uniseg/eastasianwidth.go | 2556 ++++++++++++ .../rivo/uniseg/emojipresentation.go | 285 ++ .../github.com/rivo/uniseg/gen_breaktest.go | 213 + .../github.com/rivo/uniseg/gen_properties.go | 256 ++ vendor/github.com/rivo/uniseg/grapheme.go | 484 ++- .../rivo/uniseg/graphemeproperties.go | 1891 +++++++++ .../github.com/rivo/uniseg/graphemerules.go | 138 + vendor/github.com/rivo/uniseg/line.go | 131 + .../github.com/rivo/uniseg/lineproperties.go | 3513 +++++++++++++++++ vendor/github.com/rivo/uniseg/linerules.go | 470 +++ vendor/github.com/rivo/uniseg/properties.go | 1760 +-------- vendor/github.com/rivo/uniseg/sentence.go | 88 + .../rivo/uniseg/sentenceproperties.go | 2815 +++++++++++++ .../github.com/rivo/uniseg/sentencerules.go | 205 + vendor/github.com/rivo/uniseg/step.go | 244 ++ vendor/github.com/rivo/uniseg/width.go | 54 + vendor/github.com/rivo/uniseg/word.go | 87 + .../github.com/rivo/uniseg/wordproperties.go | 1848 +++++++++ vendor/github.com/rivo/uniseg/wordrules.go | 246 ++ .../github.com/sigstore/fulcio/COPYRIGHT.txt | 14 + vendor/github.com/sigstore/fulcio/LICENSE | 201 + .../sigstore/fulcio/pkg/certificate/doc.go | 17 + .../fulcio/pkg/certificate/extensions.go | 132 + .../github.com/sigstore/rekor/CONTRIBUTORS.md | 116 + .../github.com/sigstore/rekor/COPYRIGHT.txt | 14 + vendor/github.com/sigstore/rekor/LICENSE | 202 + .../rekor/pkg/generated/models/alpine.go | 210 + .../pkg/generated/models/alpine_schema.go | 29 + .../generated/models/alpine_v001_schema.go | 448 +++ .../pkg/generated/models/consistency_proof.go | 118 + .../rekor/pkg/generated/models/cose.go | 210 + .../rekor/pkg/generated/models/cose_schema.go | 29 + .../pkg/generated/models/cose_v001_schema.go | 508 +++ .../rekor/pkg/generated/models/error.go | 69 + .../pkg/generated/models/hashedrekord.go | 210 + .../generated/models/hashedrekord_schema.go | 29 + .../models/hashedrekord_v001_schema.go | 501 +++ .../rekor/pkg/generated/models/helm.go | 210 + .../rekor/pkg/generated/models/helm_schema.go | 29 + .../pkg/generated/models/helm_v001_schema.go | 649 +++ .../models/inactive_shard_log_info.go | 153 + .../pkg/generated/models/inclusion_proof.go | 179 + .../rekor/pkg/generated/models/intoto.go | 210 + .../pkg/generated/models/intoto_schema.go | 29 + .../generated/models/intoto_v001_schema.go | 503 +++ .../generated/models/intoto_v002_schema.go | 724 ++++ .../rekor/pkg/generated/models/jar.go | 210 + .../rekor/pkg/generated/models/jar_schema.go | 29 + .../pkg/generated/models/jar_v001_schema.go | 557 +++ .../rekor/pkg/generated/models/log_entry.go | 430 ++ .../rekor/pkg/generated/models/log_info.go | 216 + .../pkg/generated/models/proposed_entry.go | 189 + .../rekor/pkg/generated/models/rekord.go | 210 + .../pkg/generated/models/rekord_schema.go | 29 + .../generated/models/rekord_v001_schema.go | 603 +++ .../rekor/pkg/generated/models/rfc3161.go | 210 + .../pkg/generated/models/rfc3161_schema.go | 29 + .../generated/models/rfc3161_v001_schema.go | 182 + .../rekor/pkg/generated/models/rpm.go | 210 + .../rekor/pkg/generated/models/rpm_schema.go | 29 + .../pkg/generated/models/rpm_v001_schema.go | 443 +++ .../pkg/generated/models/search_index.go | 336 ++ .../pkg/generated/models/search_log_query.go | 293 ++ .../rekor/pkg/generated/models/tuf.go | 210 + .../rekor/pkg/generated/models/tuf_schema.go | 29 + .../pkg/generated/models/tuf_v001_schema.go | 283 ++ .../sigstore/sigstore/pkg/cryptoutils/sans.go | 149 + .../sigstore/sigstore/pkg/signature/ecdsa.go | 9 + .../sylabs/sif/v2/pkg/sif/create.go | 4 +- .../sylabs/sif/v2/pkg/sif/descriptor.go | 5 + vendor/github.com/ulikunitz/xz/.gitignore | 3 + vendor/github.com/ulikunitz/xz/LICENSE | 2 +- vendor/github.com/ulikunitz/xz/README.md | 4 + vendor/github.com/ulikunitz/xz/TODO.md | 11 +- vendor/github.com/ulikunitz/xz/bits.go | 2 +- vendor/github.com/ulikunitz/xz/crc.go | 2 +- vendor/github.com/ulikunitz/xz/format.go | 2 +- .../ulikunitz/xz/internal/hash/cyclic_poly.go | 2 +- .../ulikunitz/xz/internal/hash/doc.go | 2 +- .../ulikunitz/xz/internal/hash/rabin_karp.go | 2 +- .../ulikunitz/xz/internal/hash/roller.go | 2 +- .../ulikunitz/xz/internal/xlog/xlog.go | 5 +- .../github.com/ulikunitz/xz/lzma/bintree.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/bitops.go | 2 +- .../github.com/ulikunitz/xz/lzma/breader.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/buffer.go | 2 +- .../ulikunitz/xz/lzma/bytewriter.go | 2 +- .../github.com/ulikunitz/xz/lzma/decoder.go | 2 +- .../ulikunitz/xz/lzma/decoderdict.go | 2 +- .../ulikunitz/xz/lzma/directcodec.go | 2 +- .../github.com/ulikunitz/xz/lzma/distcodec.go | 2 +- .../github.com/ulikunitz/xz/lzma/encoder.go | 2 +- .../ulikunitz/xz/lzma/encoderdict.go | 2 +- .../github.com/ulikunitz/xz/lzma/hashtable.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/header.go | 2 +- .../github.com/ulikunitz/xz/lzma/header2.go | 2 +- .../ulikunitz/xz/lzma/lengthcodec.go | 5 +- .../ulikunitz/xz/lzma/literalcodec.go | 2 +- .../ulikunitz/xz/lzma/matchalgorithm.go | 2 +- .../github.com/ulikunitz/xz/lzma/operation.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/prob.go | 2 +- .../ulikunitz/xz/lzma/properties.go | 2 +- .../ulikunitz/xz/lzma/rangecodec.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/reader.go | 4 +- .../github.com/ulikunitz/xz/lzma/reader2.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/state.go | 2 +- .../ulikunitz/xz/lzma/treecodecs.go | 2 +- vendor/github.com/ulikunitz/xz/lzma/writer.go | 2 +- .../github.com/ulikunitz/xz/lzma/writer2.go | 2 +- vendor/github.com/ulikunitz/xz/lzmafilter.go | 2 +- vendor/github.com/ulikunitz/xz/none-check.go | 2 +- vendor/github.com/ulikunitz/xz/reader.go | 2 +- vendor/github.com/ulikunitz/xz/writer.go | 2 +- .../vishvananda/netlink/addr_linux.go | 17 +- .../github.com/vishvananda/netlink/filter.go | 33 + .../vishvananda/netlink/filter_linux.go | 22 +- .../vishvananda/netlink/handle_linux.go | 15 + vendor/github.com/vishvananda/netlink/link.go | 1 + .../vishvananda/netlink/link_linux.go | 8 +- .../vishvananda/netlink/nl/nl_linux.go | 64 +- .../vishvananda/netlink/nl/tc_linux.go | 31 + .../{proc_event.go => proc_event_linux.go} | 0 .../github.com/vishvananda/netlink/route.go | 18 + .../vishvananda/netlink/route_linux.go | 22 +- vendor/github.com/vishvananda/netlink/rule.go | 1 + .../vishvananda/netlink/rule_linux.go | 8 + .../vishvananda/netlink/xfrm_policy_linux.go | 12 +- .../vishvananda/netlink/xfrm_state_linux.go | 12 +- vendor/go.mongodb.org/mongo-driver/LICENSE | 201 + .../go.mongodb.org/mongo-driver/bson/bson.go | 50 + .../bson/bsoncodec/array_codec.go | 50 + .../mongo-driver/bson/bsoncodec/bsoncodec.go | 238 ++ .../bson/bsoncodec/byte_slice_codec.go | 111 + .../bson/bsoncodec/cond_addr_codec.go | 63 + .../bson/bsoncodec/default_value_decoders.go | 1729 ++++++++ .../bson/bsoncodec/default_value_encoders.go | 766 ++++ .../mongo-driver/bson/bsoncodec/doc.go | 90 + .../bson/bsoncodec/empty_interface_codec.go | 147 + .../mongo-driver/bson/bsoncodec/map_codec.go | 309 ++ .../mongo-driver/bson/bsoncodec/mode.go | 65 + .../bson/bsoncodec/pointer_codec.go | 109 + .../mongo-driver/bson/bsoncodec/proxy.go | 14 + .../mongo-driver/bson/bsoncodec/registry.go | 469 +++ .../bson/bsoncodec/slice_codec.go | 199 + .../bson/bsoncodec/string_codec.go | 119 + .../bson/bsoncodec/struct_codec.go | 664 ++++ .../bson/bsoncodec/struct_tag_parser.go | 139 + .../mongo-driver/bson/bsoncodec/time_codec.go | 127 + .../mongo-driver/bson/bsoncodec/types.go | 57 + .../mongo-driver/bson/bsoncodec/uint_codec.go | 173 + .../bsonoptions/byte_slice_codec_options.go | 38 + .../mongo-driver/bson/bsonoptions/doc.go | 8 + .../empty_interface_codec_options.go | 38 + .../bson/bsonoptions/map_codec_options.go | 67 + .../bson/bsonoptions/slice_codec_options.go | 38 + .../bson/bsonoptions/string_codec_options.go | 41 + .../bson/bsonoptions/struct_codec_options.go | 87 + .../bson/bsonoptions/time_codec_options.go | 38 + .../bson/bsonoptions/uint_codec_options.go | 38 + .../mongo-driver/bson/bsonrw/copier.go | 445 +++ .../mongo-driver/bson/bsonrw/doc.go | 9 + .../bson/bsonrw/extjson_parser.go | 806 ++++ .../bson/bsonrw/extjson_reader.go | 644 +++ .../bson/bsonrw/extjson_tables.go | 223 ++ .../bson/bsonrw/extjson_wrappers.go | 492 +++ .../bson/bsonrw/extjson_writer.go | 732 ++++ .../mongo-driver/bson/bsonrw/json_scanner.go | 528 +++ .../mongo-driver/bson/bsonrw/mode.go | 108 + .../mongo-driver/bson/bsonrw/reader.go | 63 + .../mongo-driver/bson/bsonrw/value_reader.go | 874 ++++ .../mongo-driver/bson/bsonrw/value_writer.go | 606 +++ .../mongo-driver/bson/bsonrw/writer.go | 78 + .../mongo-driver/bson/bsontype/bsontype.go | 97 + .../mongo-driver/bson/decoder.go | 141 + .../go.mongodb.org/mongo-driver/bson/doc.go | 141 + .../mongo-driver/bson/encoder.go | 99 + .../mongo-driver/bson/marshal.go | 248 ++ .../mongo-driver/bson/primitive/decimal.go | 423 ++ .../mongo-driver/bson/primitive/objectid.go | 206 + .../mongo-driver/bson/primitive/primitive.go | 217 + .../mongo-driver/bson/primitive_codecs.go | 92 + .../go.mongodb.org/mongo-driver/bson/raw.go | 85 + .../mongo-driver/bson/raw_element.go | 51 + .../mongo-driver/bson/raw_value.go | 309 ++ .../mongo-driver/bson/registry.go | 24 + .../go.mongodb.org/mongo-driver/bson/types.go | 36 + .../mongo-driver/bson/unmarshal.go | 101 + .../mongo-driver/x/bsonx/bsoncore/array.go | 164 + .../x/bsonx/bsoncore/bson_arraybuilder.go | 201 + .../x/bsonx/bsoncore/bson_documentbuilder.go | 189 + .../mongo-driver/x/bsonx/bsoncore/bsoncore.go | 862 ++++ .../mongo-driver/x/bsonx/bsoncore/document.go | 386 ++ .../x/bsonx/bsoncore/document_sequence.go | 189 + .../mongo-driver/x/bsonx/bsoncore/element.go | 152 + .../mongo-driver/x/bsonx/bsoncore/tables.go | 223 ++ .../mongo-driver/x/bsonx/bsoncore/value.go | 980 +++++ vendor/go.opencensus.io/Makefile | 8 +- vendor/go.opencensus.io/opencensus.go | 2 +- vendor/go.opencensus.io/trace/doc.go | 13 +- vendor/go.opencensus.io/trace/lrumap.go | 2 +- vendor/go.opencensus.io/trace/trace_go11.go | 1 + .../go.opencensus.io/trace/trace_nongo11.go | 1 + vendor/golang.org/x/crypto/bcrypt/bcrypt.go | 11 +- vendor/golang.org/x/crypto/cast5/cast5.go | 11 +- .../x/crypto/openpgp/armor/armor.go | 2 +- .../x/crypto/salsa20/salsa/hsalsa20.go | 66 +- .../x/crypto/salsa20/salsa/salsa208.go | 66 +- .../x/crypto/salsa20/salsa/salsa20_ref.go | 66 +- vendor/golang.org/x/crypto/sha3/keccakf.go | 194 +- vendor/golang.org/x/crypto/ssh/common.go | 15 + vendor/golang.org/x/crypto/ssh/handshake.go | 69 +- vendor/golang.org/x/crypto/ssh/messages.go | 2 +- vendor/golang.org/x/crypto/ssh/server.go | 13 +- vendor/golang.org/x/mod/LICENSE | 27 + vendor/golang.org/x/mod/PATENTS | 22 + vendor/golang.org/x/mod/semver/semver.go | 401 ++ vendor/golang.org/x/net/http2/flow.go | 88 +- vendor/golang.org/x/net/http2/server.go | 85 +- vendor/golang.org/x/net/http2/transport.go | 86 +- .../golang.org/x/net/internal/socks/client.go | 168 + .../golang.org/x/net/internal/socks/socks.go | 317 ++ vendor/golang.org/x/net/proxy/dial.go | 54 + vendor/golang.org/x/net/proxy/direct.go | 31 + vendor/golang.org/x/net/proxy/per_host.go | 155 + vendor/golang.org/x/net/proxy/proxy.go | 149 + vendor/golang.org/x/net/proxy/socks5.go | 42 + .../golang.org/x/sys/cpu/cpu_linux_arm64.go | 44 +- vendor/golang.org/x/sys/cpu/parse.go | 43 + .../x/sys/cpu/proc_cpuinfo_linux.go | 54 + vendor/golang.org/x/sys/unix/gccgo.go | 4 +- vendor/golang.org/x/sys/unix/gccgo_c.c | 4 +- vendor/golang.org/x/sys/unix/ioctl.go | 4 +- vendor/golang.org/x/sys/unix/mkall.sh | 4 +- .../x/sys/unix/syscall_dragonfly.go | 1 + .../golang.org/x/sys/unix/syscall_freebsd.go | 1 + vendor/golang.org/x/sys/unix/syscall_hurd.go | 22 + .../golang.org/x/sys/unix/syscall_hurd_386.go | 29 + vendor/golang.org/x/sys/unix/syscall_linux.go | 50 +- .../golang.org/x/sys/unix/syscall_netbsd.go | 15 + .../golang.org/x/sys/unix/syscall_openbsd.go | 1 + .../x/sys/unix/syscall_openbsd_libc.go | 4 +- .../golang.org/x/sys/unix/syscall_solaris.go | 1 + vendor/golang.org/x/sys/unix/syscall_unix.go | 55 +- .../x/sys/unix/zerrors_openbsd_386.go | 356 +- .../x/sys/unix/zerrors_openbsd_amd64.go | 189 +- .../x/sys/unix/zerrors_openbsd_arm.go | 348 +- .../x/sys/unix/zerrors_openbsd_arm64.go | 160 +- .../x/sys/unix/zerrors_openbsd_mips64.go | 95 +- .../x/sys/unix/zsyscall_dragonfly_amd64.go | 10 + .../x/sys/unix/zsyscall_freebsd_386.go | 10 + .../x/sys/unix/zsyscall_freebsd_amd64.go | 10 + .../x/sys/unix/zsyscall_freebsd_arm.go | 10 + .../x/sys/unix/zsyscall_freebsd_arm64.go | 10 + .../x/sys/unix/zsyscall_freebsd_riscv64.go | 10 + .../x/sys/unix/zsyscall_netbsd_386.go | 10 + .../x/sys/unix/zsyscall_netbsd_amd64.go | 10 + .../x/sys/unix/zsyscall_netbsd_arm.go | 10 + .../x/sys/unix/zsyscall_netbsd_arm64.go | 10 + .../x/sys/unix/zsyscall_openbsd_386.go | 14 + .../x/sys/unix/zsyscall_openbsd_386.s | 137 +- .../x/sys/unix/zsyscall_openbsd_amd64.go | 14 + .../x/sys/unix/zsyscall_openbsd_amd64.s | 137 +- .../x/sys/unix/zsyscall_openbsd_arm.go | 14 + .../x/sys/unix/zsyscall_openbsd_arm.s | 137 +- .../x/sys/unix/zsyscall_openbsd_arm64.go | 14 + .../x/sys/unix/zsyscall_openbsd_arm64.s | 137 +- .../x/sys/unix/zsyscall_openbsd_mips64.go | 812 +++- .../x/sys/unix/zsyscall_openbsd_mips64.s | 669 ++++ .../x/sys/unix/zsyscall_openbsd_ppc64.go | 14 + .../x/sys/unix/zsyscall_openbsd_ppc64.s | 6 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 14 + .../x/sys/unix/zsyscall_openbsd_riscv64.s | 137 +- .../x/sys/unix/zsyscall_solaris_amd64.go | 13 + .../x/sys/unix/zsysctl_openbsd_386.go | 51 +- .../x/sys/unix/zsysctl_openbsd_amd64.go | 17 +- .../x/sys/unix/zsysctl_openbsd_arm.go | 51 +- .../x/sys/unix/zsysctl_openbsd_arm64.go | 11 +- .../x/sys/unix/zsysctl_openbsd_mips64.go | 3 +- .../x/sys/unix/zsysnum_openbsd_mips64.go | 1 + .../x/sys/unix/ztypes_netbsd_386.go | 84 + .../x/sys/unix/ztypes_netbsd_amd64.go | 84 + .../x/sys/unix/ztypes_netbsd_arm.go | 84 + .../x/sys/unix/ztypes_netbsd_arm64.go | 84 + .../x/sys/unix/ztypes_openbsd_386.go | 97 +- .../x/sys/unix/ztypes_openbsd_amd64.go | 33 +- .../x/sys/unix/ztypes_openbsd_arm.go | 9 +- .../x/sys/unix/ztypes_openbsd_arm64.go | 9 +- .../x/sys/unix/ztypes_openbsd_mips64.go | 9 +- vendor/golang.org/x/tools/LICENSE | 27 + vendor/golang.org/x/tools/PATENTS | 22 + .../x/tools/cmd/stringer/stringer.go | 657 +++ .../x/tools/go/gcexportdata/gcexportdata.go | 181 + .../x/tools/go/gcexportdata/importer.go | 75 + .../tools/go/internal/packagesdriver/sizes.go | 49 + vendor/golang.org/x/tools/go/packages/doc.go | 220 ++ .../x/tools/go/packages/external.go | 101 + .../golang.org/x/tools/go/packages/golist.go | 1173 ++++++ .../x/tools/go/packages/golist_overlay.go | 575 +++ .../x/tools/go/packages/loadmode_string.go | 57 + .../x/tools/go/packages/packages.go | 1313 ++++++ .../golang.org/x/tools/go/packages/visit.go | 59 + .../x/tools/internal/event/core/event.go | 85 + .../x/tools/internal/event/core/export.go | 70 + .../x/tools/internal/event/core/fast.go | 77 + .../golang.org/x/tools/internal/event/doc.go | 7 + .../x/tools/internal/event/event.go | 127 + .../x/tools/internal/event/keys/keys.go | 564 +++ .../x/tools/internal/event/keys/standard.go | 22 + .../x/tools/internal/event/label/label.go | 215 + .../x/tools/internal/gcimporter/bexport.go | 852 ++++ .../x/tools/internal/gcimporter/bimport.go | 1053 +++++ .../x/tools/internal/gcimporter/exportdata.go | 99 + .../x/tools/internal/gcimporter/gcimporter.go | 1182 ++++++ .../x/tools/internal/gcimporter/iexport.go | 1056 +++++ .../x/tools/internal/gcimporter/iimport.go | 898 +++++ .../internal/gcimporter/newInterface10.go | 22 + .../internal/gcimporter/newInterface11.go | 14 + .../internal/gcimporter/support_go117.go | 16 + .../internal/gcimporter/support_go118.go | 37 + .../x/tools/internal/gcimporter/unified_no.go | 10 + .../tools/internal/gcimporter/unified_yes.go | 10 + .../x/tools/internal/gcimporter/ureader_no.go | 19 + .../tools/internal/gcimporter/ureader_yes.go | 725 ++++ .../x/tools/internal/gocommand/invoke.go | 356 ++ .../x/tools/internal/gocommand/vendor.go | 109 + .../x/tools/internal/gocommand/version.go | 79 + .../internal/packagesinternal/packages.go | 30 + .../x/tools/internal/pkgbits/codes.go | 77 + .../x/tools/internal/pkgbits/decoder.go | 517 +++ .../x/tools/internal/pkgbits/doc.go | 32 + .../x/tools/internal/pkgbits/encoder.go | 383 ++ .../x/tools/internal/pkgbits/flags.go | 9 + .../x/tools/internal/pkgbits/frames_go1.go | 21 + .../x/tools/internal/pkgbits/frames_go17.go | 28 + .../x/tools/internal/pkgbits/reloc.go | 42 + .../x/tools/internal/pkgbits/support.go | 17 + .../x/tools/internal/pkgbits/sync.go | 113 + .../internal/pkgbits/syncmarker_string.go | 89 + .../x/tools/internal/typeparams/common.go | 179 + .../x/tools/internal/typeparams/coretype.go | 122 + .../internal/typeparams/enabled_go117.go | 12 + .../internal/typeparams/enabled_go118.go | 15 + .../x/tools/internal/typeparams/normalize.go | 218 + .../x/tools/internal/typeparams/termlist.go | 163 + .../internal/typeparams/typeparams_go117.go | 197 + .../internal/typeparams/typeparams_go118.go | 151 + .../x/tools/internal/typeparams/typeterm.go | 170 + .../tools/internal/typesinternal/errorcode.go | 1560 ++++++++ .../typesinternal/errorcode_string.go | 179 + .../x/tools/internal/typesinternal/types.go | 52 + .../tools/internal/typesinternal/types_118.go | 19 + .../googleapis/rpc/status/status.pb.go | 10 +- .../grpc/attributes/attributes.go | 2 +- vendor/google.golang.org/grpc/backoff.go | 2 +- .../grpc/balancer/balancer.go | 70 +- .../grpc/balancer/base/balancer.go | 4 +- .../grpc/balancer/conn_state_evaluator.go | 74 + .../grpc/balancer/roundrobin/roundrobin.go | 16 +- .../grpc/balancer_conn_wrappers.go | 71 +- .../grpc_binarylog_v1/binarylog.pb.go | 13 +- .../grpc/channelz/channelz.go | 2 +- vendor/google.golang.org/grpc/clientconn.go | 138 +- .../grpc/credentials/credentials.go | 20 +- .../google.golang.org/grpc/credentials/tls.go | 2 +- vendor/google.golang.org/grpc/dialoptions.go | 40 +- .../grpc/encoding/encoding.go | 5 +- .../grpc/grpclog/loggerv2.go | 2 +- .../grpc/internal/binarylog/binarylog.go | 20 +- .../grpc/internal/binarylog/env_config.go | 20 +- .../grpc/internal/binarylog/method_logger.go | 19 +- .../grpc/internal/channelz/types.go | 16 +- .../grpc/internal/envconfig/envconfig.go | 8 +- .../grpc/internal/envconfig/observability.go | 36 + .../grpc/internal/envconfig/xds.go | 6 +- .../grpc/internal/grpclog/grpclog.go | 2 +- .../grpc/internal/grpcrand/grpcrand.go | 7 + .../grpc/internal/grpcsync/oncefunc.go | 32 + .../grpc/internal/grpcutil/compressor.go | 47 + .../grpc/internal/grpcutil/method.go | 6 +- .../grpc/internal/internal.go | 42 +- .../grpc/internal/resolver/unix/unix.go | 5 +- .../internal/serviceconfig/serviceconfig.go | 8 +- .../grpc/internal/status/status.go | 10 + .../grpc/internal/transport/controlbuf.go | 4 +- .../grpc/internal/transport/handler_server.go | 8 +- .../grpc/internal/transport/http2_client.go | 224 +- .../grpc/internal/transport/http2_server.go | 40 +- .../grpc/internal/transport/http_util.go | 29 +- .../grpc/internal/transport/transport.go | 14 +- .../grpc/metadata/metadata.go | 73 +- .../google.golang.org/grpc/picker_wrapper.go | 7 +- vendor/google.golang.org/grpc/preloader.go | 2 +- .../grpc/resolver/resolver.go | 14 +- vendor/google.golang.org/grpc/rpc_util.go | 39 +- vendor/google.golang.org/grpc/server.go | 199 +- .../google.golang.org/grpc/service_config.go | 7 +- .../grpc/serviceconfig/serviceconfig.go | 2 +- .../google.golang.org/grpc/status/status.go | 12 +- vendor/google.golang.org/grpc/stream.go | 163 +- vendor/google.golang.org/grpc/tap/tap.go | 2 +- vendor/google.golang.org/grpc/version.go | 2 +- vendor/google.golang.org/grpc/vet.sh | 7 +- vendor/modules.txt | 212 +- 1311 files changed, 126530 insertions(+), 16820 deletions(-) create mode 100644 vendor/github.com/Microsoft/go-winio/.gitattributes create mode 100644 vendor/github.com/Microsoft/go-winio/.golangci.yml create mode 100644 vendor/github.com/Microsoft/go-winio/SECURITY.md rename vendor/github.com/Microsoft/go-winio/backuptar/{noop.go => doc.go} (81%) create mode 100644 vendor/github.com/Microsoft/go-winio/doc.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/socket.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go create mode 100644 vendor/github.com/Microsoft/go-winio/tools.go create mode 100644 vendor/github.com/asaskevich/govalidator/.gitignore create mode 100644 vendor/github.com/asaskevich/govalidator/.travis.yml create mode 100644 vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md create mode 100644 vendor/github.com/asaskevich/govalidator/LICENSE create mode 100644 vendor/github.com/asaskevich/govalidator/README.md create mode 100644 vendor/github.com/asaskevich/govalidator/arrays.go create mode 100644 vendor/github.com/asaskevich/govalidator/converter.go create mode 100644 vendor/github.com/asaskevich/govalidator/doc.go create mode 100644 vendor/github.com/asaskevich/govalidator/error.go create mode 100644 vendor/github.com/asaskevich/govalidator/numerics.go create mode 100644 vendor/github.com/asaskevich/govalidator/patterns.go create mode 100644 vendor/github.com/asaskevich/govalidator/types.go create mode 100644 vendor/github.com/asaskevich/govalidator/utils.go create mode 100644 vendor/github.com/asaskevich/govalidator/validator.go create mode 100644 vendor/github.com/asaskevich/govalidator/wercker.yml delete mode 100644 vendor/github.com/blang/semver/.travis.yml delete mode 100644 vendor/github.com/blang/semver/README.md delete mode 100644 vendor/github.com/blang/semver/json.go delete mode 100644 vendor/github.com/blang/semver/package.json delete mode 100644 vendor/github.com/blang/semver/range.go delete mode 100644 vendor/github.com/blang/semver/semver.go delete mode 100644 vendor/github.com/blang/semver/sort.go delete mode 100644 vendor/github.com/blang/semver/sql.go delete mode 100644 vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore delete mode 100644 vendor/github.com/checkpoint-restore/go-criu/v5/Makefile delete mode 100644 vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go create mode 100644 vendor/github.com/checkpoint-restore/go-criu/v6/.gitignore rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/.golangci.yml (51%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/LICENSE (100%) create mode 100644 vendor/github.com/checkpoint-restore/go-criu/v6/Makefile rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/README.md (82%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/features.go (96%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/main.go (99%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/notify.go (100%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/rpc/rpc.pb.go (72%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/rpc/rpc.proto (96%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/stats/stats.pb.go (99%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/stats/stats.proto (100%) rename vendor/github.com/checkpoint-restore/go-criu/{v5/magic => v6/stats}/types.go (73%) rename vendor/github.com/checkpoint-restore/go-criu/{v5 => v6}/stats/utils.go (65%) create mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go create mode 100644 vendor/github.com/containers/buildah/define/mount_unsupported.go create mode 100644 vendor/github.com/containers/image/v5/docker/body_reader.go create mode 100644 vendor/github.com/containers/image/v5/internal/signer/signer.go create mode 100644 vendor/github.com/containers/image/v5/internal/useragent/useragent.go create mode 100644 vendor/github.com/containers/image/v5/signature/fulcio_cert.go create mode 100644 vendor/github.com/containers/image/v5/signature/internal/rekor_set.go create mode 100644 vendor/github.com/containers/image/v5/signature/policy_config_sigstore.go create mode 100644 vendor/github.com/containers/image/v5/signature/signer/signer.go create mode 100644 vendor/github.com/containers/image/v5/signature/sigstore/generate.go create mode 100644 vendor/github.com/containers/image/v5/signature/sigstore/internal/signer.go delete mode 100644 vendor/github.com/containers/image/v5/signature/sigstore/sign.go create mode 100644 vendor/github.com/containers/image/v5/signature/sigstore/signer.go create mode 100644 vendor/github.com/containers/image/v5/signature/simplesigning/signer.go create mode 100644 vendor/github.com/containers/ocicrypt/.gitignore create mode 100644 vendor/github.com/containers/ocicrypt/.golangci.yml delete mode 100644 vendor/github.com/containers/ocicrypt/.travis.yml delete mode 100644 vendor/github.com/containers/podman/v4/cmd/podman/parse/json.go delete mode 100644 vendor/github.com/containers/podman/v4/libpod/boltdb_state_freebsd.go delete mode 100644 vendor/github.com/containers/podman/v4/libpod/boltdb_state_linux.go create mode 100644 vendor/github.com/containers/podman/v4/libpod/healthcheck_nosystemd_linux.go create mode 100644 vendor/github.com/containers/podman/v4/libpod/networking_pasta_linux.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_apply_options.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_down_options.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/bindings/network/types_extracreate_options.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/bindings/network/types_update_options.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/entities/apply.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/apply.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_freebsd.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_linux.go delete mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/generate.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/kube.go delete mode 100644 vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/play.go create mode 100644 vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.c create mode 100644 vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.go delete mode 100644 vendor/github.com/containers/podman/v4/utils/utils_freebsd.go create mode 100644 vendor/github.com/containers/storage/deprecated.go create mode 100644 vendor/github.com/containers/storage/pkg/archive/archive_bsd.go delete mode 100644 vendor/github.com/containers/storage/pkg/archive/archive_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/archive/fflags_bsd.go create mode 100644 vendor/github.com/containers/storage/pkg/archive/fflags_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/chunked/compression_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/parsers/kernel/uname_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported_type.go create mode 100644 vendor/github.com/containers/storage/pkg/regexp/regexp.go create mode 100644 vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go create mode 100644 vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lchflags_bsd.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_common.go create mode 100644 vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen_example.go create mode 100644 vendor/github.com/cyberphone/json-canonicalization/LICENSE create mode 100644 vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/es6numfmt.go create mode 100644 vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/jsoncanonicalizer.go create mode 100644 vendor/github.com/go-openapi/analysis/.codecov.yml create mode 100644 vendor/github.com/go-openapi/analysis/.gitattributes create mode 100644 vendor/github.com/go-openapi/analysis/.gitignore create mode 100644 vendor/github.com/go-openapi/analysis/.golangci.yml create mode 100644 vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/analysis/LICENSE create mode 100644 vendor/github.com/go-openapi/analysis/README.md create mode 100644 vendor/github.com/go-openapi/analysis/analyzer.go create mode 100644 vendor/github.com/go-openapi/analysis/appveyor.yml create mode 100644 vendor/github.com/go-openapi/analysis/debug.go create mode 100644 vendor/github.com/go-openapi/analysis/doc.go create mode 100644 vendor/github.com/go-openapi/analysis/fixer.go create mode 100644 vendor/github.com/go-openapi/analysis/flatten.go create mode 100644 vendor/github.com/go-openapi/analysis/flatten_name.go create mode 100644 vendor/github.com/go-openapi/analysis/flatten_options.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/debug/debug.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go create mode 100644 vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go create mode 100644 vendor/github.com/go-openapi/analysis/mixin.go create mode 100644 vendor/github.com/go-openapi/analysis/schema.go create mode 100644 vendor/github.com/go-openapi/errors/.gitattributes create mode 100644 vendor/github.com/go-openapi/errors/.gitignore create mode 100644 vendor/github.com/go-openapi/errors/.golangci.yml create mode 100644 vendor/github.com/go-openapi/errors/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/errors/LICENSE create mode 100644 vendor/github.com/go-openapi/errors/README.md create mode 100644 vendor/github.com/go-openapi/errors/api.go create mode 100644 vendor/github.com/go-openapi/errors/auth.go create mode 100644 vendor/github.com/go-openapi/errors/doc.go create mode 100644 vendor/github.com/go-openapi/errors/headers.go create mode 100644 vendor/github.com/go-openapi/errors/middleware.go create mode 100644 vendor/github.com/go-openapi/errors/parsing.go create mode 100644 vendor/github.com/go-openapi/errors/schema.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/.editorconfig create mode 100644 vendor/github.com/go-openapi/jsonpointer/.gitignore create mode 100644 vendor/github.com/go-openapi/jsonpointer/.travis.yml create mode 100644 vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/jsonpointer/LICENSE create mode 100644 vendor/github.com/go-openapi/jsonpointer/README.md create mode 100644 vendor/github.com/go-openapi/jsonpointer/pointer.go create mode 100644 vendor/github.com/go-openapi/jsonreference/.gitignore create mode 100644 vendor/github.com/go-openapi/jsonreference/.golangci.yml create mode 100644 vendor/github.com/go-openapi/jsonreference/.travis.yml create mode 100644 vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/jsonreference/LICENSE create mode 100644 vendor/github.com/go-openapi/jsonreference/README.md create mode 100644 vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go create mode 100644 vendor/github.com/go-openapi/jsonreference/reference.go create mode 100644 vendor/github.com/go-openapi/loads/.editorconfig create mode 100644 vendor/github.com/go-openapi/loads/.gitignore create mode 100644 vendor/github.com/go-openapi/loads/.golangci.yml create mode 100644 vendor/github.com/go-openapi/loads/.travis.yml create mode 100644 vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/loads/LICENSE create mode 100644 vendor/github.com/go-openapi/loads/README.md create mode 100644 vendor/github.com/go-openapi/loads/doc.go create mode 100644 vendor/github.com/go-openapi/loads/loaders.go create mode 100644 vendor/github.com/go-openapi/loads/options.go create mode 100644 vendor/github.com/go-openapi/loads/spec.go create mode 100644 vendor/github.com/go-openapi/runtime/.editorconfig create mode 100644 vendor/github.com/go-openapi/runtime/.gitattributes create mode 100644 vendor/github.com/go-openapi/runtime/.gitignore create mode 100644 vendor/github.com/go-openapi/runtime/.golangci.yml create mode 100644 vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/runtime/LICENSE create mode 100644 vendor/github.com/go-openapi/runtime/README.md create mode 100644 vendor/github.com/go-openapi/runtime/bytestream.go create mode 100644 vendor/github.com/go-openapi/runtime/client_auth_info.go create mode 100644 vendor/github.com/go-openapi/runtime/client_operation.go create mode 100644 vendor/github.com/go-openapi/runtime/client_request.go create mode 100644 vendor/github.com/go-openapi/runtime/client_response.go create mode 100644 vendor/github.com/go-openapi/runtime/constants.go create mode 100644 vendor/github.com/go-openapi/runtime/csv.go create mode 100644 vendor/github.com/go-openapi/runtime/discard.go create mode 100644 vendor/github.com/go-openapi/runtime/file.go create mode 100644 vendor/github.com/go-openapi/runtime/headers.go create mode 100644 vendor/github.com/go-openapi/runtime/interfaces.go create mode 100644 vendor/github.com/go-openapi/runtime/json.go create mode 100644 vendor/github.com/go-openapi/runtime/request.go create mode 100644 vendor/github.com/go-openapi/runtime/statuses.go create mode 100644 vendor/github.com/go-openapi/runtime/text.go create mode 100644 vendor/github.com/go-openapi/runtime/values.go create mode 100644 vendor/github.com/go-openapi/runtime/xml.go create mode 100644 vendor/github.com/go-openapi/spec/.editorconfig create mode 100644 vendor/github.com/go-openapi/spec/.gitignore create mode 100644 vendor/github.com/go-openapi/spec/.golangci.yml create mode 100644 vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/spec/LICENSE create mode 100644 vendor/github.com/go-openapi/spec/README.md create mode 100644 vendor/github.com/go-openapi/spec/appveyor.yml create mode 100644 vendor/github.com/go-openapi/spec/bindata.go create mode 100644 vendor/github.com/go-openapi/spec/cache.go create mode 100644 vendor/github.com/go-openapi/spec/contact_info.go create mode 100644 vendor/github.com/go-openapi/spec/debug.go create mode 100644 vendor/github.com/go-openapi/spec/errors.go create mode 100644 vendor/github.com/go-openapi/spec/expander.go create mode 100644 vendor/github.com/go-openapi/spec/external_docs.go create mode 100644 vendor/github.com/go-openapi/spec/header.go create mode 100644 vendor/github.com/go-openapi/spec/info.go create mode 100644 vendor/github.com/go-openapi/spec/items.go create mode 100644 vendor/github.com/go-openapi/spec/license.go create mode 100644 vendor/github.com/go-openapi/spec/normalizer.go create mode 100644 vendor/github.com/go-openapi/spec/normalizer_nonwindows.go create mode 100644 vendor/github.com/go-openapi/spec/normalizer_windows.go create mode 100644 vendor/github.com/go-openapi/spec/operation.go create mode 100644 vendor/github.com/go-openapi/spec/parameter.go create mode 100644 vendor/github.com/go-openapi/spec/path_item.go create mode 100644 vendor/github.com/go-openapi/spec/paths.go create mode 100644 vendor/github.com/go-openapi/spec/properties.go create mode 100644 vendor/github.com/go-openapi/spec/ref.go create mode 100644 vendor/github.com/go-openapi/spec/resolver.go create mode 100644 vendor/github.com/go-openapi/spec/response.go create mode 100644 vendor/github.com/go-openapi/spec/responses.go create mode 100644 vendor/github.com/go-openapi/spec/schema.go create mode 100644 vendor/github.com/go-openapi/spec/schema_loader.go create mode 100644 vendor/github.com/go-openapi/spec/security_scheme.go create mode 100644 vendor/github.com/go-openapi/spec/spec.go create mode 100644 vendor/github.com/go-openapi/spec/swagger.go create mode 100644 vendor/github.com/go-openapi/spec/tag.go create mode 100644 vendor/github.com/go-openapi/spec/url_go18.go create mode 100644 vendor/github.com/go-openapi/spec/url_go19.go create mode 100644 vendor/github.com/go-openapi/spec/validations.go create mode 100644 vendor/github.com/go-openapi/spec/xml_object.go create mode 100644 vendor/github.com/go-openapi/strfmt/.editorconfig create mode 100644 vendor/github.com/go-openapi/strfmt/.gitattributes create mode 100644 vendor/github.com/go-openapi/strfmt/.gitignore create mode 100644 vendor/github.com/go-openapi/strfmt/.golangci.yml create mode 100644 vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/strfmt/LICENSE create mode 100644 vendor/github.com/go-openapi/strfmt/README.md create mode 100644 vendor/github.com/go-openapi/strfmt/bson.go create mode 100644 vendor/github.com/go-openapi/strfmt/date.go create mode 100644 vendor/github.com/go-openapi/strfmt/default.go create mode 100644 vendor/github.com/go-openapi/strfmt/doc.go create mode 100644 vendor/github.com/go-openapi/strfmt/duration.go create mode 100644 vendor/github.com/go-openapi/strfmt/format.go create mode 100644 vendor/github.com/go-openapi/strfmt/time.go create mode 100644 vendor/github.com/go-openapi/strfmt/ulid.go create mode 100644 vendor/github.com/go-openapi/swag/.editorconfig create mode 100644 vendor/github.com/go-openapi/swag/.gitattributes create mode 100644 vendor/github.com/go-openapi/swag/.gitignore create mode 100644 vendor/github.com/go-openapi/swag/.golangci.yml create mode 100644 vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/swag/LICENSE create mode 100644 vendor/github.com/go-openapi/swag/README.md create mode 100644 vendor/github.com/go-openapi/swag/convert.go create mode 100644 vendor/github.com/go-openapi/swag/convert_types.go create mode 100644 vendor/github.com/go-openapi/swag/doc.go create mode 100644 vendor/github.com/go-openapi/swag/file.go create mode 100644 vendor/github.com/go-openapi/swag/json.go create mode 100644 vendor/github.com/go-openapi/swag/loading.go create mode 100644 vendor/github.com/go-openapi/swag/name_lexem.go create mode 100644 vendor/github.com/go-openapi/swag/net.go create mode 100644 vendor/github.com/go-openapi/swag/path.go create mode 100644 vendor/github.com/go-openapi/swag/post_go18.go create mode 100644 vendor/github.com/go-openapi/swag/post_go19.go create mode 100644 vendor/github.com/go-openapi/swag/pre_go18.go create mode 100644 vendor/github.com/go-openapi/swag/pre_go19.go create mode 100644 vendor/github.com/go-openapi/swag/split.go create mode 100644 vendor/github.com/go-openapi/swag/util.go create mode 100644 vendor/github.com/go-openapi/swag/yaml.go create mode 100644 vendor/github.com/go-openapi/validate/.editorconfig create mode 100644 vendor/github.com/go-openapi/validate/.gitattributes create mode 100644 vendor/github.com/go-openapi/validate/.gitignore create mode 100644 vendor/github.com/go-openapi/validate/.golangci.yml create mode 100644 vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/validate/LICENSE create mode 100644 vendor/github.com/go-openapi/validate/README.md create mode 100644 vendor/github.com/go-openapi/validate/appveyor.yml create mode 100644 vendor/github.com/go-openapi/validate/context.go create mode 100644 vendor/github.com/go-openapi/validate/debug.go create mode 100644 vendor/github.com/go-openapi/validate/default_validator.go create mode 100644 vendor/github.com/go-openapi/validate/doc.go create mode 100644 vendor/github.com/go-openapi/validate/example_validator.go create mode 100644 vendor/github.com/go-openapi/validate/formats.go create mode 100644 vendor/github.com/go-openapi/validate/helpers.go create mode 100644 vendor/github.com/go-openapi/validate/object_validator.go create mode 100644 vendor/github.com/go-openapi/validate/options.go create mode 100644 vendor/github.com/go-openapi/validate/result.go create mode 100644 vendor/github.com/go-openapi/validate/rexp.go create mode 100644 vendor/github.com/go-openapi/validate/schema.go create mode 100644 vendor/github.com/go-openapi/validate/schema_messages.go create mode 100644 vendor/github.com/go-openapi/validate/schema_option.go create mode 100644 vendor/github.com/go-openapi/validate/schema_props.go create mode 100644 vendor/github.com/go-openapi/validate/slice_validator.go create mode 100644 vendor/github.com/go-openapi/validate/spec.go create mode 100644 vendor/github.com/go-openapi/validate/spec_messages.go create mode 100644 vendor/github.com/go-openapi/validate/type.go create mode 100644 vendor/github.com/go-openapi/validate/update-fixtures.sh create mode 100644 vendor/github.com/go-openapi/validate/validator.go create mode 100644 vendor/github.com/go-openapi/validate/values.go create mode 100644 vendor/github.com/godbus/dbus/v5/.golangci.yml create mode 100644 vendor/github.com/josharian/intern/README.md create mode 100644 vendor/github.com/josharian/intern/intern.go create mode 100644 vendor/github.com/josharian/intern/license.md create mode 100644 vendor/github.com/mailru/easyjson/LICENSE create mode 100644 vendor/github.com/mailru/easyjson/buffer/pool.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/error.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/lexer.go create mode 100644 vendor/github.com/mailru/easyjson/jwriter/writer.go delete mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml delete mode 100644 vendor/github.com/mattn/go-runewidth/go.test.sh create mode 100644 vendor/github.com/mitchellh/mapstructure/CHANGELOG.md rename vendor/github.com/{blang/semver => mitchellh/mapstructure}/LICENSE (92%) create mode 100644 vendor/github.com/mitchellh/mapstructure/README.md create mode 100644 vendor/github.com/mitchellh/mapstructure/decode_hooks.go create mode 100644 vendor/github.com/mitchellh/mapstructure/error.go create mode 100644 vendor/github.com/mitchellh/mapstructure/mapstructure.go create mode 100644 vendor/github.com/oklog/ulid/.gitignore create mode 100644 vendor/github.com/oklog/ulid/.travis.yml create mode 100644 vendor/github.com/oklog/ulid/AUTHORS.md create mode 100644 vendor/github.com/oklog/ulid/CHANGELOG.md create mode 100644 vendor/github.com/oklog/ulid/CONTRIBUTING.md create mode 100644 vendor/github.com/oklog/ulid/Gopkg.lock create mode 100644 vendor/github.com/oklog/ulid/Gopkg.toml create mode 100644 vendor/github.com/oklog/ulid/LICENSE create mode 100644 vendor/github.com/oklog/ulid/README.md create mode 100644 vendor/github.com/oklog/ulid/ulid.go create mode 100644 vendor/github.com/rivo/uniseg/eastasianwidth.go create mode 100644 vendor/github.com/rivo/uniseg/emojipresentation.go create mode 100644 vendor/github.com/rivo/uniseg/gen_breaktest.go create mode 100644 vendor/github.com/rivo/uniseg/gen_properties.go create mode 100644 vendor/github.com/rivo/uniseg/graphemeproperties.go create mode 100644 vendor/github.com/rivo/uniseg/graphemerules.go create mode 100644 vendor/github.com/rivo/uniseg/line.go create mode 100644 vendor/github.com/rivo/uniseg/lineproperties.go create mode 100644 vendor/github.com/rivo/uniseg/linerules.go create mode 100644 vendor/github.com/rivo/uniseg/sentence.go create mode 100644 vendor/github.com/rivo/uniseg/sentenceproperties.go create mode 100644 vendor/github.com/rivo/uniseg/sentencerules.go create mode 100644 vendor/github.com/rivo/uniseg/step.go create mode 100644 vendor/github.com/rivo/uniseg/width.go create mode 100644 vendor/github.com/rivo/uniseg/word.go create mode 100644 vendor/github.com/rivo/uniseg/wordproperties.go create mode 100644 vendor/github.com/rivo/uniseg/wordrules.go create mode 100644 vendor/github.com/sigstore/fulcio/COPYRIGHT.txt create mode 100644 vendor/github.com/sigstore/fulcio/LICENSE create mode 100644 vendor/github.com/sigstore/fulcio/pkg/certificate/doc.go create mode 100644 vendor/github.com/sigstore/fulcio/pkg/certificate/extensions.go create mode 100644 vendor/github.com/sigstore/rekor/CONTRIBUTORS.md create mode 100644 vendor/github.com/sigstore/rekor/COPYRIGHT.txt create mode 100644 vendor/github.com/sigstore/rekor/LICENSE create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/alpine.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/alpine_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/alpine_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/consistency_proof.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/cose.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/cose_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/cose_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/error.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/hashedrekord.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/hashedrekord_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/hashedrekord_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/helm.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/helm_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/helm_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/inactive_shard_log_info.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/inclusion_proof.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/intoto.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/intoto_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/intoto_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/intoto_v002_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/jar.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/jar_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/jar_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/log_entry.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/log_info.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/proposed_entry.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rekord.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rekord_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rekord_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rfc3161.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rfc3161_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rfc3161_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rpm.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rpm_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/rpm_v001_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/search_index.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/search_log_query.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/tuf.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/tuf_schema.go create mode 100644 vendor/github.com/sigstore/rekor/pkg/generated/models/tuf_v001_schema.go create mode 100644 vendor/github.com/sigstore/sigstore/pkg/cryptoutils/sans.go rename vendor/github.com/vishvananda/netlink/{proc_event.go => proc_event_linux.go} (100%) create mode 100644 vendor/go.mongodb.org/mongo-driver/LICENSE create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bson.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/array_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/byte_slice_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/cond_addr_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/empty_interface_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/mode.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/pointer_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/proxy.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/slice_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/string_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_tag_parser.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/time_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/uint_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/byte_slice_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/empty_interface_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/map_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/slice_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/string_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/struct_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/time_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/uint_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/copier.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_tables.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/json_scanner.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/mode.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/decoder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/encoder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/marshal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw_element.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw_value.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/registry.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/types.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/array.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_arraybuilder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document_sequence.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/element.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/tables.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go create mode 100644 vendor/golang.org/x/mod/LICENSE create mode 100644 vendor/golang.org/x/mod/PATENTS create mode 100644 vendor/golang.org/x/mod/semver/semver.go create mode 100644 vendor/golang.org/x/net/internal/socks/client.go create mode 100644 vendor/golang.org/x/net/internal/socks/socks.go create mode 100644 vendor/golang.org/x/net/proxy/dial.go create mode 100644 vendor/golang.org/x/net/proxy/direct.go create mode 100644 vendor/golang.org/x/net/proxy/per_host.go create mode 100644 vendor/golang.org/x/net/proxy/proxy.go create mode 100644 vendor/golang.org/x/net/proxy/socks5.go create mode 100644 vendor/golang.org/x/sys/cpu/parse.go create mode 100644 vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_hurd.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_hurd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s create mode 100644 vendor/golang.org/x/tools/LICENSE create mode 100644 vendor/golang.org/x/tools/PATENTS create mode 100644 vendor/golang.org/x/tools/cmd/stringer/stringer.go create mode 100644 vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go create mode 100644 vendor/golang.org/x/tools/go/gcexportdata/importer.go create mode 100644 vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go create mode 100644 vendor/golang.org/x/tools/go/packages/doc.go create mode 100644 vendor/golang.org/x/tools/go/packages/external.go create mode 100644 vendor/golang.org/x/tools/go/packages/golist.go create mode 100644 vendor/golang.org/x/tools/go/packages/golist_overlay.go create mode 100644 vendor/golang.org/x/tools/go/packages/loadmode_string.go create mode 100644 vendor/golang.org/x/tools/go/packages/packages.go create mode 100644 vendor/golang.org/x/tools/go/packages/visit.go create mode 100644 vendor/golang.org/x/tools/internal/event/core/event.go create mode 100644 vendor/golang.org/x/tools/internal/event/core/export.go create mode 100644 vendor/golang.org/x/tools/internal/event/core/fast.go create mode 100644 vendor/golang.org/x/tools/internal/event/doc.go create mode 100644 vendor/golang.org/x/tools/internal/event/event.go create mode 100644 vendor/golang.org/x/tools/internal/event/keys/keys.go create mode 100644 vendor/golang.org/x/tools/internal/event/keys/standard.go create mode 100644 vendor/golang.org/x/tools/internal/event/label/label.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/bexport.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/bimport.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/exportdata.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/iexport.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/iimport.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/newInterface10.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/newInterface11.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/support_go117.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/support_go118.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/unified_no.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/ureader_no.go create mode 100644 vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go create mode 100644 vendor/golang.org/x/tools/internal/gocommand/invoke.go create mode 100644 vendor/golang.org/x/tools/internal/gocommand/vendor.go create mode 100644 vendor/golang.org/x/tools/internal/gocommand/version.go create mode 100644 vendor/golang.org/x/tools/internal/packagesinternal/packages.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/codes.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/decoder.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/doc.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/encoder.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/flags.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/reloc.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/support.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/sync.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/common.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/coretype.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/normalize.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/termlist.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeterm.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/errorcode.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/errorcode_string.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/types.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/types_118.go create mode 100644 vendor/google.golang.org/grpc/balancer/conn_state_evaluator.go create mode 100644 vendor/google.golang.org/grpc/internal/envconfig/observability.go create mode 100644 vendor/google.golang.org/grpc/internal/grpcsync/oncefunc.go create mode 100644 vendor/google.golang.org/grpc/internal/grpcutil/compressor.go diff --git a/go.mod b/go.mod index 12af2612..0c2645b4 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/containers/prometheus-podman-exporter -go 1.17 +go 1.18 require ( - github.com/containers/common v0.50.1 - github.com/containers/image/v5 v5.23.1 - github.com/containers/podman/v4 v4.3.1 + github.com/containers/common v0.51.0 + github.com/containers/image/v5 v5.24.1 + github.com/containers/podman/v4 v4.4.2 github.com/go-kit/log v0.2.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 @@ -17,50 +17,61 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v1.2.0 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/Microsoft/hcsshim v0.9.4 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/Microsoft/hcsshim v0.9.6 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0 // indirect - github.com/checkpoint-restore/go-criu/v5 v5.3.0 // indirect + github.com/checkpoint-restore/go-criu/v6 v6.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/container-orchestrated-devices/container-device-interface v0.5.2 // indirect + github.com/container-orchestrated-devices/container-device-interface v0.5.3 // indirect github.com/containerd/cgroups v1.0.4 // indirect - github.com/containerd/containerd v1.6.8 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect + github.com/containerd/containerd v1.6.15 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect github.com/containernetworking/cni v1.1.2 // indirect - github.com/containernetworking/plugins v1.1.1 // indirect - github.com/containers/buildah v1.28.0 // indirect + github.com/containernetworking/plugins v1.2.0 // indirect + github.com/containers/buildah v1.29.0 // indirect github.com/containers/conmon v2.0.20+incompatible // indirect - github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a // indirect - github.com/containers/ocicrypt v1.1.6 // indirect - github.com/containers/psgo v1.7.3 // indirect - github.com/containers/storage v1.43.0 // indirect + github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect + github.com/containers/ocicrypt v1.1.7 // indirect + github.com/containers/psgo v1.8.0 // indirect + github.com/containers/storage v1.45.3 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect - github.com/coreos/go-systemd/v22 v22.4.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/disiqueira/gotree/v3 v3.0.2 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v20.10.18+incompatible // indirect + github.com/docker/docker v20.10.23+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fsouza/go-dockerclient v1.8.3 // indirect + github.com/fsouza/go-dockerclient v1.9.3 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/runtime v0.24.1 // indirect + github.com/go-openapi/spec v0.20.7 // indirect + github.com/go-openapi/strfmt v0.21.3 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.0 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20221029134443-4b691ce883d5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-containerregistry v0.11.0 // indirect + github.com/google/go-containerregistry v0.12.1 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -72,18 +83,21 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jinzhu/copier v0.3.5 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.11 // indirect + github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // 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-20221109233200-85aa52084eaf // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/sys/mount v0.3.3 // indirect github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect @@ -92,11 +106,12 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/nxadm/tail v1.4.8 // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/opencontainers/runc v1.1.4 // indirect - github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab // indirect - github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 // indirect + github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb // indirect + github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86 // indirect github.com/opencontainers/selinux v1.10.2 // indirect github.com/openshift/imagebuilder v1.2.4-0.20220711175835-4151e43600df // indirect github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect @@ -105,38 +120,42 @@ require ( github.com/proglottis/gpgme v0.1.3 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/seccomp/libseccomp-golang v0.10.0 // indirect - github.com/sigstore/sigstore v1.4.2 // indirect + github.com/sigstore/fulcio v1.0.0 // indirect + github.com/sigstore/rekor v1.0.1 // indirect + github.com/sigstore/sigstore v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect - github.com/sylabs/sif/v2 v2.8.0 // indirect + github.com/sylabs/sif/v2 v2.9.0 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/tchap/go-patricia v2.3.0+incompatible // indirect - github.com/theupdateframework/go-tuf v0.5.1 // indirect + github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect - github.com/ulikunitz/xz v0.5.10 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect github.com/vbatts/tar-split v0.11.2 // indirect github.com/vbauerster/mpb/v7 v7.5.3 // indirect - github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4 // indirect + github.com/vishvananda/netlink v1.2.1-beta.2 // indirect github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect + go.mongodb.org/mongo-driver v1.11.1 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect - go.opencensus.io v0.23.0 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.4.0 // indirect - golang.org/x/oauth2 v0.3.0 // indirect + go.opencensus.io v0.24.0 // indirect + golang.org/x/crypto v0.5.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.5.0 // indirect + golang.org/x/oauth2 v0.4.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/term v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect + golang.org/x/tools v0.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220720214146-176da50484ac // indirect - google.golang.org/grpc v1.48.0 // indirect + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect @@ -147,5 +166,3 @@ require ( ) replace github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.1-0.20220617142545-8b9452f75cbc - -replace github.com/onsi/ginkgo/v2 => github.com/onsi/ginkgo/v2 v2.4.0 diff --git a/go.sum b/go.sum index 845ecdf6..0a0247eb 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,4 @@ -4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -15,7 +12,6 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= @@ -23,105 +19,46 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.103.0/go.mod h1:vwLx1nqLrzLX/fpwSMOXmFIqBOyHsvHbnAdbGSJ+mKk= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= -cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg= -github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= -github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -132,8 +69,9 @@ github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JP github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -141,27 +79,21 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0= github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/Microsoft/hcsshim v0.9.4/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= +github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= -github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -170,50 +102,17 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= -github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.102/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.16.15/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= -github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= -github.com/aws/aws-sdk-go-v2/config v1.17.7/go.mod h1:dN2gja/QXxFF15hQreyrqYhLBaQo1d9ZKe/v/uplQoI= -github.com/aws/aws-sdk-go-v2/credentials v1.12.20/go.mod h1:UKY5HyIux08bbNA7Blv4PcXQ8cTkGh7ghHMFklaviR4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.22/go.mod h1:/vNv5Al0bpiF8YdX2Ov6Xy05VTiXsql94yUqJMYaj0w= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.16/go.mod h1:62dsXI0BqTIGomDl8Hpm33dv0OntGaVblri3ZRParVQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI= -github.com/aws/aws-sdk-go-v2/service/kms v1.18.10/go.mod h1:45pB2oUV71tilooilIi3dC1KVWWJHHhc7JnyqByuheo= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM= -github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af/go.mod h1:84CWnaDz4g1tEVnFLnuBigmGK15oPohy0RfvSN8d4eg= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -222,47 +121,29 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= -github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= -github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0 h1:txB5jvhzUCSiiQmqmMWpo5CEB7Gj/Hq5Xqi7eaPl8ko= github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0/go.mod h1:67kWC1PXQLR3lM/mmNnu3Kzn7K4TSWZAGUuQP1JSngk= github.com/checkpoint-restore/go-criu/v5 v5.2.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/checkpoint-restore/go-criu/v6 v6.3.0 h1:mIdrSO2cPNWQY1truPg6uHLXyKHk3Z5Odx4wjKOASzA= +github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -276,29 +157,15 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.9.0/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/container-orchestrated-devices/container-device-interface v0.5.2 h1:Bf/Zq8UBhbSBtB+pFBVIQ2Rh7sNK/x2ZEr6uW5YjNv8= -github.com/container-orchestrated-devices/container-device-interface v0.5.2/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY= +github.com/container-orchestrated-devices/container-device-interface v0.5.3 h1:4v6FMaa1Pn8SS0IBwgsvCsno8HRXoQvI87Uj1Zu7Tw4= +github.com/container-orchestrated-devices/container-device-interface v0.5.3/go.mod h1:SQohok453ewi9dItvUcO0MrP7K1CEQTxPDNd7OV+nxI= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -313,7 +180,6 @@ github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4S github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -337,12 +203,9 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= -github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= -github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= +github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= +github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -350,8 +213,7 @@ github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cE github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -360,9 +222,6 @@ github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1S github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUtk4cauMe34= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= @@ -372,15 +231,14 @@ github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= github.com/containerd/stargz-snapshotter/estargz v0.12.0/go.mod h1:AIQ59TewBFJ4GOPEQXujcrJ/EKxh5xXZegW1rkR1P/M= +github.com/containerd/stargz-snapshotter/estargz v0.13.0 h1:fD7AwuVV+B40p0d9qVkH/Au1qhp8hn/HWJHIYjpEcfw= +github.com/containerd/stargz-snapshotter/estargz v0.13.0/go.mod h1:m+9VaGJGlhCnrcEUod8mYumTmRgblwd3rC5UCEh2Yp0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -399,103 +257,80 @@ github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNR github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE= -github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= -github.com/containers/buildah v1.28.0 h1:63Kpf9nAEJGsDEOArb5Q0dn5S3B9wFQc9kST4nU7+Pw= -github.com/containers/buildah v1.28.0/go.mod h1:pTYSfpf+Ha/KbnMmwhhhEjkSF3NuhpxZfiDNDORLgqY= -github.com/containers/common v0.50.1 h1:AYRAf1xyahNVRez49KIkREInNf36SQx1lyLY9M95wQI= -github.com/containers/common v0.50.1/go.mod h1:XnWlXPyE9Ky+8v8MfYWJZFnejkprAkUeo0DTWmSiwcY= +github.com/containernetworking/plugins v1.2.0 h1:SWgg3dQG1yzUo4d9iD8cwSVh1VqI+bP7mkPDoSfP9VU= +github.com/containernetworking/plugins v1.2.0/go.mod h1:/VjX4uHecW5vVimFa1wkG4s+r/s9qIfPdqlLF4TW8c4= +github.com/containers/buildah v1.29.0 h1:rA3S2SXJffrJjvY2kyxOsAaIseDY6Ib77FsD7n88Mj4= +github.com/containers/buildah v1.29.0/go.mod h1:mah+CGmpOjkBJJ5rhOP0M2ETnODhiuhtnXusfh0hc6Q= +github.com/containers/common v0.51.0 h1:Ax4YHNTG8cEPHZJcMYRoP7sfBgOISceeyOvmZzmSucg= +github.com/containers/common v0.51.0/go.mod h1:3W2WIdalgQfrsX/T5tjX+6CxgT3ThJVN2G9sNuFjuCM= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= -github.com/containers/image/v5 v5.23.0/go.mod h1:EXFFGEsL99S6aqLqK2mQJ3yrNh6Q05UCHt4mhF9JNoM= -github.com/containers/image/v5 v5.23.1 h1:dUK9p5xfd38iTQXJEgMsUGqIHXbkDugrleZtd3zB+Wg= -github.com/containers/image/v5 v5.23.1/go.mod h1:EXFFGEsL99S6aqLqK2mQJ3yrNh6Q05UCHt4mhF9JNoM= -github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= -github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= +github.com/containers/image/v5 v5.24.1 h1:XaRw3FJmvZtI297uBVTJluUVH4AQJ//YpHviaOw0C4M= +github.com/containers/image/v5 v5.24.1/go.mod h1:oss5F6ssGQz8ZtC79oY+fuzYA3m3zBek9tq9gmhuvHc= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g= -github.com/containers/ocicrypt v1.1.5/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= -github.com/containers/ocicrypt v1.1.6 h1:uoG52u2e91RE4UqmBICZY8dNshgfvkdl3BW6jnxiFaI= -github.com/containers/ocicrypt v1.1.6/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= -github.com/containers/podman/v4 v4.3.1 h1:cMCaKMwXd/FGfylA/w6VBtPb3ImIY0Wg8qLYYovMIjE= -github.com/containers/podman/v4 v4.3.1/go.mod h1:RDbLSy1WeMSqw90DSxlQBAU5U6c0er9wFrXCOwYv8tQ= -github.com/containers/psgo v1.7.3 h1:KTNurTMXpZjDJHWmlieVO7k7jgKJ4CR/HpPeSaAKtgc= -github.com/containers/psgo v1.7.3/go.mod h1:PfaNzzHmMb8M9/blPgyD4BB3ZEj/0ApZIxN6nNtA+t4= +github.com/containers/ocicrypt v1.1.7 h1:thhNr4fu2ltyGz8aMx8u48Ae0Pnbip3ePP9/mzkZ/3U= +github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8NscCYRawuDNtw= +github.com/containers/podman/v4 v4.4.2 h1:OSlO6NZ3lQuHOhb+MhW0Rl8IU7W1SlGK/0jh+utLqYU= +github.com/containers/podman/v4 v4.4.2/go.mod h1:q7uhwIw4/69ExGUjNf1Bum3NHGT3yInyC3TJWvnFBeI= +github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY= +github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc= github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= -github.com/containers/storage v1.42.0/go.mod h1:JiUJwOgOo1dr2DdOUc1MRe2GCAXABYoYmOdPF8yvH78= -github.com/containers/storage v1.43.0 h1:P+zulGXA3mqe2GnYmZU0xu87Wy1M0PVHM2ucrgmvTdU= github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s= +github.com/containers/storage v1.45.3 h1:GbtTvTtp3GW2/tcFg5VhgHXcYMwVn2KfZKiHjf9FAOM= +github.com/containers/storage v1.45.3/go.mod h1:OdRUYHrq1HP6iAo79VxqtYuJzC5j4eA2I60jKOoCT7g= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU= -github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3/go.mod h1:RTjQyHgO/G37oJ3qnqYK6Z4TPZ5EsaabOtfMjVXmgko= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h1:vU+EP9ZuFUCYE0NYLwTSob+3LNEJATzNfP/DC7SWGWI= +github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= -github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q= -github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001/go.mod h1:IetBE52JfFxK46p2n2Rqm+p5Gx1gpu2hRHsrbnPOWZQ= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWhkNRq8= github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -503,11 +338,9 @@ github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= -github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.23+incompatible h1:1ZQUUYAdh+oylOT85aA2ZcfRp22jmLhoaEcVEfK8dyA= +github.com/docker/docker v20.10.23+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -523,13 +356,10 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eggsampler/acme/v3 v3.3.0/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -540,189 +370,123 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= -github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg= -github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01/go.mod h1:ypD5nozFk9vcGw1ATYefw6jHe/jZP++Z15/+VTMcWhc= github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8= -github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52/go.mod h1:yIquW87NGRw1FU5p5lEkpnt/QxoH5uPAOUlOVkAUuMg= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/go-dockerclient v1.7.7/go.mod h1:njNCXvoZj3sLPjf3yO0DPHf1mdLdCPDYPc14GskKA4Y= -github.com/fsouza/go-dockerclient v1.8.3 h1:ckb6sG98D607PNQo012YEhQ8hvuRHx7RISIJM5tjkQM= -github.com/fsouza/go-dockerclient v1.8.3/go.mod h1:oenNB8JjNKY4o8I/sug4Qah9si/7OxH4MjL+u7oBxP8= +github.com/fsouza/go-dockerclient v1.9.3 h1:nos0eFmulRVoq9QevZbwJzFS4cFHMoeUTDNC04klEVM= +github.com/fsouza/go-dockerclient v1.9.3/go.mod h1:soNpY8X1z9RW5UxuXU+gA94/ESSbiAoWwsiqYa6ofHQ= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= -github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo= +github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= +github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-rod/rod v0.109.3/go.mod h1:GZDtmEs6RpF6kBRYpGCZXxXlKNneKVPiKOjaMbmVVjE= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= +github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= -github.com/gobuffalo/attrs v0.1.0/go.mod h1:fmNpaWyHM0tRm8gCZWKx8yY9fvaNLo2PyzBNSrBZ5Hw= -github.com/gobuffalo/attrs v1.0.1/go.mod h1:qGdnq2RukKtBl4ASJit0OFckc5XGSyTFk98SvRpMFrQ= -github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= -github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= -github.com/gobuffalo/envy v1.10.1/go.mod h1:AWx4++KnNOW3JOeEvhSaq+mvgAvnMYOY1XSIin4Mago= -github.com/gobuffalo/fizz v1.10.0/go.mod h1:J2XGPO0AfJ1zKw7+2BA+6FEGAkyEsdCOLvN93WCT2WI= -github.com/gobuffalo/fizz v1.14.0/go.mod h1:0aF1kAZYCfKqbLM/lmZ3jXFyqqWE/kY/nIOKnNdAYXQ= -github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= -github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= -github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= -github.com/gobuffalo/genny/v2 v2.0.5/go.mod h1:kRkJuAw9mdI37AiEYjV4Dl+TgkBDYf8HZVjLkqe5eBg= -github.com/gobuffalo/genny/v2 v2.0.8/go.mod h1:R45scCyQfff2HysNJHNanjrpvPw4Qu+rM1MOMDBB5oU= -github.com/gobuffalo/genny/v2 v2.0.9/go.mod h1:R45scCyQfff2HysNJHNanjrpvPw4Qu+rM1MOMDBB5oU= -github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic= -github.com/gobuffalo/github_flavored_markdown v1.1.1/go.mod h1:yU32Pen+eorS58oxh/bNZx76zUOCJwmvyV5FBrvzOKQ= -github.com/gobuffalo/helpers v0.6.0/go.mod h1:pncVrer7x/KRvnL5aJABLAuT/RhKRR9klL6dkUOhyv8= -github.com/gobuffalo/helpers v0.6.1/go.mod h1:wInbDi0vTJKZBviURTLRMFLE4+nF2uRuuL2fnlYo7w4= -github.com/gobuffalo/helpers v0.6.4/go.mod h1:m2aOKsTl3KB0RUwwpxf3tykaaitujQ3irivqrlNAcJ0= -github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= -github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/nulls v0.2.0/go.mod h1:w4q8RoSCEt87Q0K0sRIZWYeIxkxog5mh3eN3C/n+dUc= -github.com/gobuffalo/nulls v0.4.1/go.mod h1:pp8e1hWTRJZFpMl4fj/CVbSMlaxjeGKkFq4RuBZi3w8= -github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= -github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= -github.com/gobuffalo/plush/v4 v4.0.0/go.mod h1:ErFS3UxKqEb8fpFJT7lYErfN/Nw6vHGiDMTjxpk5bQ0= -github.com/gobuffalo/plush/v4 v4.1.9/go.mod h1:9OOII9uAM5pZnhWu1OkQnboXJjaWMQ7kcTl3zNcxvTM= -github.com/gobuffalo/plush/v4 v4.1.11/go.mod h1:9OOII9uAM5pZnhWu1OkQnboXJjaWMQ7kcTl3zNcxvTM= -github.com/gobuffalo/pop/v5 v5.3.1/go.mod h1:vcEDhh6cJ3WVENqJDFt/6z7zNb7lLnlN8vj3n5G9rYA= -github.com/gobuffalo/pop/v6 v6.0.0/go.mod h1:5rd3OnViLhjteR8+0i/mT9Q4CzkTzCoR7tm/9mmAic4= -github.com/gobuffalo/pop/v6 v6.0.4/go.mod h1:dFcrMNPOwk+sl1Oa0lOb/jGbmjv+JV+5CZjMWNYR3KI= -github.com/gobuffalo/tags/v3 v3.0.2/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA= -github.com/gobuffalo/tags/v3 v3.1.0/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA= -github.com/gobuffalo/tags/v3 v3.1.2/go.mod h1:o3ldUfKv50jxWAC8eZHXMm8dnKW3YvyZUMr0xqUcZTI= -github.com/gobuffalo/validate/v3 v3.0.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0= -github.com/gobuffalo/validate/v3 v3.1.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0= -github.com/gobuffalo/validate/v3 v3.3.1/go.mod h1:Ehu8ieNJQuUM4peDDr/0VapzdGA7RgTc3wbe51vHfS0= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/godbus/dbus/v5 v5.1.1-0.20221029134443-4b691ce883d5 h1:boOtwyhKoC3Aadiw5zbhU54YyCkm9EpZCSN6mOx0KLc= +github.com/godbus/dbus/v5 v5.1.1-0.20221029134443-4b691ce883d5/go.mod h1:fXoNnqaUvdKqjJmMGeiBgmRphUg+kO0MT4AhPOP6+Qg= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -732,10 +496,6 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -753,7 +513,6 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -772,25 +531,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.43.0/go.mod h1:VIFlUqidx5ggxDfQagdvd9E67UjMXtTHBkBQ7sHoC5Q= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/certificate-transparency-go v1.0.22-0.20181127102053-c25855a82c75/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -803,13 +546,10 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= -github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= +github.com/google/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= +github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -819,55 +559,32 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= -github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= -github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -876,111 +593,45 @@ github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= -github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= -github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= -github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= -github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= -github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= -github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= -github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= -github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.1.0/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/honeycombio/beeline-go v1.1.1/go.mod h1:kN0cfUGBMfA87DyCYbiiLoSzWsnw3bluZvNEWtatHxk= -github.com/honeycombio/beeline-go v1.9.0 h1:MM/20HBFE2AKno0N9bOgEicSHHiTkTklGdtjdtAEWbk= -github.com/honeycombio/beeline-go v1.9.0/go.mod h1:/8gmL2gGFbAnIhfldNUj26AFi7+JTdjtTfIujJww6yI= -github.com/honeycombio/libhoney-go v1.15.2/go.mod h1:JzhRPYgoBCd0rZvudrqmej4Ntx0w7AT3wAJpf5+t1WA= -github.com/honeycombio/libhoney-go v1.15.8 h1:TECEltZ48K6J4NG1JVYqmi0vCJNnHYooFor83fgKesA= -github.com/honeycombio/libhoney-go v1.15.8/go.mod h1:+tnL2etFnJmVx30yqmoUkVyQjp7uRJw0a2QGu48lSyY= +github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc= +github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -991,102 +642,21 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.6.0/go.mod h1:yeseQo4xhQbgyJs2c87RAXOH2i624N0Fh1KSPJya7qo= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= -github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2Hy3c5Z4n14XmSvTI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= -github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= -github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= -github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= -github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= -github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= -github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= -github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= -github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= -github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -1094,30 +664,21 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 h1:BcxbplxjtczA1a6d3wYoa7a0WL3rq9DKBMGHeKyjEF0= github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -1134,164 +695,77 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= -github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= -github.com/labstack/echo/v4 v4.3.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8= -github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/letsencrypt/boulder v0.0.0-20220723181115-27de4befb95e h1:2ba+yBBeT8ZFyZjRLPDKvkqVrWX4CCYAuR6nuJGojD0= -github.com/letsencrypt/boulder v0.0.0-20220723181115-27de4befb95e/go.mod h1:54WQpg5QI0mpRhxoj9bxysLqA5WJylVsLtXOrb3zAiU= -github.com/letsencrypt/challtestsrv v1.2.1/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc= -github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf h1:ndns1qx/5dL43g16EQkPV/i8+b3l5bYQwLeoSBe7tS8= +github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf/go.mod h1:aGkAgvWY/IUcVFfuly53REpfv5edu25oij+qHRFaraA= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= -github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= -github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= -github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= -github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mistifyio/go-zfs/v3 v3.0.0 h1:J5QK618xRcXnQYZ2GE5FdmpS1ufIrWue+lR/mpe6/14= github.com/mistifyio/go-zfs/v3 v3.0.0/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs= github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.1/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/moby/vpnkit v0.5.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1299,75 +773,48 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= -github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= -github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= -github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= -github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1376,11 +823,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= -github.com/opencontainers/image-spec v1.1.0-rc1/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.1-0.20220617142545-8b9452f75cbc h1:qjkUzmFsOFbQyjObybk40mRida83j5IHRaKzLGdBbEU= @@ -1390,46 +833,31 @@ github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20201121164853-7413a7f753e1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab h1:YQZXa3elcHgKXAa2GjVFC9M3JeP7ZPyFD1YByDx/dgQ= -github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb h1:1xSVPOd7/UA+39/hXEGnBJ13p6JFB0E1EvQFlrRDOXI= +github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 h1:Rf+QsQGxrYCia8mVyOPnoQZ+vJkZGL+ESWBDUM5s9cQ= -github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7/go.mod h1:/tgP02fPXGHkU3/qKK1Y0Db4yqNyGm03vLq/mzHzcS4= +github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86 h1:AaK4/fBxOmEFtb1bs/7KrJsQIgVPnhIrtgJ92RaqM60= +github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.2 h1:NFy2xCsjn7+WspbfZkUd5zyVeisV7VFbPSP96+8/ha4= github.com/opencontainers/selinux v1.10.2/go.mod h1:cARutUbaUrlRClyvxOICCgKixCs6L05aUsohzA3EkHQ= github.com/openshift/imagebuilder v1.2.4-0.20220711175835-4151e43600df h1:vf6pdI10F2Tim5a9JKiVVl4/dpNz1OEhz4EnfLdLtiA= github.com/openshift/imagebuilder v1.2.4-0.20220711175835-4151e43600df/go.mod h1:TRYHe4CH9U6nkDjxjBNM5klrLbJBrRbpJE5SaRwUBsQ= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M= github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1438,13 +866,9 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/proglottis/gpgme v0.1.3 h1:Crxx0oz4LKB3QXc5Ea0J19K/3ICfy3ftr5exgUK1AU0= github.com/proglottis/gpgme v0.1.3/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0= @@ -1453,13 +877,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1474,12 +892,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.40.0 h1:Afz7EVRqGg2Mqqf4JuF9vdvp1pi220m55Pi9T2JnO4Q= github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE= github.com/prometheus/exporter-toolkit v0.8.2 h1:sbJAfBXQFkG6sUkbwBun8MNdzW9+wd5YfPYofbmj0YM= @@ -1495,77 +908,41 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= -github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= -github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= -github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rootless-containers/rootlesskit v1.0.1/go.mod h1:t2UAiYagxrJ+wmpFAUIZPcqsm4k2B7ve6g7lILKbloc= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= -github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= -github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= -github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= -github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= -github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= -github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sigstore/sigstore v1.4.2 h1:fTppzuZBAmQ/skgl7FWJRLyby70pxCqJGKyWfkSuMR8= -github.com/sigstore/sigstore v1.4.2/go.mod h1:wCv58Fia7u1snVJyPcxdgIh/3uw1XdOLhxPExTwwyt4= +github.com/sigstore/fulcio v1.0.0 h1:hBZW6qg9GXTtCX8jOg1hmyjYLrmsEKZGeMwAbW3XNEg= +github.com/sigstore/fulcio v1.0.0/go.mod h1:j4MzLxX/Be0rHYh3JF2dgMorkWGzEMHBqIHwFU8I/Rw= +github.com/sigstore/rekor v1.0.1 h1:rcESXSNkAPRWFYZel9rarspdvneET60F2ngNkadi89c= +github.com/sigstore/rekor v1.0.1/go.mod h1:ecTKdZWGWqE1pl3U1m1JebQJLU/hSjD9vYHOmHQ7w4g= +github.com/sigstore/sigstore v1.5.1 h1:iUou0QJW8eQKMUkTXbFyof9ZOblDtfaW2Sn2+QI8Tcs= +github.com/sigstore/sigstore v1.5.1/go.mod h1:3i6UTWVNtFwOtbgG63FZZNID4vO9KcO8AszIJlaNI8k= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -1573,35 +950,21 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -1612,23 +975,17 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= -github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1636,53 +993,31 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/sylabs/sif/v2 v2.8.0 h1:FIfWA1fYSFynKD1LJwGbWJ2ib8ylT8XwZl9naLlciPE= -github.com/sylabs/sif/v2 v2.8.0/go.mod h1:LQOdYXC9a8i7BleTKRw9lohi0rTbXkJOeS9u0ebvgyM= -github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= +github.com/sylabs/sif/v2 v2.9.0 h1:q9K92j1QW4/QLOtKh9YZpJHrXav6x15AVhQGPVLcg+4= +github.com/sylabs/sif/v2 v2.9.0/go.mod h1:bRdFzcqif0eDjwx0isG4cgTFoKTQn/vfBXVSoP2rB2Y= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= -github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/theupdateframework/go-tuf v0.5.1-0.20220920170306-f237d7ca5b42/go.mod h1:vAqWV3zEs89byeFsAYoh/Q14vJTgJkHwnnRCWBBBINY= -github.com/theupdateframework/go-tuf v0.5.1 h1:kd8in4+boNxtYf8AYPbm9//Ps75RG5yURkN9Fng4Qs4= -github.com/theupdateframework/go-tuf v0.5.1/go.mod h1:vAqWV3zEs89byeFsAYoh/Q14vJTgJkHwnnRCWBBBINY= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 h1:s+Yvt6bzRwHljSE7j6DLBDcfpZEdBhrvLgOUmd8f7ZM= +github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5/go.mod h1:Le8NAjvDJK1vmLgpVYr4AR1Tqam/b/mTdQyTy37UJDA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= -github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= -github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= -github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= -github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= -github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1690,41 +1025,29 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.5.1/go.mod h1:oDzoM7pVwz6wHn5ogWgFUU1s4VJayeQS+aEZDqXIEJs= -github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vbauerster/mpb/v7 v7.5.3 h1:BkGfmb6nMrrBQDFECR/Q7RkKCw7ylMetCb4079CGs4w= github.com/vbauerster/mpb/v7 v7.5.3/go.mod h1:i+h4QY6lmLvBNK2ah1fSreiw3ajskRlBp9AhY/PnuOE= -github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4 h1:fB26rIBlWTVJyEB6ONHdoEvUbvwoudH0/cMEXHiD1RU= -github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= +github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/weppos/publicsuffix-go v0.15.1-0.20210807195340-dc689ff0bb59/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= -github.com/weppos/publicsuffix-go v0.15.1-0.20220413065649-906f534b73a4/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1734,51 +1057,31 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= -github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= -github.com/ysmood/got v0.31.3/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= -github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= -github.com/ysmood/gson v0.7.1/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= -github.com/ysmood/gson v0.7.2/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= -github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= -github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= -github.com/zmap/zcrypto v0.0.0-20210811211718-6f9bc4aff20f/go.mod h1:y/9hjFEub4DtQxTHp/pqticBgdYeCwL97vojV3lsvHY= -github.com/zmap/zlint/v3 v3.3.1-0.20211019173530-cb17369b4628/go.mod h1:O+4OXRfNLKqOyDl4eKZ1SBlYudKGUBGRFcv+m1KLr28= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= +go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= @@ -1788,91 +1091,39 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/contrib/propagators v0.19.0/go.mod h1:4QOdZClXISU5S43xZxk5tYaWcpb+lehqfKtE6PK6msE= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -goji.io/v3 v3.0.0/go.mod h1:c02FFnNiVNCDo+DpR2IhBQpM9r5G1BG/MkHNTPUJ13U= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1883,7 +1134,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1909,10 +1159,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1925,7 +1173,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -1937,10 +1184,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1948,7 +1192,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1958,47 +1201,23 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2011,21 +1230,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2038,8 +1244,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2050,21 +1254,18 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2073,15 +1274,11 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2091,7 +1288,6 @@ golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2102,27 +1298,21 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2133,84 +1323,37 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2220,41 +1363,32 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -2262,28 +1396,17 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2292,14 +1415,8 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -2307,67 +1424,31 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -2386,40 +1467,16 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -2428,7 +1485,6 @@ google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dT google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -2443,7 +1499,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -2451,13 +1506,10 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2467,52 +1519,11 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220720214146-176da50484ac h1:EOa+Yrhx1C0O+4pHeXeWrCwdI0tWI6IfUU56Vebs9wQ= -google.golang.org/genproto v0.0.0-20220720214146-176da50484ac/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2525,7 +1536,6 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -2536,24 +1546,10 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2573,26 +1569,19 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= -gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -2602,19 +1591,18 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -2632,34 +1620,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -2667,30 +1647,18 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go index 09523315..0ca1dc4f 100644 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -21,7 +21,9 @@ type Unmarshaler interface { UnmarshalTOML(interface{}) error } -// Unmarshal decodes the contents of `data` in TOML format into a pointer `v`. +// Unmarshal decodes the contents of data in TOML format into a pointer v. +// +// See [Decoder] for a description of the decoding process. func Unmarshal(data []byte, v interface{}) error { _, err := NewDecoder(bytes.NewReader(data)).Decode(v) return err @@ -29,13 +31,12 @@ func Unmarshal(data []byte, v interface{}) error { // Decode the TOML data in to the pointer v. // -// See the documentation on Decoder for a description of the decoding process. +// See [Decoder] for a description of the decoding process. func Decode(data string, v interface{}) (MetaData, error) { return NewDecoder(strings.NewReader(data)).Decode(v) } -// DecodeFile is just like Decode, except it will automatically read the -// contents of the file at path and decode it for you. +// DecodeFile reads the contents of a file and decodes it with [Decode]. func DecodeFile(path string, v interface{}) (MetaData, error) { fp, err := os.Open(path) if err != nil { @@ -48,7 +49,7 @@ func DecodeFile(path string, v interface{}) (MetaData, error) { // Primitive is a TOML value that hasn't been decoded into a Go value. // // This type can be used for any value, which will cause decoding to be delayed. -// You can use the PrimitiveDecode() function to "manually" decode these values. +// You can use [PrimitiveDecode] to "manually" decode these values. // // NOTE: The underlying representation of a `Primitive` value is subject to // change. Do not rely on it. @@ -70,15 +71,15 @@ const ( // Decoder decodes TOML data. // -// TOML tables correspond to Go structs or maps (dealer's choice – they can be -// used interchangeably). +// TOML tables correspond to Go structs or maps; they can be used +// interchangeably, but structs offer better type safety. // // TOML table arrays correspond to either a slice of structs or a slice of maps. // -// TOML datetimes correspond to Go time.Time values. Local datetimes are parsed -// in the local timezone. +// TOML datetimes correspond to [time.Time]. Local datetimes are parsed in the +// local timezone. // -// time.Duration types are treated as nanoseconds if the TOML value is an +// [time.Duration] types are treated as nanoseconds if the TOML value is an // integer, or they're parsed with time.ParseDuration() if they're strings. // // All other TOML types (float, string, int, bool and array) correspond to the @@ -90,7 +91,7 @@ const ( // UnmarshalText method. See the Unmarshaler example for a demonstration with // email addresses. // -// Key mapping +// ### Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go struct. // The special `toml` struct tag can be used to map TOML keys to struct fields @@ -168,17 +169,16 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) { return md, md.unify(p.mapping, rv) } -// PrimitiveDecode is just like the other `Decode*` functions, except it -// decodes a TOML value that has already been parsed. Valid primitive values -// can *only* be obtained from values filled by the decoder functions, -// including this method. (i.e., `v` may contain more `Primitive` -// values.) +// PrimitiveDecode is just like the other Decode* functions, except it decodes a +// TOML value that has already been parsed. Valid primitive values can *only* be +// obtained from values filled by the decoder functions, including this method. +// (i.e., v may contain more [Primitive] values.) // -// Meta data for primitive values is included in the meta data returned by -// the `Decode*` functions with one exception: keys returned by the Undecoded -// method will only reflect keys that were decoded. Namely, any keys hidden -// behind a Primitive will be considered undecoded. Executing this method will -// update the undecoded keys in the meta data. (See the example.) +// Meta data for primitive values is included in the meta data returned by the +// Decode* functions with one exception: keys returned by the Undecoded method +// will only reflect keys that were decoded. Namely, any keys hidden behind a +// Primitive will be considered undecoded. Executing this method will update the +// undecoded keys in the meta data. (See the example.) func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { md.context = primValue.context defer func() { md.context = nil }() diff --git a/vendor/github.com/BurntSushi/toml/decode_go116.go b/vendor/github.com/BurntSushi/toml/decode_go116.go index eddfb641..086d0b68 100644 --- a/vendor/github.com/BurntSushi/toml/decode_go116.go +++ b/vendor/github.com/BurntSushi/toml/decode_go116.go @@ -7,8 +7,8 @@ import ( "io/fs" ) -// DecodeFS is just like Decode, except it will automatically read the contents -// of the file at `path` from a fs.FS instance. +// DecodeFS reads the contents of a file from [fs.FS] and decodes it with +// [Decode]. func DecodeFS(fsys fs.FS, path string, v interface{}) (MetaData, error) { fp, err := fsys.Open(path) if err != nil { diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go index 099c4a77..81a7c0fe 100644 --- a/vendor/github.com/BurntSushi/toml/doc.go +++ b/vendor/github.com/BurntSushi/toml/doc.go @@ -1,13 +1,11 @@ -/* -Package toml implements decoding and encoding of TOML files. - -This package supports TOML v1.0.0, as listed on https://toml.io - -There is also support for delaying decoding with the Primitive type, and -querying the set of keys in a TOML document with the MetaData type. - -The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator, -and can be used to verify if TOML document is valid. It can also be used to -print the type of each key. -*/ +// Package toml implements decoding and encoding of TOML files. +// +// This package supports TOML v1.0.0, as specified at https://toml.io +// +// There is also support for delaying decoding with the Primitive type, and +// querying the set of keys in a TOML document with the MetaData type. +// +// The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator, +// and can be used to verify if TOML document is valid. It can also be used to +// print the type of each key. package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go index dc8568d1..930e1d52 100644 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -79,12 +79,12 @@ type Marshaler interface { // Encoder encodes a Go to a TOML document. // // The mapping between Go values and TOML values should be precisely the same as -// for the Decode* functions. +// for [Decode]. // // time.Time is encoded as a RFC 3339 string, and time.Duration as its string // representation. // -// The toml.Marshaler and encoder.TextMarshaler interfaces are supported to +// The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to // encoding the value as custom TOML. // // If you want to write arbitrary binary data then you will need to use @@ -130,7 +130,7 @@ func NewEncoder(w io.Writer) *Encoder { } } -// Encode writes a TOML representation of the Go value to the Encoder's writer. +// Encode writes a TOML representation of the Go value to the [Encoder]'s writer. // // An error is returned if the value given cannot be encoded to a valid TOML // document. @@ -261,7 +261,7 @@ func (enc *Encoder) eElement(rv reflect.Value) { enc.eElement(reflect.ValueOf(v)) return } - encPanic(errors.New(fmt.Sprintf("Unable to convert \"%s\" to neither int64 nor float64", n))) + encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n)) } switch rv.Kind() { @@ -504,7 +504,8 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { if opts.name != "" { keyName = opts.name } - if opts.omitempty && isEmpty(fieldVal) { + + if opts.omitempty && enc.isEmpty(fieldVal) { continue } if opts.omitzero && isZero(fieldVal) { @@ -648,12 +649,26 @@ func isZero(rv reflect.Value) bool { return false } -func isEmpty(rv reflect.Value) bool { +func (enc *Encoder) isEmpty(rv reflect.Value) bool { switch rv.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return rv.Len() == 0 case reflect.Struct: - return reflect.Zero(rv.Type()).Interface() == rv.Interface() + if rv.Type().Comparable() { + return reflect.Zero(rv.Type()).Interface() == rv.Interface() + } + // Need to also check if all the fields are empty, otherwise something + // like this with uncomparable types will always return true: + // + // type a struct{ field b } + // type b struct{ s []string } + // s := a{field: b{s: []string{"AAA"}}} + for i := 0; i < rv.NumField(); i++ { + if !enc.isEmpty(rv.Field(i)) { + return false + } + } + return true case reflect.Bool: return !rv.Bool() } @@ -668,16 +683,15 @@ func (enc *Encoder) newline() { // Write a key/value pair: // -// key = +// key = // // This is also used for "k = v" in inline tables; so something like this will // be written in three calls: // -// ┌────────────────────┐ -// │ ┌───┐ ┌─────┐│ -// v v v v vv -// key = {k = v, k2 = v2} -// +// ┌───────────────────┐ +// │ ┌───┐ ┌────┐│ +// v v v v vv +// key = {k = 1, k2 = 2} func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { if len(key) == 0 { encPanic(errNoKey) diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go index 2ac24e77..f4f390e6 100644 --- a/vendor/github.com/BurntSushi/toml/error.go +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -5,57 +5,60 @@ import ( "strings" ) -// ParseError is returned when there is an error parsing the TOML syntax. -// -// For example invalid syntax, duplicate keys, etc. +// ParseError is returned when there is an error parsing the TOML syntax such as +// invalid syntax, duplicate keys, etc. // // In addition to the error message itself, you can also print detailed location -// information with context by using ErrorWithPosition(): +// information with context by using [ErrorWithPosition]: // -// toml: error: Key 'fruit' was already created and cannot be used as an array. +// toml: error: Key 'fruit' was already created and cannot be used as an array. // -// At line 4, column 2-7: +// At line 4, column 2-7: // -// 2 | fruit = [] -// 3 | -// 4 | [[fruit]] # Not allowed -// ^^^^^ +// 2 | fruit = [] +// 3 | +// 4 | [[fruit]] # Not allowed +// ^^^^^ // -// Furthermore, the ErrorWithUsage() can be used to print the above with some -// more detailed usage guidance: +// [ErrorWithUsage] can be used to print the above with some more detailed usage +// guidance: // -// toml: error: newlines not allowed within inline tables +// toml: error: newlines not allowed within inline tables // -// At line 1, column 18: +// At line 1, column 18: // -// 1 | x = [{ key = 42 # -// ^ +// 1 | x = [{ key = 42 # +// ^ // -// Error help: +// Error help: // -// Inline tables must always be on a single line: +// Inline tables must always be on a single line: // -// table = {key = 42, second = 43} +// table = {key = 42, second = 43} // -// It is invalid to split them over multiple lines like so: +// It is invalid to split them over multiple lines like so: // -// # INVALID -// table = { -// key = 42, -// second = 43 -// } +// # INVALID +// table = { +// key = 42, +// second = 43 +// } // -// Use regular for this: +// Use regular for this: // -// [table] -// key = 42 -// second = 43 +// [table] +// key = 42 +// second = 43 type ParseError struct { Message string // Short technical message. Usage string // Longer message with usage guidance; may be blank. Position Position // Position of the error LastKey string // Last parsed key, may be blank. - Line int // Line the error occurred. Deprecated: use Position. + + // Line the error occurred. + // + // Deprecated: use [Position]. + Line int err error input string @@ -83,7 +86,7 @@ func (pe ParseError) Error() string { // ErrorWithUsage() returns the error with detailed location context. // -// See the documentation on ParseError. +// See the documentation on [ParseError]. func (pe ParseError) ErrorWithPosition() string { if pe.input == "" { // Should never happen, but just in case. return pe.Error() @@ -124,7 +127,7 @@ func (pe ParseError) ErrorWithPosition() string { // ErrorWithUsage() returns the error with detailed location context and usage // guidance. // -// See the documentation on ParseError. +// See the documentation on [ParseError]. func (pe ParseError) ErrorWithUsage() string { m := pe.ErrorWithPosition() if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" { diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index 28ed4dd3..d4d70871 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -771,7 +771,7 @@ func lexRawString(lx *lexer) stateFn { } // lexMultilineRawString consumes a raw string. Nothing can be escaped in such -// a string. It assumes that the beginning "'''" has already been consumed and +// a string. It assumes that the beginning ''' has already been consumed and // ignored. func lexMultilineRawString(lx *lexer) stateFn { r := lx.next() diff --git a/vendor/github.com/BurntSushi/toml/meta.go b/vendor/github.com/BurntSushi/toml/meta.go index d284f2a0..71847a04 100644 --- a/vendor/github.com/BurntSushi/toml/meta.go +++ b/vendor/github.com/BurntSushi/toml/meta.go @@ -71,7 +71,7 @@ func (md *MetaData) Keys() []Key { // Undecoded returns all keys that have not been decoded in the order in which // they appear in the original TOML document. // -// This includes keys that haven't been decoded because of a Primitive value. +// This includes keys that haven't been decoded because of a [Primitive] value. // Once the Primitive value is decoded, the keys will be considered decoded. // // Also note that decoding into an empty interface will result in no decoding, @@ -89,7 +89,7 @@ func (md *MetaData) Undecoded() []Key { return undecoded } -// Key represents any TOML key, including key groups. Use (MetaData).Keys to get +// Key represents any TOML key, including key groups. Use [MetaData.Keys] to get // values of this type. type Key []string diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes new file mode 100644 index 00000000..94f480de --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore index b883f1fd..815e2066 100644 --- a/vendor/github.com/Microsoft/go-winio/.gitignore +++ b/vendor/github.com/Microsoft/go-winio/.gitignore @@ -1 +1,10 @@ +.vscode/ + *.exe + +# testing +testdata + +# go workspaces +go.work +go.work.sum diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml new file mode 100644 index 00000000..af403bb1 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.golangci.yml @@ -0,0 +1,144 @@ +run: + skip-dirs: + - pkg/etw/sample + +linters: + enable: + # style + - containedctx # struct contains a context + - dupl # duplicate code + - errname # erorrs are named correctly + - goconst # strings that should be constants + - godot # comments end in a period + - misspell + - nolintlint # "//nolint" directives are properly explained + - revive # golint replacement + - stylecheck # golint replacement, less configurable than revive + - unconvert # unnecessary conversions + - wastedassign + + # bugs, performance, unused, etc ... + - contextcheck # function uses a non-inherited context + - errorlint # errors not wrapped for 1.13 + - exhaustive # check exhaustiveness of enum switch statements + - gofmt # files are gofmt'ed + - gosec # security + - nestif # deeply nested ifs + - nilerr # returns nil even with non-nil error + - prealloc # slices that can be pre-allocated + - structcheck # unused struct fields + - unparam # unused function params + +issues: + exclude-rules: + # err is very often shadowed in nested scopes + - linters: + - govet + text: '^shadow: declaration of "err" shadows declaration' + + # ignore long lines for skip autogen directives + - linters: + - revive + text: "^line-length-limit: " + source: "^//(go:generate|sys) " + + # allow unjustified ignores of error checks in defer statements + - linters: + - nolintlint + text: "^directive `//nolint:errcheck` should provide explanation" + source: '^\s*defer ' + + # allow unjustified ignores of error lints for io.EOF + - linters: + - nolintlint + text: "^directive `//nolint:errorlint` should provide explanation" + source: '[=|!]= io.EOF' + + +linters-settings: + govet: + enable-all: true + disable: + # struct order is often for Win32 compat + # also, ignore pointer bytes/GC issues for now until performance becomes an issue + - fieldalignment + check-shadowing: true + nolintlint: + allow-leading-space: false + require-explanation: true + require-specific: true + revive: + # revive is more configurable than static check, so likely the preferred alternative to static-check + # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997) + enable-all-rules: + true + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md + rules: + # rules with required arguments + - name: argument-limit + disabled: true + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: file-header + disabled: true + - name: function-length + disabled: true + - name: function-result-limit + disabled: true + - name: max-public-structs + disabled: true + # geneally annoying rules + - name: add-constant # complains about any and all strings and integers + disabled: true + - name: confusing-naming # we frequently use "Foo()" and "foo()" together + disabled: true + - name: flag-parameter # excessive, and a common idiom we use + disabled: true + # general config + - name: line-length-limit + arguments: + - 140 + - name: var-naming + arguments: + - [] + - - CID + - CRI + - CTRD + - DACL + - DLL + - DOS + - ETW + - FSCTL + - GCS + - GMSA + - HCS + - HV + - IO + - LCOW + - LDAP + - LPAC + - LTSC + - MMIO + - NT + - OCI + - PMEM + - PWSH + - RX + - SACl + - SID + - SMB + - TX + - VHD + - VHDX + - VMID + - VPCI + - WCOW + - WIM + stylecheck: + checks: + - "all" + - "-ST1003" # use revive's var naming diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md index 683be1dc..7474b4f0 100644 --- a/vendor/github.com/Microsoft/go-winio/README.md +++ b/vendor/github.com/Microsoft/go-winio/README.md @@ -13,16 +13,60 @@ Please see the LICENSE file for licensing information. ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) -declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. +This project welcomes contributions and suggestions. +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit [Microsoft CLA](https://cla.microsoft.com). -When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR -appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. +When you submit a pull request, a CLA-bot will automatically determine whether you need to +provide a CLA and decorate the PR appropriately (e.g., label, comment). +Simply follow the instructions provided by the bot. +You will only need to do this once across all repos using our CLA. -We also require that contributors sign their commits using git commit -s or git commit --signoff to certify they either authored the work themselves -or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for more info, as well as to make sure that you can -attest to the rules listed. Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. +Additionally, the pull request pipeline requires the following steps to be performed before +mergining. +### Code Sign-Off + +We require that contributors sign their commits using [`git commit --signoff`][git-commit-s] +to certify they either authored the work themselves or otherwise have permission to use it in this project. + +A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s]. + +Please see [the developer certificate](https://developercertificate.org) for more info, +as well as to make sure that you can attest to the rules listed. +Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. + +### Linting + +Code must pass a linting stage, which uses [`golangci-lint`][lint]. +The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run +automatically with VSCode by adding the following to your workspace or folder settings: + +```json + "go.lintTool": "golangci-lint", + "go.lintOnSave": "package", +``` + +Additional editor [integrations options are also available][lint-ide]. + +Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root: + +```shell +# use . or specify a path to only lint a package +# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0" +> golangci-lint run ./... +``` + +### Go Generate + +The pipeline checks that auto-generated code, via `go generate`, are up to date. + +This can be done for the entire repo: + +```shell +> go generate ./... +``` ## Code of Conduct @@ -30,8 +74,16 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Special Thanks +Thanks to [natefinch][natefinch] for the inspiration for this library. +See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation. -## Special Thanks -Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe -for another named pipe implementation. +[lint]: https://golangci-lint.run/ +[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration +[lint-install]: https://golangci-lint.run/usage/install/#local-installation + +[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s +[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff + +[natefinch]: https://github.com/natefinch diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md new file mode 100644 index 00000000..869fdfe2 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go index 2be34af4..09621c88 100644 --- a/vendor/github.com/Microsoft/go-winio/backup.go +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -7,11 +8,12 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "runtime" "syscall" "unicode/utf16" + + "golang.org/x/sys/windows" ) //sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead @@ -24,7 +26,7 @@ const ( BackupAlternateData BackupLink BackupPropertyData - BackupObjectId + BackupObjectId //revive:disable-line:var-naming ID, not Id BackupReparseData BackupSparseBlock BackupTxfsData @@ -34,14 +36,16 @@ const ( StreamSparseAttributes = uint32(8) ) +//nolint:revive // var-naming: ALL_CAPS const ( - WRITE_DAC = 0x40000 - WRITE_OWNER = 0x80000 - ACCESS_SYSTEM_SECURITY = 0x1000000 + WRITE_DAC = windows.WRITE_DAC + WRITE_OWNER = windows.WRITE_OWNER + ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY ) // BackupHeader represents a backup stream of a file. type BackupHeader struct { + //revive:disable-next-line:var-naming ID, not Id Id uint32 // The backup stream ID Attributes uint32 // Stream attributes Size int64 // The size of the stream in bytes @@ -49,8 +53,8 @@ type BackupHeader struct { Offset int64 // The offset of the stream in the file (for BackupSparseBlock only). } -type win32StreamId struct { - StreamId uint32 +type win32StreamID struct { + StreamID uint32 Attributes uint32 Size uint64 NameSize uint32 @@ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader { // Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if // it was not completely read. func (r *BackupStreamReader) Next() (*BackupHeader, error) { - if r.bytesLeft > 0 { + if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this if s, ok := r.r.(io.Seeker); ok { // Make sure Seek on io.SeekCurrent sometimes succeeds // before trying the actual seek. @@ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { r.bytesLeft = 0 } } - if _, err := io.Copy(ioutil.Discard, r); err != nil { + if _, err := io.Copy(io.Discard, r); err != nil { return nil, err } } - var wsi win32StreamId + var wsi win32StreamID if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { return nil, err } hdr := &BackupHeader{ - Id: wsi.StreamId, + Id: wsi.StreamID, Attributes: wsi.Attributes, Size: int64(wsi.Size), } @@ -102,7 +106,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { } hdr.Name = syscall.UTF16ToString(name) } - if wsi.StreamId == BackupSparseBlock { + if wsi.StreamID == BackupSparseBlock { if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { return nil, err } @@ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { return fmt.Errorf("missing %d bytes", w.bytesLeft) } name := utf16.Encode([]rune(hdr.Name)) - wsi := win32StreamId{ - StreamId: hdr.Id, + wsi := win32StreamID{ + StreamID: hdr.Id, Attributes: hdr.Attributes, Size: uint64(hdr.Size), NameSize: uint32(len(name) * 2), @@ -203,7 +207,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { var bytesRead uint32 err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) if err != nil { - return 0, &os.PathError{"BackupRead", r.f.Name(), err} + return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err} } runtime.KeepAlive(r.f) if bytesRead == 0 { @@ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { // the underlying file. func (r *BackupFileReader) Close() error { if r.ctx != 0 { - backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) + _ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) runtime.KeepAlive(r.f) r.ctx = 0 } @@ -242,7 +246,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { var bytesWritten uint32 err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) if err != nil { - return 0, &os.PathError{"BackupWrite", w.f.Name(), err} + return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err} } runtime.KeepAlive(w.f) if int(bytesWritten) != len(b) { @@ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { // close the underlying file. func (w *BackupFileWriter) Close() error { if w.ctx != 0 { - backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) + _ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) runtime.KeepAlive(w.f) w.ctx = 0 } @@ -271,7 +275,13 @@ func OpenForBackup(path string, access uint32, share uint32, createmode uint32) if err != nil { return nil, err } - h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) + h, err := syscall.CreateFile(&winPath[0], + access, + share, + nil, + createmode, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, + 0) if err != nil { err = &os.PathError{Op: "open", Path: path, Err: err} return nil, err diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/noop.go b/vendor/github.com/Microsoft/go-winio/backuptar/doc.go similarity index 81% rename from vendor/github.com/Microsoft/go-winio/backuptar/noop.go rename to vendor/github.com/Microsoft/go-winio/backuptar/doc.go index d39eccf0..965d52ab 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/noop.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/doc.go @@ -1,4 +1,3 @@ -// +build !windows // This file only exists to allow go get on non-Windows platforms. package backuptar diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go b/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go index 34160966..455fd798 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go @@ -1,3 +1,5 @@ +//go:build windows + package backuptar import ( diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go index 2342a7fc..6b3b0cd5 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package backuptar @@ -7,7 +8,6 @@ import ( "encoding/base64" "fmt" "io" - "io/ioutil" "path/filepath" "strconv" "strings" @@ -18,17 +18,18 @@ import ( "golang.org/x/sys/windows" ) +//nolint:deadcode,varcheck // keep unused constants for potential future use const ( - c_ISUID = 04000 // Set uid - c_ISGID = 02000 // Set gid - c_ISVTX = 01000 // Save text (sticky bit) - c_ISDIR = 040000 // Directory - c_ISFIFO = 010000 // FIFO - c_ISREG = 0100000 // Regular file - c_ISLNK = 0120000 // Symbolic link - c_ISBLK = 060000 // Block special file - c_ISCHR = 020000 // Character special file - c_ISSOCK = 0140000 // Socket + cISUID = 0004000 // Set uid + cISGID = 0002000 // Set gid + cISVTX = 0001000 // Save text (sticky bit) + cISDIR = 0040000 // Directory + cISFIFO = 0010000 // FIFO + cISREG = 0100000 // Regular file + cISLNK = 0120000 // Symbolic link + cISBLK = 0060000 // Block special file + cISCHR = 0020000 // Character special file + cISSOCK = 0140000 // Socket ) const ( @@ -44,7 +45,7 @@ const ( // zeroReader is an io.Reader that always returns 0s. type zeroReader struct{} -func (zr zeroReader) Read(b []byte) (int, error) { +func (zeroReader) Read(b []byte) (int, error) { for i := range b { b[i] = 0 } @@ -55,7 +56,7 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { curOffset := int64(0) for { bhdr, err := br.Next() - if err == io.EOF { + if err == io.EOF { //nolint:errorlint err = io.ErrUnexpectedEOF } if err != nil { @@ -71,8 +72,8 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { } // archive/tar does not support writing sparse files // so just write zeroes to catch up to the current offset. - if _, err := io.CopyN(t, zeroReader{}, bhdr.Offset-curOffset); err != nil { - return fmt.Errorf("seek to offset %d: %s", bhdr.Offset, err) + if _, err = io.CopyN(t, zeroReader{}, bhdr.Offset-curOffset); err != nil { + return fmt.Errorf("seek to offset %d: %w", bhdr.Offset, err) } if bhdr.Size == 0 { // A sparse block with size = 0 is used to mark the end of the sparse blocks. @@ -106,7 +107,7 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds())) if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { - hdr.Mode |= c_ISDIR + hdr.Mode |= cISDIR hdr.Size = 0 hdr.Typeflag = tar.TypeDir } @@ -116,32 +117,29 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta // SecurityDescriptorFromTarHeader reads the SDDL associated with the header of the current file // from the tar header and returns the security descriptor into a byte slice. func SecurityDescriptorFromTarHeader(hdr *tar.Header) ([]byte, error) { - // Maintaining old SDDL-based behavior for backward - // compatibility. All new tar headers written by this library - // will have raw binary for the security descriptor. - var sd []byte - var err error - if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { - sd, err = winio.SddlToSecurityDescriptor(sddl) - if err != nil { - return nil, err - } - } if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { - sd, err = base64.StdEncoding.DecodeString(sdraw) + sd, err := base64.StdEncoding.DecodeString(sdraw) if err != nil { + // Not returning sd as-is in the error-case, as base64.DecodeString + // may return partially decoded data (not nil or empty slice) in case + // of a failure: https://github.com/golang/go/blob/go1.17.7/src/encoding/base64/base64.go#L382-L387 return nil, err } + return sd, nil } - return sd, nil + // Maintaining old SDDL-based behavior for backward compatibility. All new + // tar headers written by this library will have raw binary for the security + // descriptor. + if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { + return winio.SddlToSecurityDescriptor(sddl) + } + return nil, nil } // ExtendedAttributesFromTarHeader reads the EAs associated with the header of the // current file from the tar header and returns it as a byte slice. func ExtendedAttributesFromTarHeader(hdr *tar.Header) ([]byte, error) { - var eas []winio.ExtendedAttribute - var eadata []byte - var err error + var eas []winio.ExtendedAttribute //nolint:prealloc // len(eas) <= len(hdr.PAXRecords); prealloc is wasteful for k, v := range hdr.PAXRecords { if !strings.HasPrefix(k, hdrEaPrefix) { continue @@ -155,13 +153,15 @@ func ExtendedAttributesFromTarHeader(hdr *tar.Header) ([]byte, error) { Value: data, }) } + var eaData []byte + var err error if len(eas) != 0 { - eadata, err = winio.EncodeExtendedAttributes(eas) + eaData, err = winio.EncodeExtendedAttributes(eas) if err != nil { return nil, err } } - return eadata, nil + return eaData, nil } // EncodeReparsePointFromTarHeader reads the ReparsePoint structure from the tar header @@ -182,11 +182,9 @@ func EncodeReparsePointFromTarHeader(hdr *tar.Header) []byte { // // The additional Win32 metadata is: // -// MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value -// -// MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format -// -// MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink) +// - MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value +// - MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format +// - MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink) func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error { name = filepath.ToSlash(name) hdr := BasicInfoHeader(name, size, fileInfo) @@ -209,7 +207,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size var dataHdr *winio.BackupHeader for dataHdr == nil { bhdr, err := br.Next() - if err == io.EOF { + if err == io.EOF { //nolint:errorlint break } if err != nil { @@ -217,21 +215,21 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } switch bhdr.Id { case winio.BackupData: - hdr.Mode |= c_ISREG + hdr.Mode |= cISREG if !readTwice { dataHdr = bhdr } case winio.BackupSecurity: - sd, err := ioutil.ReadAll(br) + sd, err := io.ReadAll(br) if err != nil { return err } hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd) case winio.BackupReparseData: - hdr.Mode |= c_ISLNK + hdr.Mode |= cISLNK hdr.Typeflag = tar.TypeSymlink - reparseBuffer, err := ioutil.ReadAll(br) + reparseBuffer, _ := io.ReadAll(br) rp, err := winio.DecodeReparsePoint(reparseBuffer) if err != nil { return err @@ -242,7 +240,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size hdr.Linkname = rp.Target case winio.BackupEaData: - eab, err := ioutil.ReadAll(br) + eab, err := io.ReadAll(br) if err != nil { return err } @@ -276,7 +274,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } for dataHdr == nil { bhdr, err := br.Next() - if err == io.EOF { + if err == io.EOF { //nolint:errorlint break } if err != nil { @@ -311,7 +309,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size // range of the file containing the range contents. Finally there is a sparse block stream with // size = 0 and offset = . - if dataHdr != nil { + if dataHdr != nil { //nolint:nestif // todo: reduce nesting complexity // A data stream was found. Copy the data. // We assume that we will either have a data stream size > 0 XOR have sparse block streams. if dataHdr.Size > 0 || (dataHdr.Attributes&winio.StreamSparseAttributes) == 0 { @@ -319,13 +317,13 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size) } if _, err = io.Copy(t, br); err != nil { - return fmt.Errorf("%s: copying contents from data stream: %s", name, err) + return fmt.Errorf("%s: copying contents from data stream: %w", name, err) } } else if size > 0 { // As of a recent OS change, BackupRead now returns a data stream for empty sparse files. // These files have no sparse block streams, so skip the copySparse call if file size = 0. if err = copySparse(t, br); err != nil { - return fmt.Errorf("%s: copying contents from sparse block stream: %s", name, err) + return fmt.Errorf("%s: copying contents from sparse block stream: %w", name, err) } } } @@ -335,7 +333,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size // been written. In practice, this means that we don't get EA or TXF metadata. for { bhdr, err := br.Next() - if err == io.EOF { + if err == io.EOF { //nolint:errorlint break } if err != nil { @@ -343,35 +341,30 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } switch bhdr.Id { case winio.BackupAlternateData: - altName := bhdr.Name - if strings.HasSuffix(altName, ":$DATA") { - altName = altName[:len(altName)-len(":$DATA")] - } - if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 { - hdr = &tar.Header{ - Format: hdr.Format, - Name: name + altName, - Mode: hdr.Mode, - Typeflag: tar.TypeReg, - Size: bhdr.Size, - ModTime: hdr.ModTime, - AccessTime: hdr.AccessTime, - ChangeTime: hdr.ChangeTime, - } - err = t.WriteHeader(hdr) - if err != nil { - return err - } - _, err = io.Copy(t, br) - if err != nil { - return err - } - - } else { + if (bhdr.Attributes & winio.StreamSparseAttributes) != 0 { // Unsupported for now, since the size of the alternate stream is not present // in the backup stream until after the data has been read. return fmt.Errorf("%s: tar of sparse alternate data streams is unsupported", name) } + altName := strings.TrimSuffix(bhdr.Name, ":$DATA") + hdr = &tar.Header{ + Format: hdr.Format, + Name: name + altName, + Mode: hdr.Mode, + Typeflag: tar.TypeReg, + Size: bhdr.Size, + ModTime: hdr.ModTime, + AccessTime: hdr.AccessTime, + ChangeTime: hdr.ChangeTime, + } + err = t.WriteHeader(hdr) + if err != nil { + return err + } + _, err = io.Copy(t, br) + if err != nil { + return err + } case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData: // ignore these streams default: @@ -413,7 +406,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win } fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano()) } - return + return name, size, fileInfo, err } // WriteBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple @@ -474,7 +467,6 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( if err != nil { return nil, err } - } if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go new file mode 100644 index 00000000..1f5bfe2d --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/doc.go @@ -0,0 +1,22 @@ +// This package provides utilities for efficiently performing Win32 IO operations in Go. +// Currently, this package is provides support for genreal IO and management of +// - named pipes +// - files +// - [Hyper-V sockets] +// +// This code is similar to Go's [net] package, and uses IO completion ports to avoid +// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines. +// +// This limits support to Windows Vista and newer operating systems. +// +// Additionally, this package provides support for: +// - creating and managing GUIDs +// - writing to [ETW] +// - opening and manageing VHDs +// - parsing [Windows Image files] +// - auto-generating Win32 API code +// +// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service +// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw- +// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images +package winio diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go index 4051c1b3..e104dbdf 100644 --- a/vendor/github.com/Microsoft/go-winio/ea.go +++ b/vendor/github.com/Microsoft/go-winio/ea.go @@ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) if err != nil { err = errInvalidEaBuffer - return + return ea, nb, err } nameOffset := fileFullEaInformationSize @@ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { nextOffset := int(info.NextEntryOffset) if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { err = errInvalidEaBuffer - return + return ea, nb, err } ea.Name = string(b[nameOffset : nameOffset+nameLen]) @@ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { if info.NextEntryOffset != 0 { nb = b[info.NextEntryOffset:] } - return + return ea, nb, err } // DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION @@ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { eas = append(eas, ea) b = nb } - return + return eas, err } func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 293ab54c..175a99d3 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -11,6 +11,8 @@ import ( "sync/atomic" "syscall" "time" + + "golang.org/x/sys/windows" ) //sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx @@ -24,6 +26,8 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } + +//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg func (b *atomicBool) swap(new bool) bool { var newInt int32 if new { @@ -32,11 +36,6 @@ func (b *atomicBool) swap(new bool) bool { return atomic.SwapInt32((*int32)(b), newInt) == 1 } -const ( - cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 - cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 -) - var ( ErrFileClosed = errors.New("file has already been closed") ErrTimeout = &timeoutError{} @@ -44,28 +43,28 @@ var ( type timeoutError struct{} -func (e *timeoutError) Error() string { return "i/o timeout" } -func (e *timeoutError) Timeout() bool { return true } -func (e *timeoutError) Temporary() bool { return true } +func (*timeoutError) Error() string { return "i/o timeout" } +func (*timeoutError) Timeout() bool { return true } +func (*timeoutError) Temporary() bool { return true } type timeoutChan chan struct{} var ioInitOnce sync.Once var ioCompletionPort syscall.Handle -// ioResult contains the result of an asynchronous IO operation +// ioResult contains the result of an asynchronous IO operation. type ioResult struct { bytes uint32 err error } -// ioOperation represents an outstanding asynchronous Win32 IO +// ioOperation represents an outstanding asynchronous Win32 IO. type ioOperation struct { o syscall.Overlapped ch chan ioResult } -func initIo() { +func initIO() { h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) if err != nil { panic(err) @@ -94,15 +93,15 @@ type deadlineHandler struct { timedout atomicBool } -// makeWin32File makes a new win32File from an existing file handle +// makeWin32File makes a new win32File from an existing file handle. func makeWin32File(h syscall.Handle) (*win32File, error) { f := &win32File{handle: h} - ioInitOnce.Do(initIo) + ioInitOnce.Do(initIO) _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) if err != nil { return nil, err } - err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) + err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE) if err != nil { return nil, err } @@ -121,14 +120,14 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { return f, nil } -// closeHandle closes the resources associated with a Win32 handle +// closeHandle closes the resources associated with a Win32 handle. func (f *win32File) closeHandle() { f.wgLock.Lock() // Atomically set that we are closing, releasing the resources only once. if !f.closing.swap(true) { f.wgLock.Unlock() // cancel all IO and wait for it to complete - cancelIoEx(f.handle, nil) + _ = cancelIoEx(f.handle, nil) f.wg.Wait() // at this point, no new IO can start syscall.Close(f.handle) @@ -144,14 +143,14 @@ func (f *win32File) Close() error { return nil } -// IsClosed checks if the file has been closed +// IsClosed checks if the file has been closed. func (f *win32File) IsClosed() bool { return f.closing.isSet() } -// prepareIo prepares for a new IO operation. +// prepareIO prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. -func (f *win32File) prepareIo() (*ioOperation, error) { +func (f *win32File) prepareIO() (*ioOperation, error) { f.wgLock.RLock() if f.closing.isSet() { f.wgLock.RUnlock() @@ -164,7 +163,7 @@ func (f *win32File) prepareIo() (*ioOperation, error) { return c, nil } -// ioCompletionProcessor processes completed async IOs forever +// ioCompletionProcessor processes completed async IOs forever. func ioCompletionProcessor(h syscall.Handle) { for { var bytes uint32 @@ -178,15 +177,17 @@ func ioCompletionProcessor(h syscall.Handle) { } } -// asyncIo processes the return value from ReadFile or WriteFile, blocking until +// todo: helsaawy - create an asyncIO version that takes a context + +// asyncIO processes the return value from ReadFile or WriteFile, blocking until // the operation has actually completed. -func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { - if err != syscall.ERROR_IO_PENDING { +func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { + if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno return int(bytes), err } if f.closing.isSet() { - cancelIoEx(f.handle, &c.o) + _ = cancelIoEx(f.handle, &c.o) } var timeout timeoutChan @@ -200,7 +201,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er select { case r = <-c.ch: err = r.err - if err == syscall.ERROR_OPERATION_ABORTED { + if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno if f.closing.isSet() { err = ErrFileClosed } @@ -210,10 +211,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) } case <-timeout: - cancelIoEx(f.handle, &c.o) + _ = cancelIoEx(f.handle, &c.o) r = <-c.ch err = r.err - if err == syscall.ERROR_OPERATION_ABORTED { + if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno err = ErrTimeout } } @@ -221,13 +222,14 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er // runtime.KeepAlive is needed, as c is passed via native // code to ioCompletionProcessor, c must remain alive // until the channel read is complete. + // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive? runtime.KeepAlive(c) return int(r.bytes), err } // Read reads from a file handle. func (f *win32File) Read(b []byte) (int, error) { - c, err := f.prepareIo() + c, err := f.prepareIO() if err != nil { return 0, err } @@ -239,13 +241,13 @@ func (f *win32File) Read(b []byte) (int, error) { var bytes uint32 err = syscall.ReadFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, &f.readDeadline, bytes, err) + n, err := f.asyncIO(c, &f.readDeadline, bytes, err) runtime.KeepAlive(b) // Handle EOF conditions. if err == nil && n == 0 && len(b) != 0 { return 0, io.EOF - } else if err == syscall.ERROR_BROKEN_PIPE { + } else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno return 0, io.EOF } else { return n, err @@ -254,7 +256,7 @@ func (f *win32File) Read(b []byte) (int, error) { // Write writes to a file handle. func (f *win32File) Write(b []byte) (int, error) { - c, err := f.prepareIo() + c, err := f.prepareIO() if err != nil { return 0, err } @@ -266,7 +268,7 @@ func (f *win32File) Write(b []byte) (int, error) { var bytes uint32 err = syscall.WriteFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) + n, err := f.asyncIO(c, &f.writeDeadline, bytes, err) runtime.KeepAlive(b) return n, err } diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index 3ab6bff6..702950e7 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -14,13 +15,18 @@ import ( type FileBasicInfo struct { CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime FileAttributes uint32 - pad uint32 // padding + _ uint32 // padding } // GetFileBasicInfo retrieves times and attributes for a file. func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { bi := &FileBasicInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(bi)), + uint32(unsafe.Sizeof(*bi)), + ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -29,7 +35,12 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { // SetFileBasicInfo sets times and attributes for a file. func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { - if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.SetFileInformationByHandle( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(bi)), + uint32(unsafe.Sizeof(*bi)), + ); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -48,7 +59,10 @@ type FileStandardInfo struct { // GetFileStandardInfo retrieves ended information for the file. func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { si := &FileStandardInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), + windows.FileStandardInfo, + (*byte)(unsafe.Pointer(si)), + uint32(unsafe.Sizeof(*si))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -65,7 +79,12 @@ type FileIDInfo struct { // GetFileID retrieves the unique (volume, file ID) pair for a file. func GetFileID(f *os.File) (*FileIDInfo, error) { fileID := &FileIDInfo{} - if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileIdInfo, + (*byte)(unsafe.Pointer(fileID)), + uint32(unsafe.Sizeof(*fileID)), + ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go index b2b644d0..52f1c280 100644 --- a/vendor/github.com/Microsoft/go-winio/hvsock.go +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -4,6 +4,8 @@ package winio import ( + "context" + "errors" "fmt" "io" "net" @@ -12,16 +14,87 @@ import ( "time" "unsafe" + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/socket" "github.com/Microsoft/go-winio/pkg/guid" ) -//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind +const afHVSock = 34 // AF_HYPERV -const ( - afHvSock = 34 // AF_HYPERV +// Well known Service and VM IDs +//https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards - socketError = ^uintptr(0) -) +// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions. +func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000 + return guid.GUID{} +} + +// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions. +func HvsockGUIDBroadcast() guid.GUID { //ffffffff-ffff-ffff-ffff-ffffffffffff + return guid.GUID{ + Data1: 0xffffffff, + Data2: 0xffff, + Data3: 0xffff, + Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + } +} + +// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector. +func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838 + return guid.GUID{ + Data1: 0xe0e16197, + Data2: 0xdd56, + Data3: 0x4a10, + Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38}, + } +} + +// HvsockGUIDSiloHost is the address of a silo's host partition: +// - The silo host of a hosted silo is the utility VM. +// - The silo host of a silo on a physical host is the physical host. +func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568 + return guid.GUID{ + Data1: 0x36bd0c5c, + Data2: 0x7276, + Data3: 0x4223, + Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68}, + } +} + +// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions. +func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd + return guid.GUID{ + Data1: 0x90db8b89, + Data2: 0xd35, + Data3: 0x4f79, + Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd}, + } +} + +// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition. +// Listening on this VmId accepts connection from: +// - Inside silos: silo host partition. +// - Inside hosted silo: host of the VM. +// - Inside VM: VM host. +// - Physical host: Not supported. +func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878 + return guid.GUID{ + Data1: 0xa42e7cda, + Data2: 0xd03f, + Data3: 0x480c, + Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78}, + } +} + +// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol. +func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3 + return guid.GUID{ + Data2: 0xfacb, + Data3: 0x11e6, + Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3}, + } +} // An HvsockAddr is an address for a AF_HYPERV socket. type HvsockAddr struct { @@ -36,8 +109,10 @@ type rawHvsockAddr struct { ServiceID guid.GUID } +var _ socket.RawSockaddr = &rawHvsockAddr{} + // Network returns the address's network name, "hvsock". -func (addr *HvsockAddr) Network() string { +func (*HvsockAddr) Network() string { return "hvsock" } @@ -47,14 +122,14 @@ func (addr *HvsockAddr) String() string { // VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port. func VsockServiceID(port uint32) guid.GUID { - g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3") + g := hvsockVsockServiceTemplate() // make a copy g.Data1 = port return g } func (addr *HvsockAddr) raw() rawHvsockAddr { return rawHvsockAddr{ - Family: afHvSock, + Family: afHVSock, VMID: addr.VMID, ServiceID: addr.ServiceID, } @@ -65,20 +140,48 @@ func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { addr.ServiceID = raw.ServiceID } +// Sockaddr returns a pointer to and the size of this struct. +// +// Implements the [socket.RawSockaddr] interface, and allows use in +// [socket.Bind] and [socket.ConnectEx]. +func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) { + return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil +} + +// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`. +func (r *rawHvsockAddr) FromBytes(b []byte) error { + n := int(unsafe.Sizeof(rawHvsockAddr{})) + + if len(b) < n { + return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize) + } + + copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n]) + if r.Family != afHVSock { + return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily) + } + + return nil +} + // HvsockListener is a socket listener for the AF_HYPERV address family. type HvsockListener struct { sock *win32File addr HvsockAddr } +var _ net.Listener = &HvsockListener{} + // HvsockConn is a connected socket of the AF_HYPERV address family. type HvsockConn struct { sock *win32File local, remote HvsockAddr } -func newHvSocket() (*win32File, error) { - fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1) +var _ net.Conn = &HvsockConn{} + +func newHVSocket() (*win32File, error) { + fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1) if err != nil { return nil, os.NewSyscallError("socket", err) } @@ -94,12 +197,12 @@ func newHvSocket() (*win32File, error) { // ListenHvsock listens for connections on the specified hvsock address. func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { l := &HvsockListener{addr: *addr} - sock, err := newHvSocket() + sock, err := newHVSocket() if err != nil { return nil, l.opErr("listen", err) } sa := addr.raw() - err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa))) + err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { return nil, l.opErr("listen", os.NewSyscallError("socket", err)) } @@ -121,7 +224,7 @@ func (l *HvsockListener) Addr() net.Addr { // Accept waits for the next connection and returns it. func (l *HvsockListener) Accept() (_ net.Conn, err error) { - sock, err := newHvSocket() + sock, err := newHVSocket() if err != nil { return nil, l.opErr("accept", err) } @@ -130,27 +233,42 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) { sock.Close() } }() - c, err := l.sock.prepareIo() + c, err := l.sock.prepareIO() if err != nil { return nil, l.opErr("accept", err) } defer l.sock.wg.Done() // AcceptEx, per documentation, requires an extra 16 bytes per address. + // + // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) var addrbuf [addrlen * 2]byte var bytes uint32 - err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o) - _, err = l.sock.asyncIo(c, nil, bytes, err) - if err != nil { + err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /*rxdatalen*/, addrlen, addrlen, &bytes, &c.o) + if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil { return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) } + conn := &HvsockConn{ sock: sock, } + // The local address returned in the AcceptEx buffer is the same as the Listener socket's + // address. However, the service GUID reported by GetSockName is different from the Listeners + // socket, and is sometimes the same as the local address of the socket that dialed the + // address, with the service GUID.Data1 incremented, but othertimes is different. + // todo: does the local address matter? is the listener's address or the actual address appropriate? conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) + + // initialize the accepted socket and update its properties with those of the listening socket + if err = windows.Setsockopt(windows.Handle(sock.handle), + windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT, + (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil { + return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err)) + } + sock = nil return conn, nil } @@ -160,43 +278,171 @@ func (l *HvsockListener) Close() error { return l.sock.Close() } -/* Need to finish ConnectEx handling -func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) { - sock, err := newHvSocket() +// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]). +type HvsockDialer struct { + // Deadline is the time the Dial operation must connect before erroring. + Deadline time.Time + + // Retries is the number of additional connects to try if the connection times out, is refused, + // or the host is unreachable + Retries uint + + // RetryWait is the time to wait after a connection error to retry + RetryWait time.Duration + + rt *time.Timer // redial wait timer +} + +// Dial the Hyper-V socket at addr. +// +// See [HvsockDialer.Dial] for more information. +func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + return (&HvsockDialer{}).Dial(ctx, addr) +} + +// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful. +// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between +// retries. +// +// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx. +func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + op := "dial" + // create the conn early to use opErr() + conn = &HvsockConn{ + remote: *addr, + } + + if !d.Deadline.IsZero() { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, d.Deadline) + defer cancel() + } + + // preemptive timeout/cancellation check + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) + } + + sock, err := newHVSocket() if err != nil { - return nil, err + return nil, conn.opErr(op, err) } defer func() { if sock != nil { sock.Close() } }() - c, err := sock.prepareIo() + + sa := addr.raw() + err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { - return nil, err + return nil, conn.opErr(op, os.NewSyscallError("bind", err)) + } + + c, err := sock.prepareIO() + if err != nil { + return nil, conn.opErr(op, err) } defer sock.wg.Done() var bytes uint32 - err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o) - _, err = sock.asyncIo(ctx, c, nil, bytes, err) + for i := uint(0); i <= d.Retries; i++ { + err = socket.ConnectEx( + windows.Handle(sock.handle), + &sa, + nil, // sendBuf + 0, // sendDataLen + &bytes, + (*windows.Overlapped)(unsafe.Pointer(&c.o))) + _, err = sock.asyncIO(c, nil, bytes, err) + if i < d.Retries && canRedial(err) { + if err = d.redialWait(ctx); err == nil { + continue + } + } + break + } if err != nil { - return nil, err + return nil, conn.opErr(op, os.NewSyscallError("connectex", err)) } - conn := &HvsockConn{ - sock: sock, - remote: *addr, + + // update the connection properties, so shutdown can be used + if err = windows.Setsockopt( + windows.Handle(sock.handle), + windows.SOL_SOCKET, + windows.SO_UPDATE_CONNECT_CONTEXT, + nil, // optvalue + 0, // optlen + ); err != nil { + return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err)) + } + + // get the local name + var sal rawHvsockAddr + err = socket.GetSockName(windows.Handle(sock.handle), &sal) + if err != nil { + return nil, conn.opErr(op, os.NewSyscallError("getsockname", err)) + } + conn.local.fromRaw(&sal) + + // one last check for timeout, since asyncIO doesn't check the context + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) } + + conn.sock = sock sock = nil + return conn, nil } -*/ + +// redialWait waits before attempting to redial, resetting the timer as appropriate. +func (d *HvsockDialer) redialWait(ctx context.Context) (err error) { + if d.RetryWait == 0 { + return nil + } + + if d.rt == nil { + d.rt = time.NewTimer(d.RetryWait) + } else { + // should already be stopped and drained + d.rt.Reset(d.RetryWait) + } + + select { + case <-ctx.Done(): + case <-d.rt.C: + return nil + } + + // stop and drain the timer + if !d.rt.Stop() { + <-d.rt.C + } + return ctx.Err() +} + +// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall. +func canRedial(err error) bool { + //nolint:errorlint // guaranteed to be an Errno + switch err { + case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT, + windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL: + return true + default: + return false + } +} func (conn *HvsockConn) opErr(op string, err error) error { + // translate from "file closed" to "socket closed" + if errors.Is(err, ErrFileClosed) { + err = socket.ErrSocketClosed + } return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} } func (conn *HvsockConn) Read(b []byte) (int, error) { - c, err := conn.sock.prepareIo() + c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("read", err) } @@ -204,10 +450,11 @@ func (conn *HvsockConn) Read(b []byte) (int, error) { buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var flags, bytes uint32 err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) - n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err) + n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err) if err != nil { - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsarecv", err) + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsarecv", eno) } return 0, conn.opErr("read", err) } else if n == 0 { @@ -230,7 +477,7 @@ func (conn *HvsockConn) Write(b []byte) (int, error) { } func (conn *HvsockConn) write(b []byte) (int, error) { - c, err := conn.sock.prepareIo() + c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("write", err) } @@ -238,10 +485,11 @@ func (conn *HvsockConn) write(b []byte) (int, error) { buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var bytes uint32 err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) - n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err) + n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err) if err != nil { - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsasend", err) + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsasend", eno) } return 0, conn.opErr("write", err) } @@ -257,13 +505,19 @@ func (conn *HvsockConn) IsClosed() bool { return conn.sock.IsClosed() } +// shutdown disables sending or receiving on a socket. func (conn *HvsockConn) shutdown(how int) error { if conn.IsClosed() { - return ErrFileClosed + return socket.ErrSocketClosed } err := syscall.Shutdown(conn.sock.handle, how) if err != nil { + // If the connection was closed, shutdowns fail with "not connected" + if errors.Is(err, windows.WSAENOTCONN) || + errors.Is(err, windows.WSAESHUTDOWN) { + err = socket.ErrSocketClosed + } return os.NewSyscallError("shutdown", err) } return nil @@ -273,7 +527,7 @@ func (conn *HvsockConn) shutdown(how int) error { func (conn *HvsockConn) CloseRead() error { err := conn.shutdown(syscall.SHUT_RD) if err != nil { - return conn.opErr("close", err) + return conn.opErr("closeread", err) } return nil } @@ -283,7 +537,7 @@ func (conn *HvsockConn) CloseRead() error { func (conn *HvsockConn) CloseWrite() error { err := conn.shutdown(syscall.SHUT_WR) if err != nil { - return conn.opErr("close", err) + return conn.opErr("closewrite", err) } return nil } @@ -300,8 +554,13 @@ func (conn *HvsockConn) RemoteAddr() net.Addr { // SetDeadline implements the net.Conn SetDeadline method. func (conn *HvsockConn) SetDeadline(t time.Time) error { - conn.SetReadDeadline(t) - conn.SetWriteDeadline(t) + // todo: implement `SetDeadline` for `win32File` + if err := conn.SetReadDeadline(t); err != nil { + return fmt.Errorf("set read deadline: %w", err) + } + if err := conn.SetWriteDeadline(t); err != nil { + return fmt.Errorf("set write deadline: %w", err) + } return nil } diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go new file mode 100644 index 00000000..7e82f9af --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go @@ -0,0 +1,20 @@ +package socket + +import ( + "unsafe" +) + +// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The +// struct must meet the Win32 sockaddr requirements specified here: +// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 +// +// Specifically, the struct size must be least larger than an int16 (unsigned short) +// for the address family. +type RawSockaddr interface { + // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing + // for the RawSockaddr's data to be overwritten by syscalls (if necessary). + // + // It is the callers responsibility to validate that the values are valid; invalid + // pointers or size can cause a panic. + Sockaddr() (unsafe.Pointer, int32, error) +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go new file mode 100644 index 00000000..39e8c05f --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go @@ -0,0 +1,179 @@ +//go:build windows + +package socket + +import ( + "errors" + "fmt" + "net" + "sync" + "syscall" + "unsafe" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go + +//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname +//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername +//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind + +const socketError = uintptr(^uint32(0)) + +var ( + // todo(helsaawy): create custom error types to store the desired vs actual size and addr family? + + ErrBufferSize = errors.New("buffer size") + ErrAddrFamily = errors.New("address family") + ErrInvalidPointer = errors.New("invalid pointer") + ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed) +) + +// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error) + +// GetSockName writes the local address of socket s to the [RawSockaddr] rsa. +// If rsa is not large enough, the [windows.WSAEFAULT] is returned. +func GetSockName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + // although getsockname returns WSAEFAULT if the buffer is too small, it does not set + // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy + return getsockname(s, ptr, &l) +} + +// GetPeerName returns the remote address the socket is connected to. +// +// See [GetSockName] for more information. +func GetPeerName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return getpeername(s, ptr, &l) +} + +func Bind(s windows.Handle, rsa RawSockaddr) (err error) { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return bind(s, ptr, l) +} + +// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the +// their sockaddr interface, so they cannot be used with HvsockAddr +// Replicate functionality here from +// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go + +// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at +// runtime via a WSAIoctl call: +// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks + +type runtimeFunc struct { + id guid.GUID + once sync.Once + addr uintptr + err error +} + +func (f *runtimeFunc) Load() error { + f.once.Do(func() { + var s windows.Handle + s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP) + if f.err != nil { + return + } + defer windows.CloseHandle(s) //nolint:errcheck + + var n uint32 + f.err = windows.WSAIoctl(s, + windows.SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&f.id)), + uint32(unsafe.Sizeof(f.id)), + (*byte)(unsafe.Pointer(&f.addr)), + uint32(unsafe.Sizeof(f.addr)), + &n, + nil, //overlapped + 0, //completionRoutine + ) + }) + return f.err +} + +var ( + // todo: add `AcceptEx` and `GetAcceptExSockaddrs` + WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS + Data1: 0x25a207b9, + Data2: 0xddf3, + Data3: 0x4660, + Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, + } + + connectExFunc = runtimeFunc{id: WSAID_CONNECTEX} +) + +func ConnectEx( + fd windows.Handle, + rsa RawSockaddr, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) error { + if err := connectExFunc.Load(); err != nil { + return fmt.Errorf("failed to load ConnectEx function pointer: %w", err) + } + ptr, n, err := rsa.Sockaddr() + if err != nil { + return err + } + return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) +} + +// BOOL LpfnConnectex( +// [in] SOCKET s, +// [in] const sockaddr *name, +// [in] int namelen, +// [in, optional] PVOID lpSendBuffer, +// [in] DWORD dwSendDataLength, +// [out] LPDWORD lpdwBytesSent, +// [in] LPOVERLAPPED lpOverlapped +// ) + +func connectEx( + s windows.Handle, + name unsafe.Pointer, + namelen int32, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) (err error) { + // todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN + r1, _, e1 := syscall.Syscall9(connectExFunc.addr, + 7, + uintptr(s), + uintptr(name), + uintptr(namelen), + uintptr(unsafe.Pointer(sendBuf)), + uintptr(sendDataLen), + uintptr(unsafe.Pointer(bytesSent)), + uintptr(unsafe.Pointer(overlapped)), + 0, + 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return err +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go new file mode 100644 index 00000000..6d2e1a9e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go @@ -0,0 +1,72 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package socket + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") + + procbind = modws2_32.NewProc("bind") + procgetpeername = modws2_32.NewProc("getpeername") + procgetsockname = modws2_32.NewProc("getsockname") +) + +func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index 96700a73..ca6e38fc 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -13,6 +14,8 @@ import ( "syscall" "time" "unsafe" + + "golang.org/x/sys/windows" ) //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe @@ -21,10 +24,10 @@ import ( //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc -//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile -//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb -//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U -//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl +//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile +//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb +//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U +//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl type ioStatusBlock struct { Status, Information uintptr @@ -51,45 +54,22 @@ type securityDescriptor struct { Control uint16 Owner uintptr Group uintptr - Sacl uintptr - Dacl uintptr + Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl + Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl } -type ntstatus int32 +type ntStatus int32 -func (status ntstatus) Err() error { +func (status ntStatus) Err() error { if status >= 0 { return nil } return rtlNtStatusToDosError(status) } -const ( - cERROR_PIPE_BUSY = syscall.Errno(231) - cERROR_NO_DATA = syscall.Errno(232) - cERROR_PIPE_CONNECTED = syscall.Errno(535) - cERROR_SEM_TIMEOUT = syscall.Errno(121) - - cSECURITY_SQOS_PRESENT = 0x100000 - cSECURITY_ANONYMOUS = 0 - - cPIPE_TYPE_MESSAGE = 4 - - cPIPE_READMODE_MESSAGE = 2 - - cFILE_OPEN = 1 - cFILE_CREATE = 2 - - cFILE_PIPE_MESSAGE_TYPE = 1 - cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 - - cSE_DACL_PRESENT = 4 -) - var ( // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed. - // This error should match net.errClosing since docker takes a dependency on its text. - ErrPipeListenerClosed = errors.New("use of closed network connection") + ErrPipeListenerClosed = net.ErrClosed errPipeWriteClosed = errors.New("pipe has been closed for write") ) @@ -116,9 +96,10 @@ func (f *win32Pipe) RemoteAddr() net.Addr { } func (f *win32Pipe) SetDeadline(t time.Time) error { - f.SetReadDeadline(t) - f.SetWriteDeadline(t) - return nil + if err := f.SetReadDeadline(t); err != nil { + return err + } + return f.SetWriteDeadline(t) } // CloseWrite closes the write side of a message pipe in byte mode. @@ -157,14 +138,14 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { return 0, io.EOF } n, err := f.win32File.Read(b) - if err == io.EOF { + if err == io.EOF { //nolint:errorlint // If this was the result of a zero-byte read, then // it is possible that the read was due to a zero-size // message. Since we are simulating CloseWrite with a // zero-byte message, ensure that all future Read() calls // also return EOF. f.readEOF = true - } else if err == syscall.ERROR_MORE_DATA { + } else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno // ERROR_MORE_DATA indicates that the pipe's read mode is message mode // and the message still has more bytes. Treat this as a success, since // this package presents all named pipes as byte streams. @@ -173,7 +154,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { return n, err } -func (s pipeAddress) Network() string { +func (pipeAddress) Network() string { return "pipe" } @@ -184,16 +165,21 @@ func (s pipeAddress) String() string { // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) { for { - select { case <-ctx.Done(): return syscall.Handle(0), ctx.Err() default: - h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) + h, err := createFile(*path, + access, + 0, + nil, + syscall.OPEN_EXISTING, + windows.FILE_FLAG_OVERLAPPED|windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS, + 0) if err == nil { return h, nil } - if err != cERROR_PIPE_BUSY { + if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno return h, &os.PathError{Err: err, Op: "open", Path: *path} } // Wait 10 msec and try again. This is a rather simplistic @@ -213,9 +199,10 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { } else { absTimeout = time.Now().Add(2 * time.Second) } - ctx, _ := context.WithDeadline(context.Background(), absTimeout) + ctx, cancel := context.WithDeadline(context.Background(), absTimeout) + defer cancel() conn, err := DialPipeContext(ctx, path) - if err == context.DeadlineExceeded { + if errors.Is(err, context.DeadlineExceeded) { return nil, ErrTimeout } return conn, err @@ -251,7 +238,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, // If the pipe is in message mode, return a message byte pipe, which // supports CloseWrite(). - if flags&cPIPE_TYPE_MESSAGE != 0 { + if flags&windows.PIPE_TYPE_MESSAGE != 0 { return &win32MessageBytePipe{ win32Pipe: win32Pipe{win32File: f, path: path}, }, nil @@ -283,7 +270,11 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy oa.Length = unsafe.Sizeof(oa) var ntPath unicodeString - if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { + if err := rtlDosPathNameToNtPathName(&path16[0], + &ntPath, + 0, + 0, + ).Err(); err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } defer localFree(ntPath.Buffer) @@ -292,8 +283,8 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy // The security descriptor is only needed for the first pipe. if first { if sd != nil { - len := uint32(len(sd)) - sdb := localAlloc(0, len) + l := uint32(len(sd)) + sdb := localAlloc(0, l) defer localFree(sdb) copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) @@ -301,28 +292,28 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy // Construct the default named pipe security descriptor. var dacl uintptr if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { - return 0, fmt.Errorf("getting default named pipe ACL: %s", err) + return 0, fmt.Errorf("getting default named pipe ACL: %w", err) } defer localFree(dacl) sdb := &securityDescriptor{ Revision: 1, - Control: cSE_DACL_PRESENT, + Control: windows.SE_DACL_PRESENT, Dacl: dacl, } oa.SecurityDescriptor = sdb } } - typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) + typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS) if c.MessageMode { - typ |= cFILE_PIPE_MESSAGE_TYPE + typ |= windows.FILE_PIPE_MESSAGE_TYPE } - disposition := uint32(cFILE_OPEN) + disposition := uint32(windows.FILE_OPEN) access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) if first { - disposition = cFILE_CREATE + disposition = windows.FILE_CREATE // By not asking for read or write access, the named pipe file system // will put this pipe into an initially disconnected state, blocking // client connections until the next call with first == false. @@ -335,7 +326,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy h syscall.Handle iosb ioStatusBlock ) - err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() + err = ntCreateNamedPipeFile(&h, + access, + &oa, + &iosb, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, + disposition, + 0, + typ, + 0, + 0, + 0xffffffff, + uint32(c.InputBufferSize), + uint32(c.OutputBufferSize), + &timeout).Err() if err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } @@ -380,7 +384,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { p.Close() p = nil err = <-ch - if err == nil || err == ErrFileClosed { + if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno err = ErrPipeListenerClosed } } @@ -402,12 +406,12 @@ func (l *win32PipeListener) listenerRoutine() { p, err = l.makeConnectedServerPipe() // If the connection was immediately closed by the client, try // again. - if err != cERROR_NO_DATA { + if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno break } } responseCh <- acceptResponse{p, err} - closed = err == ErrPipeListenerClosed + closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno } } syscall.Close(l.firstHandle) @@ -469,15 +473,15 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { } func connectPipe(p *win32File) error { - c, err := p.prepareIo() + c, err := p.prepareIO() if err != nil { return err } defer p.wg.Done() err = connectNamedPipe(p.handle, &c.o) - _, err = p.asyncIo(c, nil, 0, err) - if err != nil && err != cERROR_PIPE_CONNECTED { + _, err = p.asyncIO(c, nil, 0, err) + if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno return err } return nil diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go index 2d9161e2..48ce4e92 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go @@ -1,5 +1,3 @@ -// +build windows - // Package guid provides a GUID type. The backing structure for a GUID is // identical to that used by the golang.org/x/sys/windows GUID type. // There are two main binary encodings used for a GUID, the big-endian encoding, @@ -9,24 +7,26 @@ package guid import ( "crypto/rand" - "crypto/sha1" + "crypto/sha1" //nolint:gosec // not used for secure application "encoding" "encoding/binary" "fmt" "strconv" ) +//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment + // Variant specifies which GUID variant (or "type") of the GUID. It determines // how the entirety of the rest of the GUID is interpreted. type Variant uint8 -// The variants specified by RFC 4122. +// The variants specified by RFC 4122 section 4.1.1. const ( // VariantUnknown specifies a GUID variant which does not conform to one of // the variant encodings specified in RFC 4122. VariantUnknown Variant = iota VariantNCS - VariantRFC4122 + VariantRFC4122 // RFC 4122 VariantMicrosoft VariantFuture ) @@ -36,6 +36,10 @@ const ( // hash of an input string. type Version uint8 +func (v Version) String() string { + return strconv.FormatUint(uint64(v), 10) +} + var _ = (encoding.TextMarshaler)(GUID{}) var _ = (encoding.TextUnmarshaler)(&GUID{}) @@ -61,7 +65,7 @@ func NewV4() (GUID, error) { // big-endian UTF16 stream of bytes. If that is desired, the string can be // encoded as such before being passed to this function. func NewV5(namespace GUID, name []byte) (GUID, error) { - b := sha1.New() + b := sha1.New() //nolint:gosec // not used for secure application namespaceBytes := namespace.ToArray() b.Write(namespaceBytes[:]) b.Write(name) diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go index f64d828c..805bd354 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package guid diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go index 83617f4e..27e45ee5 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package guid import "golang.org/x/sys/windows" diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go new file mode 100644 index 00000000..4076d313 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT. + +package guid + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[VariantUnknown-0] + _ = x[VariantNCS-1] + _ = x[VariantRFC4122-2] + _ = x[VariantMicrosoft-3] + _ = x[VariantFuture-4] +} + +const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture" + +var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33} + +func (i Variant) String() string { + if i >= Variant(len(_Variant_index)-1) { + return "Variant(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Variant_name[_Variant_index[i]:_Variant_index[i+1]] +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go index 60292078..6df87b74 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package security @@ -20,6 +21,7 @@ type ( trusteeForm uint32 trusteeType uint32 + //nolint:structcheck // structcheck thinks fields are unused, but the are used to pass data to OS explicitAccess struct { accessPermissions accessMask accessMode accessMode @@ -27,6 +29,7 @@ type ( trustee trustee } + //nolint:structcheck,unused // structcheck thinks fields are unused, but the are used to pass data to OS trustee struct { multipleTrustee *trustee multipleTrusteeOperation int32 @@ -44,6 +47,7 @@ const ( desiredAccessReadControl desiredAccess = 0x20000 desiredAccessWriteDac desiredAccess = 0x40000 + //cspell:disable-next-line gvmga = "GrantVmGroupAccess:" inheritModeNoInheritance inheritMode = 0x0 @@ -56,9 +60,9 @@ const ( shareModeRead shareMode = 0x1 shareModeWrite shareMode = 0x2 - sidVmGroup = "S-1-5-83-0" + sidVMGroup = "S-1-5-83-0" - trusteeFormIsSid trusteeForm = 0 + trusteeFormIsSID trusteeForm = 0 trusteeTypeWellKnownGroup trusteeType = 5 ) @@ -67,6 +71,8 @@ const ( // include Grant ACE entries for the VM Group SID. This is a golang re- // implementation of the same function in vmcompute, just not exported in // RS5. Which kind of sucks. Sucks a lot :/ +// +//revive:disable-next-line:var-naming VM, not Vm func GrantVmGroupAccess(name string) error { // Stat (to determine if `name` is a directory). s, err := os.Stat(name) @@ -79,7 +85,7 @@ func GrantVmGroupAccess(name string) error { if err != nil { return err // Already wrapped } - defer syscall.CloseHandle(fd) + defer syscall.CloseHandle(fd) //nolint:errcheck // Get the current DACL and Security Descriptor. Must defer LocalFree on success. ot := objectTypeFileObject @@ -89,7 +95,7 @@ func GrantVmGroupAccess(name string) error { if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err) } - defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) //nolint:errcheck // Generate a new DACL which is the current DACL with the required ACEs added. // Must defer LocalFree on success. @@ -97,7 +103,7 @@ func GrantVmGroupAccess(name string) error { if err != nil { return err // Already wrapped } - defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) //nolint:errcheck // And finally use SetSecurityInfo to apply the updated DACL. if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { @@ -110,16 +116,19 @@ func GrantVmGroupAccess(name string) error { // createFile is a helper function to call [Nt]CreateFile to get a handle to // the file or directory. func createFile(name string, isDir bool) (syscall.Handle, error) { - namep := syscall.StringToUTF16(name) + namep, err := syscall.UTF16FromString(name) + if err != nil { + return syscall.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err) + } da := uint32(desiredAccessReadControl | desiredAccessWriteDac) sm := uint32(shareModeRead | shareModeWrite) fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) if isDir { - fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) + fa |= syscall.FILE_FLAG_BACKUP_SEMANTICS } fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) if err != nil { - return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) + return syscall.InvalidHandle, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) } return fd, nil } @@ -128,9 +137,9 @@ func createFile(name string, isDir bool) (syscall.Handle, error) { // The caller is responsible for LocalFree of the returned DACL on success. func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { // Generate pointers to the SIDs based on the string SIDs - sid, err := syscall.StringToSid(sidVmGroup) + sid, err := syscall.StringToSid(sidVMGroup) if err != nil { - return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err) + return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err) } inheritance := inheritModeNoInheritance @@ -139,12 +148,12 @@ func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintp } eaArray := []explicitAccess{ - explicitAccess{ + { accessPermissions: accessMaskDesiredPermission, accessMode: accessModeGrant, inheritance: inheritance, trustee: trustee{ - trusteeForm: trusteeFormIsSid, + trusteeForm: trusteeFormIsSID, trusteeType: trusteeTypeWellKnownGroup, name: uintptr(unsafe.Pointer(sid)), }, diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go index d7096716..71326e4e 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go @@ -1,6 +1,6 @@ package security -//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go syscall_windows.go //sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) = advapi32.GetSecurityInfo //sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) = advapi32.SetSecurityInfo diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go index 4084680e..26c986b8 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go @@ -1,4 +1,6 @@ -// Code generated by 'go generate'; DO NOT EDIT. +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package security diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go index c3dd7c21..0ff9dac9 100644 --- a/vendor/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -24,22 +25,17 @@ import ( //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW const ( - SE_PRIVILEGE_ENABLED = 2 + //revive:disable-next-line:var-naming ALL_CAPS + SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED - ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 + //revive:disable-next-line:var-naming ALL_CAPS + ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED SeBackupPrivilege = "SeBackupPrivilege" SeRestorePrivilege = "SeRestorePrivilege" SeSecurityPrivilege = "SeSecurityPrivilege" ) -const ( - securityAnonymous = iota - securityIdentification - securityImpersonation - securityDelegation -) - var ( privNames = make(map[string]uint64) privNameMutex sync.Mutex @@ -51,11 +47,9 @@ type PrivilegeError struct { } func (e *PrivilegeError) Error() string { - s := "" + s := "Could not enable privilege " if len(e.privileges) > 1 { s = "Could not enable privileges " - } else { - s = "Could not enable privilege " } for i, p := range e.privileges { if i != 0 { @@ -94,7 +88,7 @@ func RunWithPrivileges(names []string, fn func() error) error { } func mapPrivileges(names []string) ([]uint64, error) { - var privileges []uint64 + privileges := make([]uint64, 0, len(names)) privNameMutex.Lock() defer privNameMutex.Unlock() for _, name := range names { @@ -127,7 +121,7 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { return err } - p, _ := windows.GetCurrentProcess() + p := windows.CurrentProcess() var token windows.Token err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) if err != nil { @@ -140,10 +134,10 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { var b bytes.Buffer - binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) + _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) for _, p := range privileges { - binary.Write(&b, binary.LittleEndian, p) - binary.Write(&b, binary.LittleEndian, action) + _ = binary.Write(&b, binary.LittleEndian, p) + _ = binary.Write(&b, binary.LittleEndian, action) } prevState := make([]byte, b.Len()) reqSize := uint32(0) @@ -151,7 +145,7 @@ func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) e if !success { return err } - if err == ERROR_NOT_ALL_ASSIGNED { + if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno return &PrivilegeError{privileges} } return nil @@ -177,7 +171,7 @@ func getPrivilegeName(luid uint64) string { } func newThreadToken() (windows.Token, error) { - err := impersonateSelf(securityImpersonation) + err := impersonateSelf(windows.SecurityImpersonation) if err != nil { return 0, err } diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go index fc1ee4d3..67d1a104 100644 --- a/vendor/github.com/Microsoft/go-winio/reparse.go +++ b/vendor/github.com/Microsoft/go-winio/reparse.go @@ -1,3 +1,6 @@ +//go:build windows +// +build windows + package winio import ( @@ -113,16 +116,16 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte { } var b bytes.Buffer - binary.Write(&b, binary.LittleEndian, &data) + _ = binary.Write(&b, binary.LittleEndian, &data) if !rp.IsMountPoint { flags := uint32(0) if relative { flags |= 1 } - binary.Write(&b, binary.LittleEndian, flags) + _ = binary.Write(&b, binary.LittleEndian, flags) } - binary.Write(&b, binary.LittleEndian, ntTarget16) - binary.Write(&b, binary.LittleEndian, target16) + _ = binary.Write(&b, binary.LittleEndian, ntTarget16) + _ = binary.Write(&b, binary.LittleEndian, target16) return b.Bytes() } diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go index db1b370a..5550ef6b 100644 --- a/vendor/github.com/Microsoft/go-winio/sd.go +++ b/vendor/github.com/Microsoft/go-winio/sd.go @@ -1,23 +1,25 @@ +//go:build windows // +build windows package winio import ( + "errors" "syscall" "unsafe" + + "golang.org/x/sys/windows" ) //sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW +//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW //sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW +//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW //sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW //sys localFree(mem uintptr) = LocalFree //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength -const ( - cERROR_NONE_MAPPED = syscall.Errno(1332) -) - type AccountLookupError struct { Name string Err error @@ -28,8 +30,10 @@ func (e *AccountLookupError) Error() string { return "lookup account: empty account name specified" } var s string - switch e.Err { - case cERROR_NONE_MAPPED: + switch { + case errors.Is(e.Err, windows.ERROR_INVALID_SID): + s = "the security ID structure is invalid" + case errors.Is(e.Err, windows.ERROR_NONE_MAPPED): s = "not found" default: s = e.Err.Error() @@ -37,6 +41,8 @@ func (e *AccountLookupError) Error() string { return "lookup account " + e.Name + ": " + s } +func (e *AccountLookupError) Unwrap() error { return e.Err } + type SddlConversionError struct { Sddl string Err error @@ -46,15 +52,19 @@ func (e *SddlConversionError) Error() string { return "convert " + e.Sddl + ": " + e.Err.Error() } +func (e *SddlConversionError) Unwrap() error { return e.Err } + // LookupSidByName looks up the SID of an account by name +// +//revive:disable-next-line:var-naming SID, not Sid func LookupSidByName(name string) (sid string, err error) { if name == "" { - return "", &AccountLookupError{name, cERROR_NONE_MAPPED} + return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED} } var sidSize, sidNameUse, refDomainSize uint32 err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) - if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { + if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno return "", &AccountLookupError{name, err} } sidBuffer := make([]byte, sidSize) @@ -73,6 +83,42 @@ func LookupSidByName(name string) (sid string, err error) { return sid, nil } +// LookupNameBySid looks up the name of an account by SID +// +//revive:disable-next-line:var-naming SID, not Sid +func LookupNameBySid(sid string) (name string, err error) { + if sid == "" { + return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED} + } + + sidBuffer, err := windows.UTF16PtrFromString(sid) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + var sidPtr *byte + if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil { + return "", &AccountLookupError{sid, err} + } + defer localFree(uintptr(unsafe.Pointer(sidPtr))) + + var nameSize, refDomainSize, sidNameUse uint32 + err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno + return "", &AccountLookupError{sid, err} + } + + nameBuffer := make([]uint16, nameSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + name = windows.UTF16ToString(nameBuffer) + return name, nil +} + func SddlToSecurityDescriptor(sddl string) ([]byte, error) { var sdBuffer uintptr err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) @@ -87,7 +133,7 @@ func SddlToSecurityDescriptor(sddl string) ([]byte, error) { func SecurityDescriptorToSddl(sd []byte) (string, error) { var sddl *uint16 - // The returned string length seems to including an aribtrary number of terminating NULs. + // The returned string length seems to include an arbitrary number of terminating NULs. // Don't use it. err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) if err != nil { diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go index 5955c99f..a6ca111b 100644 --- a/vendor/github.com/Microsoft/go-winio/syscall.go +++ b/vendor/github.com/Microsoft/go-winio/syscall.go @@ -1,3 +1,5 @@ +//go:build windows + package winio -//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go diff --git a/vendor/github.com/Microsoft/go-winio/tools.go b/vendor/github.com/Microsoft/go-winio/tools.go new file mode 100644 index 00000000..2aa04584 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/tools.go @@ -0,0 +1,5 @@ +//go:build tools + +package winio + +import _ "golang.org/x/tools/cmd/stringer" diff --git a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go index f7f78fc2..b54cad11 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go @@ -11,7 +11,7 @@ import ( "golang.org/x/sys/windows" ) -//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zvhd_windows.go vhd.go //sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk //sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk @@ -62,8 +62,8 @@ type OpenVirtualDiskParameters struct { Version2 OpenVersion2 } -// The higher level `OpenVersion2` struct uses bools to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However, -// the internal windows structure uses `BOOLS` aka int32s for these types. `openVersion2` is used for translating +// The higher level `OpenVersion2` struct uses `bool`s to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However, +// the internal windows structure uses `BOOL`s aka int32s for these types. `openVersion2` is used for translating // `OpenVersion2` fields to the correct windows internal field types on the `Open____` methods. type openVersion2 struct { getInfoOnly int32 @@ -87,9 +87,10 @@ type AttachVirtualDiskParameters struct { } const ( + //revive:disable-next-line:var-naming ALL_CAPS VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 - // Access Mask for opening a VHD + // Access Mask for opening a VHD. VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000 VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000 VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000 @@ -101,7 +102,7 @@ const ( VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000 VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000 - // Flags for creating a VHD + // Flags for creating a VHD. CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0 CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1 CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2 @@ -109,12 +110,12 @@ const ( CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8 CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10 CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20 - CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40 + CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40 //revive:disable-line:var-naming VHD, not Vhd CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80 - CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100 + CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100 //revive:disable-line:var-naming PMEM, not Pmem CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200 - // Flags for opening a VHD + // Flags for opening a VHD. OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000 OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001 OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002 @@ -127,7 +128,7 @@ const ( OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100 OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200 - // Flags for attaching a VHD + // Flags for attaching a VHD. AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000 AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001 AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002 @@ -140,12 +141,14 @@ const ( AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100 AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200 - // Flags for detaching a VHD + // Flags for detaching a VHD. DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 ) // CreateVhdx is a helper function to create a simple vhdx file at the given path using // default values. +// +//revive:disable-next-line:var-naming VHDX, not Vhdx func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { params := CreateVirtualDiskParameters{ Version: 2, @@ -172,6 +175,8 @@ func DetachVirtualDisk(handle syscall.Handle) (err error) { } // DetachVhd detaches a vhd found at `path`. +// +//revive:disable-next-line:var-naming VHD, not Vhd func DetachVhd(path string) error { handle, err := OpenVirtualDisk( path, @@ -181,12 +186,16 @@ func DetachVhd(path string) error { if err != nil { return err } - defer syscall.CloseHandle(handle) + defer syscall.CloseHandle(handle) //nolint:errcheck return DetachVirtualDisk(handle) } // AttachVirtualDisk attaches a virtual hard disk for use. -func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) { +func AttachVirtualDisk( + handle syscall.Handle, + attachVirtualDiskFlag AttachVirtualDiskFlag, + parameters *AttachVirtualDiskParameters, +) (err error) { // Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5. if err := attachVirtualDisk( handle, @@ -203,6 +212,8 @@ func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtua // AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2 // of the ATTACH_VIRTUAL_DISK_PARAMETERS. +// +//revive:disable-next-line:var-naming VHD, not Vhd func AttachVhd(path string) (err error) { handle, err := OpenVirtualDisk( path, @@ -213,7 +224,7 @@ func AttachVhd(path string) (err error) { return err } - defer syscall.CloseHandle(handle) + defer syscall.CloseHandle(handle) //nolint:errcheck params := AttachVirtualDiskParameters{Version: 2} if err := AttachVirtualDisk( handle, @@ -226,7 +237,11 @@ func AttachVhd(path string) (err error) { } // OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags. -func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) { +func OpenVirtualDisk( + vhdPath string, + virtualDiskAccessMask VirtualDiskAccessMask, + openVirtualDiskFlags VirtualDiskFlag, +) (syscall.Handle, error) { parameters := OpenVirtualDiskParameters{Version: 2} handle, err := OpenVirtualDiskWithParameters( vhdPath, @@ -241,7 +256,12 @@ func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask } // OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters. -func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) { +func OpenVirtualDiskWithParameters( + vhdPath string, + virtualDiskAccessMask VirtualDiskAccessMask, + openVirtualDiskFlags VirtualDiskFlag, + parameters *OpenVirtualDiskParameters, +) (syscall.Handle, error) { var ( handle syscall.Handle defaultType VirtualStorageType @@ -279,7 +299,12 @@ func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask Virtual } // CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk. -func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) { +func CreateVirtualDisk( + path string, + virtualDiskAccessMask VirtualDiskAccessMask, + createVirtualDiskFlags CreateVirtualDiskFlag, + parameters *CreateVirtualDiskParameters, +) (syscall.Handle, error) { var ( handle syscall.Handle defaultType VirtualStorageType @@ -323,6 +348,8 @@ func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { } // CreateDiffVhd is a helper function to create a differencing virtual disk. +// +//revive:disable-next-line:var-naming VHD, not Vhd func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error { // Setting `ParentPath` is how to signal to create a differencing disk. createParams := &CreateVirtualDiskParameters{ diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go index 1d7498db..d0e917d2 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go @@ -1,4 +1,6 @@ -// Code generated by 'go generate'; DO NOT EDIT. +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package vhd diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go index 176ff75e..83f45a13 100644 --- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -1,4 +1,6 @@ -// Code generated by 'go generate'; DO NOT EDIT. +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package winio @@ -47,9 +49,11 @@ var ( procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") + procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") @@ -74,7 +78,6 @@ var ( procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") - procbind = modws2_32.NewProc("bind") ) func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { @@ -123,6 +126,14 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision return } +func convertStringSidToSid(str *uint16, sid **byte) (err error) { + r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func getSecurityDescriptorLength(sd uintptr) (len uint32) { r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) len = uint32(r0) @@ -154,6 +165,14 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS return } +func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(systemName) @@ -380,25 +399,25 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro return } -func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { +func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) { r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { +func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { +func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) - status = ntstatus(r0) + status = ntStatus(r0) return } -func rtlNtStatusToDosError(status ntstatus) (winerr error) { +func rtlNtStatusToDosError(status ntStatus) (winerr error) { r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) if r0 != 0 { winerr = syscall.Errno(r0) @@ -417,11 +436,3 @@ func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint } return } - -func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { - r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if r1 == socketError { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go index 27a62a72..f46af33b 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go @@ -86,6 +86,12 @@ type Container interface { // container to be terminated by some error condition (including calling // Close). Wait() error + // WaitChannel returns the wait channel of the container + WaitChannel() <-chan struct{} + // WaitError returns the container termination error. + // This function should only be called after the channel in WaitChannel() + // is closed. Otherwise it is not thread safe. + WaitError() error // Modify sends a request to modify container resources Modify(ctx context.Context, config interface{}) error } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go index e21354ff..295d4b84 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -154,7 +154,7 @@ func (e *HcsError) Error() string { func (e *HcsError) Temporary() bool { err, ok := e.Err.(net.Error) - return ok && err.Temporary() + return ok && err.Temporary() //nolint:staticcheck } func (e *HcsError) Timeout() bool { @@ -193,7 +193,7 @@ func (e *SystemError) Error() string { func (e *SystemError) Temporary() bool { err, ok := e.Err.(net.Error) - return ok && err.Temporary() + return ok && err.Temporary() //nolint:staticcheck } func (e *SystemError) Timeout() bool { @@ -224,7 +224,7 @@ func (e *ProcessError) Error() string { func (e *ProcessError) Temporary() bool { err, ok := e.Err.(net.Error) - return ok && err.Temporary() + return ok && err.Temporary() //nolint:staticcheck } func (e *ProcessError) Timeout() bool { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go index 1d45a703..a76f6b25 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -287,11 +287,19 @@ func (computeSystem *System) waitBackground() { oc.SetSpanStatus(span, err) } +func (computeSystem *System) WaitChannel() <-chan struct{} { + return computeSystem.waitBlock +} + +func (computeSystem *System) WaitError() error { + return computeSystem.waitError +} + // Wait synchronously waits for the compute system to shutdown or terminate. If // the compute system has already exited returns the previous error (if any). func (computeSystem *System) Wait() error { - <-computeSystem.waitBlock - return computeSystem.waitError + <-computeSystem.WaitChannel() + return computeSystem.WaitError() } // ExitError returns an error describing the reason the compute system terminated. diff --git a/vendor/github.com/asaskevich/govalidator/.gitignore b/vendor/github.com/asaskevich/govalidator/.gitignore new file mode 100644 index 00000000..8d69a941 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/.gitignore @@ -0,0 +1,15 @@ +bin/ +.idea/ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + diff --git a/vendor/github.com/asaskevich/govalidator/.travis.yml b/vendor/github.com/asaskevich/govalidator/.travis.yml new file mode 100644 index 00000000..bb83c667 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/.travis.yml @@ -0,0 +1,12 @@ +language: go +dist: xenial +go: + - '1.10' + - '1.11' + - '1.12' + - '1.13' + - 'tip' + +script: + - go test -coverpkg=./... -coverprofile=coverage.info -timeout=5s + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md b/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..4b462b0d --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md @@ -0,0 +1,43 @@ +# Contributor Code of Conduct + +This project adheres to [The Code Manifesto](http://codemanifesto.com) +as its guidelines for contributor interactions. + +## The Code Manifesto + +We want to work in an ecosystem that empowers developers to reach their +potential — one that encourages growth and effective collaboration. A space +that is safe for all. + +A space such as this benefits everyone that participates in it. It encourages +new developers to enter our field. It is through discussion and collaboration +that we grow, and through growth that we improve. + +In the effort to create such a place, we hold to these values: + +1. **Discrimination limits us.** This includes discrimination on the basis of + race, gender, sexual orientation, gender identity, age, nationality, + technology and any other arbitrary exclusion of a group of people. +2. **Boundaries honor us.** Your comfort levels are not everyone’s comfort + levels. Remember that, and if brought to your attention, heed it. +3. **We are our biggest assets.** None of us were born masters of our trade. + Each of us has been helped along the way. Return that favor, when and where + you can. +4. **We are resources for the future.** As an extension of #3, share what you + know. Make yourself a resource to help those that come after you. +5. **Respect defines us.** Treat others as you wish to be treated. Make your + discussions, criticisms and debates from a position of respectfulness. Ask + yourself, is it true? Is it necessary? Is it constructive? Anything less is + unacceptable. +6. **Reactions require grace.** Angry responses are valid, but abusive language + and vindictive actions are toxic. When something happens that offends you, + handle it assertively, but be respectful. Escalate reasonably, and try to + allow the offender an opportunity to explain themselves, and possibly + correct the issue. +7. **Opinions are just that: opinions.** Each and every one of us, due to our + background and upbringing, have varying opinions. That is perfectly + acceptable. Remember this: if you respect your own opinions, you should + respect the opinions of others. +8. **To err is human.** You might not intend it, but mistakes do happen and + contribute to build experience. Tolerate honest mistakes, and don't + hesitate to apologize if you make one yourself. diff --git a/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md b/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md new file mode 100644 index 00000000..7ed268a1 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md @@ -0,0 +1,63 @@ +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements + +## Financial contributions + +We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/govalidator). +Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. + + +## Credits + + +### Contributors + +Thank you to all the people who have already contributed to govalidator! + + + +### Backers + +Thank you to all our backers! [[Become a backer](https://opencollective.com/govalidator#backer)] + + + + +### Sponsors + +Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/govalidator#sponsor)) + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/LICENSE b/vendor/github.com/asaskevich/govalidator/LICENSE new file mode 100644 index 00000000..cacba910 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2020 Alex Saskevich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/README.md b/vendor/github.com/asaskevich/govalidator/README.md new file mode 100644 index 00000000..2c3fc35e --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/README.md @@ -0,0 +1,622 @@ +govalidator +=========== +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) +[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) +[![Coverage](https://codecov.io/gh/asaskevich/govalidator/branch/master/graph/badge.svg)](https://codecov.io/gh/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [![Backers on Open Collective](https://opencollective.com/govalidator/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/govalidator/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_shield) + +A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). + +#### Installation +Make sure that Go is installed on your computer. +Type the following command in your terminal: + + go get github.com/asaskevich/govalidator + +or you can get specified release of the package with `gopkg.in`: + + go get gopkg.in/asaskevich/govalidator.v10 + +After it the package is ready to use. + + +#### Import package in your project +Add following line in your `*.go` file: +```go +import "github.com/asaskevich/govalidator" +``` +If you are unhappy to use long `govalidator`, you can do something like this: +```go +import ( + valid "github.com/asaskevich/govalidator" +) +``` + +#### Activate behavior to require all fields have a validation tag by default +`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function. + +`SetNilPtrAllowedByRequired` causes validation to pass when struct fields marked by `required` are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between `nil` and `zero value` state can use this. If disabled, both `nil` and `zero` values cause validation errors. + +```go +import "github.com/asaskevich/govalidator" + +func init() { + govalidator.SetFieldsRequiredByDefault(true) +} +``` + +Here's some code to explain it: +```go +// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +type exampleStruct struct { + Name string `` + Email string `valid:"email"` +} + +// this, however, will only fail when Email is empty or an invalid email address: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email"` +} + +// lastly, this will only fail when Email is an invalid email address but not when it's empty: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email,optional"` +} +``` + +#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123)) +##### Custom validator function signature +A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible. +```go +import "github.com/asaskevich/govalidator" + +// old signature +func(i interface{}) bool + +// new signature +func(i interface{}, o interface{}) bool +``` + +##### Adding a custom validator +This was changed to prevent data races when accessing custom validators. +```go +import "github.com/asaskevich/govalidator" + +// before +govalidator.CustomTypeTagMap["customByteArrayValidator"] = func(i interface{}, o interface{}) bool { + // ... +} + +// after +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, o interface{}) bool { + // ... +}) +``` + +#### List of functions: +```go +func Abs(value float64) float64 +func BlackList(str, chars string) string +func ByteLength(str string, params ...string) bool +func CamelCaseToUnderscore(str string) string +func Contains(str, substring string) bool +func Count(array []interface{}, iterator ConditionIterator) int +func Each(array []interface{}, iterator Iterator) +func ErrorByField(e error, field string) string +func ErrorsByField(e error) map[string]string +func Filter(array []interface{}, iterator ConditionIterator) []interface{} +func Find(array []interface{}, iterator ConditionIterator) interface{} +func GetLine(s string, index int) (string, error) +func GetLines(s string) []string +func HasLowerCase(str string) bool +func HasUpperCase(str string) bool +func HasWhitespace(str string) bool +func HasWhitespaceOnly(str string) bool +func InRange(value interface{}, left interface{}, right interface{}) bool +func InRangeFloat32(value, left, right float32) bool +func InRangeFloat64(value, left, right float64) bool +func InRangeInt(value, left, right interface{}) bool +func IsASCII(str string) bool +func IsAlpha(str string) bool +func IsAlphanumeric(str string) bool +func IsBase64(str string) bool +func IsByteLength(str string, min, max int) bool +func IsCIDR(str string) bool +func IsCRC32(str string) bool +func IsCRC32b(str string) bool +func IsCreditCard(str string) bool +func IsDNSName(str string) bool +func IsDataURI(str string) bool +func IsDialString(str string) bool +func IsDivisibleBy(str, num string) bool +func IsEmail(str string) bool +func IsExistingEmail(email string) bool +func IsFilePath(str string) (bool, int) +func IsFloat(str string) bool +func IsFullWidth(str string) bool +func IsHalfWidth(str string) bool +func IsHash(str string, algorithm string) bool +func IsHexadecimal(str string) bool +func IsHexcolor(str string) bool +func IsHost(str string) bool +func IsIP(str string) bool +func IsIPv4(str string) bool +func IsIPv6(str string) bool +func IsISBN(str string, version int) bool +func IsISBN10(str string) bool +func IsISBN13(str string) bool +func IsISO3166Alpha2(str string) bool +func IsISO3166Alpha3(str string) bool +func IsISO4217(str string) bool +func IsISO693Alpha2(str string) bool +func IsISO693Alpha3b(str string) bool +func IsIn(str string, params ...string) bool +func IsInRaw(str string, params ...string) bool +func IsInt(str string) bool +func IsJSON(str string) bool +func IsLatitude(str string) bool +func IsLongitude(str string) bool +func IsLowerCase(str string) bool +func IsMAC(str string) bool +func IsMD4(str string) bool +func IsMD5(str string) bool +func IsMagnetURI(str string) bool +func IsMongoID(str string) bool +func IsMultibyte(str string) bool +func IsNatural(value float64) bool +func IsNegative(value float64) bool +func IsNonNegative(value float64) bool +func IsNonPositive(value float64) bool +func IsNotNull(str string) bool +func IsNull(str string) bool +func IsNumeric(str string) bool +func IsPort(str string) bool +func IsPositive(value float64) bool +func IsPrintableASCII(str string) bool +func IsRFC3339(str string) bool +func IsRFC3339WithoutZone(str string) bool +func IsRGBcolor(str string) bool +func IsRegex(str string) bool +func IsRequestURI(rawurl string) bool +func IsRequestURL(rawurl string) bool +func IsRipeMD128(str string) bool +func IsRipeMD160(str string) bool +func IsRsaPub(str string, params ...string) bool +func IsRsaPublicKey(str string, keylen int) bool +func IsSHA1(str string) bool +func IsSHA256(str string) bool +func IsSHA384(str string) bool +func IsSHA512(str string) bool +func IsSSN(str string) bool +func IsSemver(str string) bool +func IsTiger128(str string) bool +func IsTiger160(str string) bool +func IsTiger192(str string) bool +func IsTime(str string, format string) bool +func IsType(v interface{}, params ...string) bool +func IsURL(str string) bool +func IsUTFDigit(str string) bool +func IsUTFLetter(str string) bool +func IsUTFLetterNumeric(str string) bool +func IsUTFNumeric(str string) bool +func IsUUID(str string) bool +func IsUUIDv3(str string) bool +func IsUUIDv4(str string) bool +func IsUUIDv5(str string) bool +func IsULID(str string) bool +func IsUnixTime(str string) bool +func IsUpperCase(str string) bool +func IsVariableWidth(str string) bool +func IsWhole(value float64) bool +func LeftTrim(str, chars string) string +func Map(array []interface{}, iterator ResultIterator) []interface{} +func Matches(str, pattern string) bool +func MaxStringLength(str string, params ...string) bool +func MinStringLength(str string, params ...string) bool +func NormalizeEmail(str string) (string, error) +func PadBoth(str string, padStr string, padLen int) string +func PadLeft(str string, padStr string, padLen int) string +func PadRight(str string, padStr string, padLen int) string +func PrependPathToErrors(err error, path string) error +func Range(str string, params ...string) bool +func RemoveTags(s string) string +func ReplacePattern(str, pattern, replace string) string +func Reverse(s string) string +func RightTrim(str, chars string) string +func RuneLength(str string, params ...string) bool +func SafeFileName(str string) string +func SetFieldsRequiredByDefault(value bool) +func SetNilPtrAllowedByRequired(value bool) +func Sign(value float64) float64 +func StringLength(str string, params ...string) bool +func StringMatches(s string, params ...string) bool +func StripLow(str string, keepNewLines bool) string +func ToBoolean(str string) (bool, error) +func ToFloat(str string) (float64, error) +func ToInt(value interface{}) (res int64, err error) +func ToJSON(obj interface{}) (string, error) +func ToString(obj interface{}) string +func Trim(str, chars string) string +func Truncate(str string, length int, ending string) string +func TruncatingErrorf(str string, args ...interface{}) error +func UnderscoreToCamelCase(s string) string +func ValidateMap(inputMap map[string]interface{}, validationMap map[string]interface{}) (bool, error) +func ValidateStruct(s interface{}) (bool, error) +func WhiteList(str, chars string) string +type ConditionIterator +type CustomTypeValidator +type Error +func (e Error) Error() string +type Errors +func (es Errors) Error() string +func (es Errors) Errors() []error +type ISO3166Entry +type ISO693Entry +type InterfaceParamValidator +type Iterator +type ParamValidator +type ResultIterator +type UnsupportedTypeError +func (e *UnsupportedTypeError) Error() string +type Validator +``` + +#### Examples +###### IsURL +```go +println(govalidator.IsURL(`http://user@pass:domain.com/path/page`)) +``` +###### IsType +```go +println(govalidator.IsType("Bob", "string")) +println(govalidator.IsType(1, "int")) +i := 1 +println(govalidator.IsType(&i, "*int")) +``` + +IsType can be used through the tag `type` which is essential for map validation: +```go +type User struct { + Name string `valid:"type(string)"` + Age int `valid:"type(int)"` + Meta interface{} `valid:"type(string)"` +} +result, err := govalidator.ValidateStruct(User{"Bob", 20, "meta"}) +if err != nil { + println("error: " + err.Error()) +} +println(result) +``` +###### ToString +```go +type User struct { + FirstName string + LastName string +} + +str := govalidator.ToString(&User{"John", "Juan"}) +println(str) +``` +###### Each, Map, Filter, Count for slices +Each iterates over the slice/array and calls Iterator for every item +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.Iterator = func(value interface{}, index int) { + println(value.(int)) +} +govalidator.Each(data, fn) +``` +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} { + return value.(int) * 3 +} +_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} +``` +```go +data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +var fn govalidator.ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 +} +_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} +_ = govalidator.Count(data, fn) // result = 5 +``` +###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2) +If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this: +```go +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) +``` +For completely custom validators (interface-based), see below. + +Here is a list of available validators for struct fields (validator - used function): +```go +"email": IsEmail, +"url": IsURL, +"dialstring": IsDialString, +"requrl": IsRequestURL, +"requri": IsRequestURI, +"alpha": IsAlpha, +"utfletter": IsUTFLetter, +"alphanum": IsAlphanumeric, +"utfletternum": IsUTFLetterNumeric, +"numeric": IsNumeric, +"utfnumeric": IsUTFNumeric, +"utfdigit": IsUTFDigit, +"hexadecimal": IsHexadecimal, +"hexcolor": IsHexcolor, +"rgbcolor": IsRGBcolor, +"lowercase": IsLowerCase, +"uppercase": IsUpperCase, +"int": IsInt, +"float": IsFloat, +"null": IsNull, +"uuid": IsUUID, +"uuidv3": IsUUIDv3, +"uuidv4": IsUUIDv4, +"uuidv5": IsUUIDv5, +"creditcard": IsCreditCard, +"isbn10": IsISBN10, +"isbn13": IsISBN13, +"json": IsJSON, +"multibyte": IsMultibyte, +"ascii": IsASCII, +"printableascii": IsPrintableASCII, +"fullwidth": IsFullWidth, +"halfwidth": IsHalfWidth, +"variablewidth": IsVariableWidth, +"base64": IsBase64, +"datauri": IsDataURI, +"ip": IsIP, +"port": IsPort, +"ipv4": IsIPv4, +"ipv6": IsIPv6, +"dns": IsDNSName, +"host": IsHost, +"mac": IsMAC, +"latitude": IsLatitude, +"longitude": IsLongitude, +"ssn": IsSSN, +"semver": IsSemver, +"rfc3339": IsRFC3339, +"rfc3339WithoutZone": IsRFC3339WithoutZone, +"ISO3166Alpha2": IsISO3166Alpha2, +"ISO3166Alpha3": IsISO3166Alpha3, +"ulid": IsULID, +``` +Validators with parameters + +```go +"range(min|max)": Range, +"length(min|max)": ByteLength, +"runelength(min|max)": RuneLength, +"stringlength(min|max)": StringLength, +"matches(pattern)": StringMatches, +"in(string1|string2|...|stringN)": IsIn, +"rsapub(keylength)" : IsRsaPub, +"minstringlength(int): MinStringLength, +"maxstringlength(int): MaxStringLength, +``` +Validators with parameters for any type + +```go +"type(type)": IsType, +``` + +And here is small example of usage: +```go +type Post struct { + Title string `valid:"alphanum,required"` + Message string `valid:"duck,ascii"` + Message2 string `valid:"animal(dog)"` + AuthorIP string `valid:"ipv4"` + Date string `valid:"-"` +} +post := &Post{ + Title: "My Example Post", + Message: "duck", + Message2: "dog", + AuthorIP: "123.234.54.3", +} + +// Add your own struct validation tags +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) + +// Add your own struct validation tags with parameter +govalidator.ParamTagMap["animal"] = govalidator.ParamValidator(func(str string, params ...string) bool { + species := params[0] + return str == species +}) +govalidator.ParamTagRegexMap["animal"] = regexp.MustCompile("^animal\\((\\w+)\\)$") + +result, err := govalidator.ValidateStruct(post) +if err != nil { + println("error: " + err.Error()) +} +println(result) +``` +###### ValidateMap [#2](https://github.com/asaskevich/govalidator/pull/338) +If you want to validate maps, you can use the map to be validated and a validation map that contain the same tags used in ValidateStruct, both maps have to be in the form `map[string]interface{}` + +So here is small example of usage: +```go +var mapTemplate = map[string]interface{}{ + "name":"required,alpha", + "family":"required,alpha", + "email":"required,email", + "cell-phone":"numeric", + "address":map[string]interface{}{ + "line1":"required,alphanum", + "line2":"alphanum", + "postal-code":"numeric", + }, +} + +var inputMap = map[string]interface{}{ + "name":"Bob", + "family":"Smith", + "email":"foo@bar.baz", + "address":map[string]interface{}{ + "line1":"", + "line2":"", + "postal-code":"", + }, +} + +result, err := govalidator.ValidateMap(inputMap, mapTemplate) +if err != nil { + println("error: " + err.Error()) +} +println(result) +``` + +###### WhiteList +```go +// Remove all characters from string ignoring characters between "a" and "z" +println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") +``` + +###### Custom validation functions +Custom validation using your own domain specific validators is also available - here's an example of how to use it: +```go +import "github.com/asaskevich/govalidator" + +type CustomByteArray [6]byte // custom types are supported and can be validated + +type StructWithCustomByteArray struct { + ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence + Email string `valid:"email"` + CustomMinLength int `valid:"-"` +} + +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, context interface{}) bool { + switch v := context.(type) { // you can type switch on the context interface being validated + case StructWithCustomByteArray: + // you can check and validate against some other field in the context, + // return early or not validate against the context at all – your choice + case SomeOtherType: + // ... + default: + // expecting some other type? Throw/panic here or continue + } + + switch v := i.(type) { // type switch on the struct field being validated + case CustomByteArray: + for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes + if e != 0 { + return true + } + } + } + return false +}) +govalidator.CustomTypeTagMap.Set("customMinLengthValidator", func(i interface{}, context interface{}) bool { + switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation + case StructWithCustomByteArray: + return len(v.ID) >= v.CustomMinLength + } + return false +}) +``` + +###### Loop over Error() +By default .Error() returns all errors in a single String. To access each error you can do this: +```go + if err != nil { + errs := err.(govalidator.Errors).Errors() + for _, e := range errs { + fmt.Println(e.Error()) + } + } +``` + +###### Custom error messages +Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it: +```go +type Ticket struct { + Id int64 `json:"id"` + FirstName string `json:"firstname" valid:"required~First name is blank"` +} +``` + +#### Notes +Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator). +Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator). + +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements + +## Credits +### Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + +#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) +* [Daniel Lohse](https://github.com/annismckenzie) +* [Attila Oláh](https://github.com/attilaolah) +* [Daniel Korner](https://github.com/Dadie) +* [Steven Wilkin](https://github.com/stevenwilkin) +* [Deiwin Sarjas](https://github.com/deiwin) +* [Noah Shibley](https://github.com/slugmobile) +* [Nathan Davies](https://github.com/nathj07) +* [Matt Sanford](https://github.com/mzsanford) +* [Simon ccl1115](https://github.com/ccl1115) + + + + +### Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)] + + + + +### Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)] + + + + + + + + + + + + + + + +## License +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_large) diff --git a/vendor/github.com/asaskevich/govalidator/arrays.go b/vendor/github.com/asaskevich/govalidator/arrays.go new file mode 100644 index 00000000..3e1da7cb --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/arrays.go @@ -0,0 +1,87 @@ +package govalidator + +// Iterator is the function that accepts element of slice/array and its index +type Iterator func(interface{}, int) + +// ResultIterator is the function that accepts element of slice/array and its index and returns any result +type ResultIterator func(interface{}, int) interface{} + +// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean +type ConditionIterator func(interface{}, int) bool + +// ReduceIterator is the function that accepts two element of slice/array and returns result of merging those values +type ReduceIterator func(interface{}, interface{}) interface{} + +// Some validates that any item of array corresponds to ConditionIterator. Returns boolean. +func Some(array []interface{}, iterator ConditionIterator) bool { + res := false + for index, data := range array { + res = res || iterator(data, index) + } + return res +} + +// Every validates that every item of array corresponds to ConditionIterator. Returns boolean. +func Every(array []interface{}, iterator ConditionIterator) bool { + res := true + for index, data := range array { + res = res && iterator(data, index) + } + return res +} + +// Reduce boils down a list of values into a single value by ReduceIterator +func Reduce(array []interface{}, iterator ReduceIterator, initialValue interface{}) interface{} { + for _, data := range array { + initialValue = iterator(initialValue, data) + } + return initialValue +} + +// Each iterates over the slice and apply Iterator to every item +func Each(array []interface{}, iterator Iterator) { + for index, data := range array { + iterator(data, index) + } +} + +// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result. +func Map(array []interface{}, iterator ResultIterator) []interface{} { + var result = make([]interface{}, len(array)) + for index, data := range array { + result[index] = iterator(data, index) + } + return result +} + +// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise. +func Find(array []interface{}, iterator ConditionIterator) interface{} { + for index, data := range array { + if iterator(data, index) { + return data + } + } + return nil +} + +// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice. +func Filter(array []interface{}, iterator ConditionIterator) []interface{} { + var result = make([]interface{}, 0) + for index, data := range array { + if iterator(data, index) { + result = append(result, data) + } + } + return result +} + +// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator. +func Count(array []interface{}, iterator ConditionIterator) int { + count := 0 + for index, data := range array { + if iterator(data, index) { + count = count + 1 + } + } + return count +} diff --git a/vendor/github.com/asaskevich/govalidator/converter.go b/vendor/github.com/asaskevich/govalidator/converter.go new file mode 100644 index 00000000..d68e990f --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/converter.go @@ -0,0 +1,81 @@ +package govalidator + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" +) + +// ToString convert the input to a string. +func ToString(obj interface{}) string { + res := fmt.Sprintf("%v", obj) + return res +} + +// ToJSON convert the input to a valid JSON string +func ToJSON(obj interface{}) (string, error) { + res, err := json.Marshal(obj) + if err != nil { + res = []byte("") + } + return string(res), err +} + +// ToFloat convert the input string to a float, or 0.0 if the input is not a float. +func ToFloat(value interface{}) (res float64, err error) { + val := reflect.ValueOf(value) + + switch value.(type) { + case int, int8, int16, int32, int64: + res = float64(val.Int()) + case uint, uint8, uint16, uint32, uint64: + res = float64(val.Uint()) + case float32, float64: + res = val.Float() + case string: + res, err = strconv.ParseFloat(val.String(), 64) + if err != nil { + res = 0 + } + default: + err = fmt.Errorf("ToInt: unknown interface type %T", value) + res = 0 + } + + return +} + +// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer. +func ToInt(value interface{}) (res int64, err error) { + val := reflect.ValueOf(value) + + switch value.(type) { + case int, int8, int16, int32, int64: + res = val.Int() + case uint, uint8, uint16, uint32, uint64: + res = int64(val.Uint()) + case float32, float64: + res = int64(val.Float()) + case string: + if IsInt(val.String()) { + res, err = strconv.ParseInt(val.String(), 0, 64) + if err != nil { + res = 0 + } + } else { + err = fmt.Errorf("ToInt: invalid numeric format %g", value) + res = 0 + } + default: + err = fmt.Errorf("ToInt: unknown interface type %T", value) + res = 0 + } + + return +} + +// ToBoolean convert the input string to a boolean. +func ToBoolean(str string) (bool, error) { + return strconv.ParseBool(str) +} diff --git a/vendor/github.com/asaskevich/govalidator/doc.go b/vendor/github.com/asaskevich/govalidator/doc.go new file mode 100644 index 00000000..55dce62d --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/doc.go @@ -0,0 +1,3 @@ +package govalidator + +// A package of validators and sanitizers for strings, structures and collections. diff --git a/vendor/github.com/asaskevich/govalidator/error.go b/vendor/github.com/asaskevich/govalidator/error.go new file mode 100644 index 00000000..1da2336f --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/error.go @@ -0,0 +1,47 @@ +package govalidator + +import ( + "sort" + "strings" +) + +// Errors is an array of multiple errors and conforms to the error interface. +type Errors []error + +// Errors returns itself. +func (es Errors) Errors() []error { + return es +} + +func (es Errors) Error() string { + var errs []string + for _, e := range es { + errs = append(errs, e.Error()) + } + sort.Strings(errs) + return strings.Join(errs, ";") +} + +// Error encapsulates a name, an error and whether there's a custom error message or not. +type Error struct { + Name string + Err error + CustomErrorMessageExists bool + + // Validator indicates the name of the validator that failed + Validator string + Path []string +} + +func (e Error) Error() string { + if e.CustomErrorMessageExists { + return e.Err.Error() + } + + errName := e.Name + if len(e.Path) > 0 { + errName = strings.Join(append(e.Path, e.Name), ".") + } + + return errName + ": " + e.Err.Error() +} diff --git a/vendor/github.com/asaskevich/govalidator/numerics.go b/vendor/github.com/asaskevich/govalidator/numerics.go new file mode 100644 index 00000000..5041d9e8 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/numerics.go @@ -0,0 +1,100 @@ +package govalidator + +import ( + "math" +) + +// Abs returns absolute value of number +func Abs(value float64) float64 { + return math.Abs(value) +} + +// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise +func Sign(value float64) float64 { + if value > 0 { + return 1 + } else if value < 0 { + return -1 + } else { + return 0 + } +} + +// IsNegative returns true if value < 0 +func IsNegative(value float64) bool { + return value < 0 +} + +// IsPositive returns true if value > 0 +func IsPositive(value float64) bool { + return value > 0 +} + +// IsNonNegative returns true if value >= 0 +func IsNonNegative(value float64) bool { + return value >= 0 +} + +// IsNonPositive returns true if value <= 0 +func IsNonPositive(value float64) bool { + return value <= 0 +} + +// InRangeInt returns true if value lies between left and right border +func InRangeInt(value, left, right interface{}) bool { + value64, _ := ToInt(value) + left64, _ := ToInt(left) + right64, _ := ToInt(right) + if left64 > right64 { + left64, right64 = right64, left64 + } + return value64 >= left64 && value64 <= right64 +} + +// InRangeFloat32 returns true if value lies between left and right border +func InRangeFloat32(value, left, right float32) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRangeFloat64 returns true if value lies between left and right border +func InRangeFloat64(value, left, right float64) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border, generic type to handle int, float32, float64 and string. +// All types must the same type. +// False if value doesn't lie in range or if it incompatible or not comparable +func InRange(value interface{}, left interface{}, right interface{}) bool { + switch value.(type) { + case int: + intValue, _ := ToInt(value) + intLeft, _ := ToInt(left) + intRight, _ := ToInt(right) + return InRangeInt(intValue, intLeft, intRight) + case float32, float64: + intValue, _ := ToFloat(value) + intLeft, _ := ToFloat(left) + intRight, _ := ToFloat(right) + return InRangeFloat64(intValue, intLeft, intRight) + case string: + return value.(string) >= left.(string) && value.(string) <= right.(string) + default: + return false + } +} + +// IsWhole returns true if value is whole number +func IsWhole(value float64) bool { + return math.Remainder(value, 1) == 0 +} + +// IsNatural returns true if value is natural number (positive and whole) +func IsNatural(value float64) bool { + return IsWhole(value) && IsPositive(value) +} diff --git a/vendor/github.com/asaskevich/govalidator/patterns.go b/vendor/github.com/asaskevich/govalidator/patterns.go new file mode 100644 index 00000000..bafc3765 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/patterns.go @@ -0,0 +1,113 @@ +package govalidator + +import "regexp" + +// Basic regular expressions for validating strings +const ( + Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$" + ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$" + ISBN13 string = "^(?:[0-9]{13})$" + UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" + UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + Alpha string = "^[a-zA-Z]+$" + Alphanumeric string = "^[a-zA-Z0-9]+$" + Numeric string = "^[0-9]+$" + Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$" + Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" + Hexadecimal string = "^[0-9a-fA-F]+$" + Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$" + ASCII string = "^[\x00-\x7F]+$" + Multibyte string = "[^\x00-\x7F]" + FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" + PrintableASCII string = "^[\x20-\x7E]+$" + DataURI string = "^data:.+\\/(.+);base64$" + MagnetURI string = "^magnet:\\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,40}&dn=.+&tr=.+$" + Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$` + IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)` + URLUsername string = `(\S+(:\S*)?@)` + URLPath string = `((\/|\?|#)[^\s]*)` + URLPort string = `(:(\d{1,5}))` + URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3]|24\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-5]))` + URLSubdomain string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))` + URL = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$` + SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` + UnixPath string = `^(/[^/\x00]*)+/?$` + WinARPath string = `^(?:(?:[a-zA-Z]:|\\\\[a-z0-9_.$●-]+\\[a-z0-9_.$●-]+)\\|\\?[^\\/:*?"<>|\r\n]+\\?)(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` + UnixARPath string = `^((\.{0,2}/)?([^/\x00]*))+/?$` + Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$" + tagName string = "valid" + hasLowerCase string = ".*[[:lower:]]" + hasUpperCase string = ".*[[:upper:]]" + hasWhitespace string = ".*[[:space:]]" + hasWhitespaceOnly string = "^[[:space:]]+$" + IMEI string = "^[0-9a-f]{14}$|^\\d{15}$|^\\d{18}$" + IMSI string = "^\\d{14,15}$" + E164 string = `^\+?[1-9]\d{1,14}$` +) + +// Used by IsFilePath func +const ( + // Unknown is unresolved OS type + Unknown = iota + // Win is Windows type + Win + // Unix is *nix OS types + Unix +) + +var ( + userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$") + hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$") + userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})") + rxEmail = regexp.MustCompile(Email) + rxCreditCard = regexp.MustCompile(CreditCard) + rxISBN10 = regexp.MustCompile(ISBN10) + rxISBN13 = regexp.MustCompile(ISBN13) + rxUUID3 = regexp.MustCompile(UUID3) + rxUUID4 = regexp.MustCompile(UUID4) + rxUUID5 = regexp.MustCompile(UUID5) + rxUUID = regexp.MustCompile(UUID) + rxAlpha = regexp.MustCompile(Alpha) + rxAlphanumeric = regexp.MustCompile(Alphanumeric) + rxNumeric = regexp.MustCompile(Numeric) + rxInt = regexp.MustCompile(Int) + rxFloat = regexp.MustCompile(Float) + rxHexadecimal = regexp.MustCompile(Hexadecimal) + rxHexcolor = regexp.MustCompile(Hexcolor) + rxRGBcolor = regexp.MustCompile(RGBcolor) + rxASCII = regexp.MustCompile(ASCII) + rxPrintableASCII = regexp.MustCompile(PrintableASCII) + rxMultibyte = regexp.MustCompile(Multibyte) + rxFullWidth = regexp.MustCompile(FullWidth) + rxHalfWidth = regexp.MustCompile(HalfWidth) + rxBase64 = regexp.MustCompile(Base64) + rxDataURI = regexp.MustCompile(DataURI) + rxMagnetURI = regexp.MustCompile(MagnetURI) + rxLatitude = regexp.MustCompile(Latitude) + rxLongitude = regexp.MustCompile(Longitude) + rxDNSName = regexp.MustCompile(DNSName) + rxURL = regexp.MustCompile(URL) + rxSSN = regexp.MustCompile(SSN) + rxWinPath = regexp.MustCompile(WinPath) + rxUnixPath = regexp.MustCompile(UnixPath) + rxARWinPath = regexp.MustCompile(WinARPath) + rxARUnixPath = regexp.MustCompile(UnixARPath) + rxSemver = regexp.MustCompile(Semver) + rxHasLowerCase = regexp.MustCompile(hasLowerCase) + rxHasUpperCase = regexp.MustCompile(hasUpperCase) + rxHasWhitespace = regexp.MustCompile(hasWhitespace) + rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly) + rxIMEI = regexp.MustCompile(IMEI) + rxIMSI = regexp.MustCompile(IMSI) + rxE164 = regexp.MustCompile(E164) +) diff --git a/vendor/github.com/asaskevich/govalidator/types.go b/vendor/github.com/asaskevich/govalidator/types.go new file mode 100644 index 00000000..c573abb5 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/types.go @@ -0,0 +1,656 @@ +package govalidator + +import ( + "reflect" + "regexp" + "sort" + "sync" +) + +// Validator is a wrapper for a validator function that returns bool and accepts string. +type Validator func(str string) bool + +// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type. +// The second parameter should be the context (in the case of validating a struct: the whole object being validated). +type CustomTypeValidator func(i interface{}, o interface{}) bool + +// ParamValidator is a wrapper for validator functions that accept additional parameters. +type ParamValidator func(str string, params ...string) bool + +// InterfaceParamValidator is a wrapper for functions that accept variants parameters for an interface value +type InterfaceParamValidator func(in interface{}, params ...string) bool +type tagOptionsMap map[string]tagOption + +func (t tagOptionsMap) orderedKeys() []string { + var keys []string + for k := range t { + keys = append(keys, k) + } + + sort.Slice(keys, func(a, b int) bool { + return t[keys[a]].order < t[keys[b]].order + }) + + return keys +} + +type tagOption struct { + name string + customErrorMessage string + order int +} + +// UnsupportedTypeError is a wrapper for reflect.Type +type UnsupportedTypeError struct { + Type reflect.Type +} + +// stringValues is a slice of reflect.Value holding *reflect.StringValue. +// It implements the methods to sort by string. +type stringValues []reflect.Value + +// InterfaceParamTagMap is a map of functions accept variants parameters for an interface value +var InterfaceParamTagMap = map[string]InterfaceParamValidator{ + "type": IsType, +} + +// InterfaceParamTagRegexMap maps interface param tags to their respective regexes. +var InterfaceParamTagRegexMap = map[string]*regexp.Regexp{ + "type": regexp.MustCompile(`^type\((.*)\)$`), +} + +// ParamTagMap is a map of functions accept variants parameters +var ParamTagMap = map[string]ParamValidator{ + "length": ByteLength, + "range": Range, + "runelength": RuneLength, + "stringlength": StringLength, + "matches": StringMatches, + "in": IsInRaw, + "rsapub": IsRsaPub, + "minstringlength": MinStringLength, + "maxstringlength": MaxStringLength, +} + +// ParamTagRegexMap maps param tags to their respective regexes. +var ParamTagRegexMap = map[string]*regexp.Regexp{ + "range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"), + "length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), + "runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"), + "stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), + "in": regexp.MustCompile(`^in\((.*)\)`), + "matches": regexp.MustCompile(`^matches\((.+)\)$`), + "rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"), + "minstringlength": regexp.MustCompile("^minstringlength\\((\\d+)\\)$"), + "maxstringlength": regexp.MustCompile("^maxstringlength\\((\\d+)\\)$"), +} + +type customTypeTagMap struct { + validators map[string]CustomTypeValidator + + sync.RWMutex +} + +func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) { + tm.RLock() + defer tm.RUnlock() + v, ok := tm.validators[name] + return v, ok +} + +func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) { + tm.Lock() + defer tm.Unlock() + tm.validators[name] = ctv +} + +// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function. +// Use this to validate compound or custom types that need to be handled as a whole, e.g. +// `type UUID [16]byte` (this would be handled as an array of bytes). +var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)} + +// TagMap is a map of functions, that can be used as tags for ValidateStruct function. +var TagMap = map[string]Validator{ + "email": IsEmail, + "url": IsURL, + "dialstring": IsDialString, + "requrl": IsRequestURL, + "requri": IsRequestURI, + "alpha": IsAlpha, + "utfletter": IsUTFLetter, + "alphanum": IsAlphanumeric, + "utfletternum": IsUTFLetterNumeric, + "numeric": IsNumeric, + "utfnumeric": IsUTFNumeric, + "utfdigit": IsUTFDigit, + "hexadecimal": IsHexadecimal, + "hexcolor": IsHexcolor, + "rgbcolor": IsRGBcolor, + "lowercase": IsLowerCase, + "uppercase": IsUpperCase, + "int": IsInt, + "float": IsFloat, + "null": IsNull, + "notnull": IsNotNull, + "uuid": IsUUID, + "uuidv3": IsUUIDv3, + "uuidv4": IsUUIDv4, + "uuidv5": IsUUIDv5, + "creditcard": IsCreditCard, + "isbn10": IsISBN10, + "isbn13": IsISBN13, + "json": IsJSON, + "multibyte": IsMultibyte, + "ascii": IsASCII, + "printableascii": IsPrintableASCII, + "fullwidth": IsFullWidth, + "halfwidth": IsHalfWidth, + "variablewidth": IsVariableWidth, + "base64": IsBase64, + "datauri": IsDataURI, + "ip": IsIP, + "port": IsPort, + "ipv4": IsIPv4, + "ipv6": IsIPv6, + "dns": IsDNSName, + "host": IsHost, + "mac": IsMAC, + "latitude": IsLatitude, + "longitude": IsLongitude, + "ssn": IsSSN, + "semver": IsSemver, + "rfc3339": IsRFC3339, + "rfc3339WithoutZone": IsRFC3339WithoutZone, + "ISO3166Alpha2": IsISO3166Alpha2, + "ISO3166Alpha3": IsISO3166Alpha3, + "ISO4217": IsISO4217, + "IMEI": IsIMEI, + "ulid": IsULID, +} + +// ISO3166Entry stores country codes +type ISO3166Entry struct { + EnglishShortName string + FrenchShortName string + Alpha2Code string + Alpha3Code string + Numeric string +} + +//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes" +var ISO3166List = []ISO3166Entry{ + {"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"}, + {"Albania", "Albanie (l')", "AL", "ALB", "008"}, + {"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"}, + {"Algeria", "Algérie (l')", "DZ", "DZA", "012"}, + {"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"}, + {"Andorra", "Andorre (l')", "AD", "AND", "020"}, + {"Angola", "Angola (l')", "AO", "AGO", "024"}, + {"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"}, + {"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"}, + {"Argentina", "Argentine (l')", "AR", "ARG", "032"}, + {"Australia", "Australie (l')", "AU", "AUS", "036"}, + {"Austria", "Autriche (l')", "AT", "AUT", "040"}, + {"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"}, + {"Bahrain", "Bahreïn", "BH", "BHR", "048"}, + {"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"}, + {"Armenia", "Arménie (l')", "AM", "ARM", "051"}, + {"Barbados", "Barbade (la)", "BB", "BRB", "052"}, + {"Belgium", "Belgique (la)", "BE", "BEL", "056"}, + {"Bermuda", "Bermudes (les)", "BM", "BMU", "060"}, + {"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"}, + {"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"}, + {"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"}, + {"Botswana", "Botswana (le)", "BW", "BWA", "072"}, + {"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"}, + {"Brazil", "Brésil (le)", "BR", "BRA", "076"}, + {"Belize", "Belize (le)", "BZ", "BLZ", "084"}, + {"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"}, + {"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"}, + {"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"}, + {"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"}, + {"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"}, + {"Myanmar", "Myanmar (le)", "MM", "MMR", "104"}, + {"Burundi", "Burundi (le)", "BI", "BDI", "108"}, + {"Belarus", "Bélarus (le)", "BY", "BLR", "112"}, + {"Cambodia", "Cambodge (le)", "KH", "KHM", "116"}, + {"Cameroon", "Cameroun (le)", "CM", "CMR", "120"}, + {"Canada", "Canada (le)", "CA", "CAN", "124"}, + {"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"}, + {"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"}, + {"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"}, + {"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"}, + {"Chad", "Tchad (le)", "TD", "TCD", "148"}, + {"Chile", "Chili (le)", "CL", "CHL", "152"}, + {"China", "Chine (la)", "CN", "CHN", "156"}, + {"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"}, + {"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"}, + {"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"}, + {"Colombia", "Colombie (la)", "CO", "COL", "170"}, + {"Comoros (the)", "Comores (les)", "KM", "COM", "174"}, + {"Mayotte", "Mayotte", "YT", "MYT", "175"}, + {"Congo (the)", "Congo (le)", "CG", "COG", "178"}, + {"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"}, + {"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"}, + {"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"}, + {"Croatia", "Croatie (la)", "HR", "HRV", "191"}, + {"Cuba", "Cuba", "CU", "CUB", "192"}, + {"Cyprus", "Chypre", "CY", "CYP", "196"}, + {"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"}, + {"Benin", "Bénin (le)", "BJ", "BEN", "204"}, + {"Denmark", "Danemark (le)", "DK", "DNK", "208"}, + {"Dominica", "Dominique (la)", "DM", "DMA", "212"}, + {"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"}, + {"Ecuador", "Équateur (l')", "EC", "ECU", "218"}, + {"El Salvador", "El Salvador", "SV", "SLV", "222"}, + {"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"}, + {"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"}, + {"Eritrea", "Érythrée (l')", "ER", "ERI", "232"}, + {"Estonia", "Estonie (l')", "EE", "EST", "233"}, + {"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"}, + {"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"}, + {"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"}, + {"Fiji", "Fidji (les)", "FJ", "FJI", "242"}, + {"Finland", "Finlande (la)", "FI", "FIN", "246"}, + {"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"}, + {"France", "France (la)", "FR", "FRA", "250"}, + {"French Guiana", "Guyane française (la )", "GF", "GUF", "254"}, + {"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"}, + {"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"}, + {"Djibouti", "Djibouti", "DJ", "DJI", "262"}, + {"Gabon", "Gabon (le)", "GA", "GAB", "266"}, + {"Georgia", "Géorgie (la)", "GE", "GEO", "268"}, + {"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"}, + {"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"}, + {"Germany", "Allemagne (l')", "DE", "DEU", "276"}, + {"Ghana", "Ghana (le)", "GH", "GHA", "288"}, + {"Gibraltar", "Gibraltar", "GI", "GIB", "292"}, + {"Kiribati", "Kiribati", "KI", "KIR", "296"}, + {"Greece", "Grèce (la)", "GR", "GRC", "300"}, + {"Greenland", "Groenland (le)", "GL", "GRL", "304"}, + {"Grenada", "Grenade (la)", "GD", "GRD", "308"}, + {"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"}, + {"Guam", "Guam", "GU", "GUM", "316"}, + {"Guatemala", "Guatemala (le)", "GT", "GTM", "320"}, + {"Guinea", "Guinée (la)", "GN", "GIN", "324"}, + {"Guyana", "Guyana (le)", "GY", "GUY", "328"}, + {"Haiti", "Haïti", "HT", "HTI", "332"}, + {"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"}, + {"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"}, + {"Honduras", "Honduras (le)", "HN", "HND", "340"}, + {"Hong Kong", "Hong Kong", "HK", "HKG", "344"}, + {"Hungary", "Hongrie (la)", "HU", "HUN", "348"}, + {"Iceland", "Islande (l')", "IS", "ISL", "352"}, + {"India", "Inde (l')", "IN", "IND", "356"}, + {"Indonesia", "Indonésie (l')", "ID", "IDN", "360"}, + {"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"}, + {"Iraq", "Iraq (l')", "IQ", "IRQ", "368"}, + {"Ireland", "Irlande (l')", "IE", "IRL", "372"}, + {"Israel", "Israël", "IL", "ISR", "376"}, + {"Italy", "Italie (l')", "IT", "ITA", "380"}, + {"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"}, + {"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"}, + {"Japan", "Japon (le)", "JP", "JPN", "392"}, + {"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"}, + {"Jordan", "Jordanie (la)", "JO", "JOR", "400"}, + {"Kenya", "Kenya (le)", "KE", "KEN", "404"}, + {"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"}, + {"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"}, + {"Kuwait", "Koweït (le)", "KW", "KWT", "414"}, + {"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"}, + {"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"}, + {"Lebanon", "Liban (le)", "LB", "LBN", "422"}, + {"Lesotho", "Lesotho (le)", "LS", "LSO", "426"}, + {"Latvia", "Lettonie (la)", "LV", "LVA", "428"}, + {"Liberia", "Libéria (le)", "LR", "LBR", "430"}, + {"Libya", "Libye (la)", "LY", "LBY", "434"}, + {"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"}, + {"Lithuania", "Lituanie (la)", "LT", "LTU", "440"}, + {"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"}, + {"Macao", "Macao", "MO", "MAC", "446"}, + {"Madagascar", "Madagascar", "MG", "MDG", "450"}, + {"Malawi", "Malawi (le)", "MW", "MWI", "454"}, + {"Malaysia", "Malaisie (la)", "MY", "MYS", "458"}, + {"Maldives", "Maldives (les)", "MV", "MDV", "462"}, + {"Mali", "Mali (le)", "ML", "MLI", "466"}, + {"Malta", "Malte", "MT", "MLT", "470"}, + {"Martinique", "Martinique (la)", "MQ", "MTQ", "474"}, + {"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"}, + {"Mauritius", "Maurice", "MU", "MUS", "480"}, + {"Mexico", "Mexique (le)", "MX", "MEX", "484"}, + {"Monaco", "Monaco", "MC", "MCO", "492"}, + {"Mongolia", "Mongolie (la)", "MN", "MNG", "496"}, + {"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"}, + {"Montenegro", "Monténégro (le)", "ME", "MNE", "499"}, + {"Montserrat", "Montserrat", "MS", "MSR", "500"}, + {"Morocco", "Maroc (le)", "MA", "MAR", "504"}, + {"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"}, + {"Oman", "Oman", "OM", "OMN", "512"}, + {"Namibia", "Namibie (la)", "NA", "NAM", "516"}, + {"Nauru", "Nauru", "NR", "NRU", "520"}, + {"Nepal", "Népal (le)", "NP", "NPL", "524"}, + {"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"}, + {"Curaçao", "Curaçao", "CW", "CUW", "531"}, + {"Aruba", "Aruba", "AW", "ABW", "533"}, + {"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"}, + {"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"}, + {"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"}, + {"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"}, + {"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"}, + {"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"}, + {"Niger (the)", "Niger (le)", "NE", "NER", "562"}, + {"Nigeria", "Nigéria (le)", "NG", "NGA", "566"}, + {"Niue", "Niue", "NU", "NIU", "570"}, + {"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"}, + {"Norway", "Norvège (la)", "NO", "NOR", "578"}, + {"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"}, + {"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"}, + {"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"}, + {"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"}, + {"Palau", "Palaos (les)", "PW", "PLW", "585"}, + {"Pakistan", "Pakistan (le)", "PK", "PAK", "586"}, + {"Panama", "Panama (le)", "PA", "PAN", "591"}, + {"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"}, + {"Paraguay", "Paraguay (le)", "PY", "PRY", "600"}, + {"Peru", "Pérou (le)", "PE", "PER", "604"}, + {"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"}, + {"Pitcairn", "Pitcairn", "PN", "PCN", "612"}, + {"Poland", "Pologne (la)", "PL", "POL", "616"}, + {"Portugal", "Portugal (le)", "PT", "PRT", "620"}, + {"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"}, + {"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"}, + {"Puerto Rico", "Porto Rico", "PR", "PRI", "630"}, + {"Qatar", "Qatar (le)", "QA", "QAT", "634"}, + {"Réunion", "Réunion (La)", "RE", "REU", "638"}, + {"Romania", "Roumanie (la)", "RO", "ROU", "642"}, + {"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"}, + {"Rwanda", "Rwanda (le)", "RW", "RWA", "646"}, + {"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"}, + {"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"}, + {"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"}, + {"Anguilla", "Anguilla", "AI", "AIA", "660"}, + {"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"}, + {"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"}, + {"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"}, + {"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"}, + {"San Marino", "Saint-Marin", "SM", "SMR", "674"}, + {"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"}, + {"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"}, + {"Senegal", "Sénégal (le)", "SN", "SEN", "686"}, + {"Serbia", "Serbie (la)", "RS", "SRB", "688"}, + {"Seychelles", "Seychelles (les)", "SC", "SYC", "690"}, + {"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"}, + {"Singapore", "Singapour", "SG", "SGP", "702"}, + {"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"}, + {"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"}, + {"Slovenia", "Slovénie (la)", "SI", "SVN", "705"}, + {"Somalia", "Somalie (la)", "SO", "SOM", "706"}, + {"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"}, + {"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"}, + {"Spain", "Espagne (l')", "ES", "ESP", "724"}, + {"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"}, + {"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"}, + {"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"}, + {"Suriname", "Suriname (le)", "SR", "SUR", "740"}, + {"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"}, + {"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"}, + {"Sweden", "Suède (la)", "SE", "SWE", "752"}, + {"Switzerland", "Suisse (la)", "CH", "CHE", "756"}, + {"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"}, + {"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"}, + {"Thailand", "Thaïlande (la)", "TH", "THA", "764"}, + {"Togo", "Togo (le)", "TG", "TGO", "768"}, + {"Tokelau", "Tokelau (les)", "TK", "TKL", "772"}, + {"Tonga", "Tonga (les)", "TO", "TON", "776"}, + {"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"}, + {"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"}, + {"Tunisia", "Tunisie (la)", "TN", "TUN", "788"}, + {"Turkey", "Turquie (la)", "TR", "TUR", "792"}, + {"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"}, + {"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"}, + {"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"}, + {"Uganda", "Ouganda (l')", "UG", "UGA", "800"}, + {"Ukraine", "Ukraine (l')", "UA", "UKR", "804"}, + {"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"}, + {"Egypt", "Égypte (l')", "EG", "EGY", "818"}, + {"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"}, + {"Guernsey", "Guernesey", "GG", "GGY", "831"}, + {"Jersey", "Jersey", "JE", "JEY", "832"}, + {"Isle of Man", "Île de Man", "IM", "IMN", "833"}, + {"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"}, + {"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"}, + {"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"}, + {"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"}, + {"Uruguay", "Uruguay (l')", "UY", "URY", "858"}, + {"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"}, + {"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"}, + {"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"}, + {"Samoa", "Samoa (le)", "WS", "WSM", "882"}, + {"Yemen", "Yémen (le)", "YE", "YEM", "887"}, + {"Zambia", "Zambie (la)", "ZM", "ZMB", "894"}, +} + +// ISO4217List is the list of ISO currency codes +var ISO4217List = []string{ + "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", + "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", + "CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK", + "DJF", "DKK", "DOP", "DZD", + "EGP", "ERN", "ETB", "EUR", + "FJD", "FKP", + "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", + "HKD", "HNL", "HRK", "HTG", "HUF", + "IDR", "ILS", "INR", "IQD", "IRR", "ISK", + "JMD", "JOD", "JPY", + "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", + "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", + "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", + "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", + "OMR", + "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", + "QAR", + "RON", "RSD", "RUB", "RWF", + "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL", + "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", + "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS", + "VEF", "VES", "VND", "VUV", + "WST", + "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", + "YER", + "ZAR", "ZMW", "ZWL", +} + +// ISO693Entry stores ISO language codes +type ISO693Entry struct { + Alpha3bCode string + Alpha2Code string + English string +} + +//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json +var ISO693List = []ISO693Entry{ + {Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"}, + {Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"}, + {Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"}, + {Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"}, + {Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"}, + {Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"}, + {Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"}, + {Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"}, + {Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"}, + {Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"}, + {Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"}, + {Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"}, + {Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"}, + {Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"}, + {Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"}, + {Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"}, + {Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"}, + {Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"}, + {Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"}, + {Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"}, + {Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"}, + {Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"}, + {Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"}, + {Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"}, + {Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"}, + {Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"}, + {Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"}, + {Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"}, + {Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"}, + {Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"}, + {Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"}, + {Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"}, + {Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"}, + {Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"}, + {Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"}, + {Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"}, + {Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"}, + {Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"}, + {Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"}, + {Alpha3bCode: "eng", Alpha2Code: "en", English: "English"}, + {Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"}, + {Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"}, + {Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"}, + {Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"}, + {Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"}, + {Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"}, + {Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"}, + {Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"}, + {Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"}, + {Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"}, + {Alpha3bCode: "ger", Alpha2Code: "de", English: "German"}, + {Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"}, + {Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"}, + {Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"}, + {Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"}, + {Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"}, + {Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"}, + {Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"}, + {Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"}, + {Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"}, + {Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"}, + {Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"}, + {Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"}, + {Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"}, + {Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"}, + {Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"}, + {Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"}, + {Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"}, + {Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"}, + {Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"}, + {Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"}, + {Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"}, + {Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"}, + {Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"}, + {Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"}, + {Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"}, + {Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"}, + {Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"}, + {Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"}, + {Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"}, + {Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"}, + {Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"}, + {Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"}, + {Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"}, + {Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"}, + {Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"}, + {Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"}, + {Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"}, + {Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"}, + {Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"}, + {Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"}, + {Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"}, + {Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"}, + {Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"}, + {Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"}, + {Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"}, + {Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"}, + {Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"}, + {Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"}, + {Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"}, + {Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"}, + {Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"}, + {Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"}, + {Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"}, + {Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"}, + {Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"}, + {Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"}, + {Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"}, + {Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"}, + {Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"}, + {Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"}, + {Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"}, + {Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"}, + {Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"}, + {Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"}, + {Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"}, + {Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"}, + {Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"}, + {Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"}, + {Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"}, + {Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"}, + {Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"}, + {Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"}, + {Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"}, + {Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"}, + {Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"}, + {Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"}, + {Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"}, + {Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"}, + {Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"}, + {Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"}, + {Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"}, + {Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"}, + {Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"}, + {Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"}, + {Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"}, + {Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"}, + {Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"}, + {Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"}, + {Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"}, + {Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"}, + {Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"}, + {Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"}, + {Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"}, + {Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"}, + {Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"}, + {Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"}, + {Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"}, + {Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"}, + {Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"}, + {Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"}, + {Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"}, + {Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"}, + {Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"}, + {Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"}, + {Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"}, + {Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"}, + {Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"}, + {Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"}, + {Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"}, + {Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"}, + {Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"}, + {Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"}, + {Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"}, + {Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"}, + {Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"}, + {Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"}, + {Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"}, + {Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"}, + {Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"}, + {Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"}, + {Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"}, + {Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"}, + {Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"}, + {Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"}, + {Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"}, + {Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"}, + {Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"}, + {Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"}, + {Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"}, + {Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"}, + {Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"}, + {Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"}, + {Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"}, +} diff --git a/vendor/github.com/asaskevich/govalidator/utils.go b/vendor/github.com/asaskevich/govalidator/utils.go new file mode 100644 index 00000000..f4c30f82 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/utils.go @@ -0,0 +1,270 @@ +package govalidator + +import ( + "errors" + "fmt" + "html" + "math" + "path" + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// Contains checks if the string contains the substring. +func Contains(str, substring string) bool { + return strings.Contains(str, substring) +} + +// Matches checks if string matches the pattern (pattern is regular expression) +// In case of error return false +func Matches(str, pattern string) bool { + match, _ := regexp.MatchString(pattern, str) + return match +} + +// LeftTrim trims characters from the left side of the input. +// If second argument is empty, it will remove leading spaces. +func LeftTrim(str, chars string) string { + if chars == "" { + return strings.TrimLeftFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("^[" + chars + "]+") + return r.ReplaceAllString(str, "") +} + +// RightTrim trims characters from the right side of the input. +// If second argument is empty, it will remove trailing spaces. +func RightTrim(str, chars string) string { + if chars == "" { + return strings.TrimRightFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("[" + chars + "]+$") + return r.ReplaceAllString(str, "") +} + +// Trim trims characters from both sides of the input. +// If second argument is empty, it will remove spaces. +func Trim(str, chars string) string { + return LeftTrim(RightTrim(str, chars), chars) +} + +// WhiteList removes characters that do not appear in the whitelist. +func WhiteList(str, chars string) string { + pattern := "[^" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// BlackList removes characters that appear in the blacklist. +func BlackList(str, chars string) string { + pattern := "[" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// StripLow removes characters with a numerical value < 32 and 127, mostly control characters. +// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD). +func StripLow(str string, keepNewLines bool) string { + chars := "" + if keepNewLines { + chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F" + } else { + chars = "\x00-\x1F\x7F" + } + return BlackList(str, chars) +} + +// ReplacePattern replaces regular expression pattern in string +func ReplacePattern(str, pattern, replace string) string { + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, replace) +} + +// Escape replaces <, >, & and " with HTML entities. +var Escape = html.EscapeString + +func addSegment(inrune, segment []rune) []rune { + if len(segment) == 0 { + return inrune + } + if len(inrune) != 0 { + inrune = append(inrune, '_') + } + inrune = append(inrune, segment...) + return inrune +} + +// UnderscoreToCamelCase converts from underscore separated form to camel case form. +// Ex.: my_func => MyFunc +func UnderscoreToCamelCase(s string) string { + return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1) +} + +// CamelCaseToUnderscore converts from camel case form to underscore separated form. +// Ex.: MyFunc => my_func +func CamelCaseToUnderscore(str string) string { + var output []rune + var segment []rune + for _, r := range str { + + // not treat number as separate segment + if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) { + output = addSegment(output, segment) + segment = nil + } + segment = append(segment, unicode.ToLower(r)) + } + output = addSegment(output, segment) + return string(output) +} + +// Reverse returns reversed string +func Reverse(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +// GetLines splits string by "\n" and return array of lines +func GetLines(s string) []string { + return strings.Split(s, "\n") +} + +// GetLine returns specified line of multiline string +func GetLine(s string, index int) (string, error) { + lines := GetLines(s) + if index < 0 || index >= len(lines) { + return "", errors.New("line index out of bounds") + } + return lines[index], nil +} + +// RemoveTags removes all tags from HTML string +func RemoveTags(s string) string { + return ReplacePattern(s, "<[^>]*>", "") +} + +// SafeFileName returns safe string that can be used in file names +func SafeFileName(str string) string { + name := strings.ToLower(str) + name = path.Clean(path.Base(name)) + name = strings.Trim(name, " ") + separators, err := regexp.Compile(`[ &_=+:]`) + if err == nil { + name = separators.ReplaceAllString(name, "-") + } + legal, err := regexp.Compile(`[^[:alnum:]-.]`) + if err == nil { + name = legal.ReplaceAllString(name, "") + } + for strings.Contains(name, "--") { + name = strings.Replace(name, "--", "-", -1) + } + return name +} + +// NormalizeEmail canonicalize an email address. +// The local part of the email address is lowercased for all domains; the hostname is always lowercased and +// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail). +// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and +// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are +// normalized to @gmail.com. +func NormalizeEmail(str string) (string, error) { + if !IsEmail(str) { + return "", fmt.Errorf("%s is not an email", str) + } + parts := strings.Split(str, "@") + parts[0] = strings.ToLower(parts[0]) + parts[1] = strings.ToLower(parts[1]) + if parts[1] == "gmail.com" || parts[1] == "googlemail.com" { + parts[1] = "gmail.com" + parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0] + } + return strings.Join(parts, "@"), nil +} + +// Truncate a string to the closest length without breaking words. +func Truncate(str string, length int, ending string) string { + var aftstr, befstr string + if len(str) > length { + words := strings.Fields(str) + before, present := 0, 0 + for i := range words { + befstr = aftstr + before = present + aftstr = aftstr + words[i] + " " + present = len(aftstr) + if present > length && i != 0 { + if (length - before) < (present - length) { + return Trim(befstr, " /\\.,\"'#!?&@+-") + ending + } + return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending + } + } + } + + return str +} + +// PadLeft pads left side of a string if size of string is less then indicated pad length +func PadLeft(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, false) +} + +// PadRight pads right side of a string if size of string is less then indicated pad length +func PadRight(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, false, true) +} + +// PadBoth pads both sides of a string if size of string is less then indicated pad length +func PadBoth(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, true) +} + +// PadString either left, right or both sides. +// Note that padding string can be unicode and more then one character +func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string { + + // When padded length is less then the current string size + if padLen < utf8.RuneCountInString(str) { + return str + } + + padLen -= utf8.RuneCountInString(str) + + targetLen := padLen + + targetLenLeft := targetLen + targetLenRight := targetLen + if padLeft && padRight { + targetLenLeft = padLen / 2 + targetLenRight = padLen - targetLenLeft + } + + strToRepeatLen := utf8.RuneCountInString(padStr) + + repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen))) + repeatedString := strings.Repeat(padStr, repeatTimes) + + leftSide := "" + if padLeft { + leftSide = repeatedString[0:targetLenLeft] + } + + rightSide := "" + if padRight { + rightSide = repeatedString[0:targetLenRight] + } + + return leftSide + str + rightSide +} + +// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object +func TruncatingErrorf(str string, args ...interface{}) error { + n := strings.Count(str, "%s") + return fmt.Errorf(str, args[:n]...) +} diff --git a/vendor/github.com/asaskevich/govalidator/validator.go b/vendor/github.com/asaskevich/govalidator/validator.go new file mode 100644 index 00000000..46ecfc84 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/validator.go @@ -0,0 +1,1769 @@ +// Package govalidator is package of validators and sanitizers for strings, structs and collections. +package govalidator + +import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "io/ioutil" + "net" + "net/url" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +var ( + fieldsRequiredByDefault bool + nilPtrAllowedByRequired = false + notNumberRegexp = regexp.MustCompile("[^0-9]+") + whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`) + paramsRegexp = regexp.MustCompile(`\(.*\)$`) +) + +const maxURLRuneCount = 2083 +const minURLRuneCount = 3 +const rfc3339WithoutZone = "2006-01-02T15:04:05" + +// SetFieldsRequiredByDefault causes validation to fail when struct fields +// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). +// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +// type exampleStruct struct { +// Name string `` +// Email string `valid:"email"` +// This, however, will only fail when Email is empty or an invalid email address: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email"` +// Lastly, this will only fail when Email is an invalid email address but not when it's empty: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email,optional"` +func SetFieldsRequiredByDefault(value bool) { + fieldsRequiredByDefault = value +} + +// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. +// The validation will still reject ptr fields in their zero value state. Example with this enabled: +// type exampleStruct struct { +// Name *string `valid:"required"` +// With `Name` set to "", this will be considered invalid input and will cause a validation error. +// With `Name` set to nil, this will be considered valid by validation. +// By default this is disabled. +func SetNilPtrAllowedByRequired(value bool) { + nilPtrAllowedByRequired = value +} + +// IsEmail checks if the string is an email. +func IsEmail(str string) bool { + // TODO uppercase letters are not supported + return rxEmail.MatchString(str) +} + +// IsExistingEmail checks if the string is an email of existing domain +func IsExistingEmail(email string) bool { + + if len(email) < 6 || len(email) > 254 { + return false + } + at := strings.LastIndex(email, "@") + if at <= 0 || at > len(email)-3 { + return false + } + user := email[:at] + host := email[at+1:] + if len(user) > 64 { + return false + } + switch host { + case "localhost", "example.com": + return true + } + if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) { + return false + } + if _, err := net.LookupMX(host); err != nil { + if _, err := net.LookupIP(host); err != nil { + return false + } + } + + return true +} + +// IsURL checks if the string is an URL. +func IsURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { + return false + } + strTemp := str + if strings.Contains(str, ":") && !strings.Contains(str, "://") { + // support no indicated urlscheme but with colon for port number + // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString + strTemp = "http://" + str + } + u, err := url.Parse(strTemp) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return rxURL.MatchString(str) +} + +// IsRequestURL checks if the string rawurl, assuming +// it was received in an HTTP request, is a valid +// URL confirm to RFC 3986 +func IsRequestURL(rawurl string) bool { + url, err := url.ParseRequestURI(rawurl) + if err != nil { + return false //Couldn't even parse the rawurl + } + if len(url.Scheme) == 0 { + return false //No Scheme found + } + return true +} + +// IsRequestURI checks if the string rawurl, assuming +// it was received in an HTTP request, is an +// absolute URI or an absolute path. +func IsRequestURI(rawurl string) bool { + _, err := url.ParseRequestURI(rawurl) + return err == nil +} + +// IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid. +func IsAlpha(str string) bool { + if IsNull(str) { + return true + } + return rxAlpha.MatchString(str) +} + +//IsUTFLetter checks if the string contains only unicode letter characters. +//Similar to IsAlpha but for all languages. Empty string is valid. +func IsUTFLetter(str string) bool { + if IsNull(str) { + return true + } + + for _, c := range str { + if !unicode.IsLetter(c) { + return false + } + } + return true + +} + +// IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid. +func IsAlphanumeric(str string) bool { + if IsNull(str) { + return true + } + return rxAlphanumeric.MatchString(str) +} + +// IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid. +func IsUTFLetterNumeric(str string) bool { + if IsNull(str) { + return true + } + for _, c := range str { + if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok + return false + } + } + return true + +} + +// IsNumeric checks if the string contains only numbers. Empty string is valid. +func IsNumeric(str string) bool { + if IsNull(str) { + return true + } + return rxNumeric.MatchString(str) +} + +// IsUTFNumeric checks if the string contains only unicode numbers of any kind. +// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid. +func IsUTFNumeric(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if !unicode.IsNumber(c) { //numbers && minus sign are ok + return false + } + } + return true + +} + +// IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid. +func IsUTFDigit(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if !unicode.IsDigit(c) { //digits && minus sign are ok + return false + } + } + return true + +} + +// IsHexadecimal checks if the string is a hexadecimal number. +func IsHexadecimal(str string) bool { + return rxHexadecimal.MatchString(str) +} + +// IsHexcolor checks if the string is a hexadecimal color. +func IsHexcolor(str string) bool { + return rxHexcolor.MatchString(str) +} + +// IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB). +func IsRGBcolor(str string) bool { + return rxRGBcolor.MatchString(str) +} + +// IsLowerCase checks if the string is lowercase. Empty string is valid. +func IsLowerCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToLower(str) +} + +// IsUpperCase checks if the string is uppercase. Empty string is valid. +func IsUpperCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToUpper(str) +} + +// HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid. +func HasLowerCase(str string) bool { + if IsNull(str) { + return true + } + return rxHasLowerCase.MatchString(str) +} + +// HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid. +func HasUpperCase(str string) bool { + if IsNull(str) { + return true + } + return rxHasUpperCase.MatchString(str) +} + +// IsInt checks if the string is an integer. Empty string is valid. +func IsInt(str string) bool { + if IsNull(str) { + return true + } + return rxInt.MatchString(str) +} + +// IsFloat checks if the string is a float. +func IsFloat(str string) bool { + return str != "" && rxFloat.MatchString(str) +} + +// IsDivisibleBy checks if the string is a number that's divisible by another. +// If second argument is not valid integer or zero, it's return false. +// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero). +func IsDivisibleBy(str, num string) bool { + f, _ := ToFloat(str) + p := int64(f) + q, _ := ToInt(num) + if q == 0 { + return false + } + return (p == 0) || (p%q == 0) +} + +// IsNull checks if the string is null. +func IsNull(str string) bool { + return len(str) == 0 +} + +// IsNotNull checks if the string is not null. +func IsNotNull(str string) bool { + return !IsNull(str) +} + +// HasWhitespaceOnly checks the string only contains whitespace +func HasWhitespaceOnly(str string) bool { + return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str) +} + +// HasWhitespace checks if the string contains any whitespace +func HasWhitespace(str string) bool { + return len(str) > 0 && rxHasWhitespace.MatchString(str) +} + +// IsByteLength checks if the string's length (in bytes) falls in a range. +func IsByteLength(str string, min, max int) bool { + return len(str) >= min && len(str) <= max +} + +// IsUUIDv3 checks if the string is a UUID version 3. +func IsUUIDv3(str string) bool { + return rxUUID3.MatchString(str) +} + +// IsUUIDv4 checks if the string is a UUID version 4. +func IsUUIDv4(str string) bool { + return rxUUID4.MatchString(str) +} + +// IsUUIDv5 checks if the string is a UUID version 5. +func IsUUIDv5(str string) bool { + return rxUUID5.MatchString(str) +} + +// IsUUID checks if the string is a UUID (version 3, 4 or 5). +func IsUUID(str string) bool { + return rxUUID.MatchString(str) +} + +// Byte to index table for O(1) lookups when unmarshaling. +// We use 0xFF as sentinel value for invalid indexes. +var ulidDec = [...]byte{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, + 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, + 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, + 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +} + +// EncodedSize is the length of a text encoded ULID. +const ulidEncodedSize = 26 + +// IsULID checks if the string is a ULID. +// +// Implementation got from: +// https://github.com/oklog/ulid (Apache-2.0 License) +// +func IsULID(str string) bool { + // Check if a base32 encoded ULID is the right length. + if len(str) != ulidEncodedSize { + return false + } + + // Check if all the characters in a base32 encoded ULID are part of the + // expected base32 character set. + if ulidDec[str[0]] == 0xFF || + ulidDec[str[1]] == 0xFF || + ulidDec[str[2]] == 0xFF || + ulidDec[str[3]] == 0xFF || + ulidDec[str[4]] == 0xFF || + ulidDec[str[5]] == 0xFF || + ulidDec[str[6]] == 0xFF || + ulidDec[str[7]] == 0xFF || + ulidDec[str[8]] == 0xFF || + ulidDec[str[9]] == 0xFF || + ulidDec[str[10]] == 0xFF || + ulidDec[str[11]] == 0xFF || + ulidDec[str[12]] == 0xFF || + ulidDec[str[13]] == 0xFF || + ulidDec[str[14]] == 0xFF || + ulidDec[str[15]] == 0xFF || + ulidDec[str[16]] == 0xFF || + ulidDec[str[17]] == 0xFF || + ulidDec[str[18]] == 0xFF || + ulidDec[str[19]] == 0xFF || + ulidDec[str[20]] == 0xFF || + ulidDec[str[21]] == 0xFF || + ulidDec[str[22]] == 0xFF || + ulidDec[str[23]] == 0xFF || + ulidDec[str[24]] == 0xFF || + ulidDec[str[25]] == 0xFF { + return false + } + + // Check if the first character in a base32 encoded ULID will overflow. This + // happens because the base32 representation encodes 130 bits, while the + // ULID is only 128 bits. + // + // See https://github.com/oklog/ulid/issues/9 for details. + if str[0] > '7' { + return false + } + return true +} + +// IsCreditCard checks if the string is a credit card. +func IsCreditCard(str string) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + if !rxCreditCard.MatchString(sanitized) { + return false + } + var sum int64 + var digit string + var tmpNum int64 + var shouldDouble bool + for i := len(sanitized) - 1; i >= 0; i-- { + digit = sanitized[i:(i + 1)] + tmpNum, _ = ToInt(digit) + if shouldDouble { + tmpNum *= 2 + if tmpNum >= 10 { + sum += (tmpNum % 10) + 1 + } else { + sum += tmpNum + } + } else { + sum += tmpNum + } + shouldDouble = !shouldDouble + } + + return sum%10 == 0 +} + +// IsISBN10 checks if the string is an ISBN version 10. +func IsISBN10(str string) bool { + return IsISBN(str, 10) +} + +// IsISBN13 checks if the string is an ISBN version 13. +func IsISBN13(str string) bool { + return IsISBN(str, 13) +} + +// IsISBN checks if the string is an ISBN (version 10 or 13). +// If version value is not equal to 10 or 13, it will be checks both variants. +func IsISBN(str string, version int) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + var checksum int32 + var i int32 + if version == 10 { + if !rxISBN10.MatchString(sanitized) { + return false + } + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(sanitized[i]-'0') + } + if sanitized[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(sanitized[9]-'0') + } + if checksum%11 == 0 { + return true + } + return false + } else if version == 13 { + if !rxISBN13.MatchString(sanitized) { + return false + } + factor := []int32{1, 3} + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(sanitized[i]-'0') + } + return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 + } + return IsISBN(str, 10) || IsISBN(str, 13) +} + +// IsJSON checks if the string is valid JSON (note: uses json.Unmarshal). +func IsJSON(str string) bool { + var js json.RawMessage + return json.Unmarshal([]byte(str), &js) == nil +} + +// IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid. +func IsMultibyte(str string) bool { + if IsNull(str) { + return true + } + return rxMultibyte.MatchString(str) +} + +// IsASCII checks if the string contains ASCII chars only. Empty string is valid. +func IsASCII(str string) bool { + if IsNull(str) { + return true + } + return rxASCII.MatchString(str) +} + +// IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid. +func IsPrintableASCII(str string) bool { + if IsNull(str) { + return true + } + return rxPrintableASCII.MatchString(str) +} + +// IsFullWidth checks if the string contains any full-width chars. Empty string is valid. +func IsFullWidth(str string) bool { + if IsNull(str) { + return true + } + return rxFullWidth.MatchString(str) +} + +// IsHalfWidth checks if the string contains any half-width chars. Empty string is valid. +func IsHalfWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) +} + +// IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid. +func IsVariableWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) +} + +// IsBase64 checks if a string is base64 encoded. +func IsBase64(str string) bool { + return rxBase64.MatchString(str) +} + +// IsFilePath checks is a string is Win or Unix file path and returns it's type. +func IsFilePath(str string) (bool, int) { + if rxWinPath.MatchString(str) { + //check windows path limit see: + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath + if len(str[3:]) > 32767 { + return false, Win + } + return true, Win + } else if rxUnixPath.MatchString(str) { + return true, Unix + } + return false, Unknown +} + +//IsWinFilePath checks both relative & absolute paths in Windows +func IsWinFilePath(str string) bool { + if rxARWinPath.MatchString(str) { + //check windows path limit see: + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath + if len(str[3:]) > 32767 { + return false + } + return true + } + return false +} + +//IsUnixFilePath checks both relative & absolute paths in Unix +func IsUnixFilePath(str string) bool { + if rxARUnixPath.MatchString(str) { + return true + } + return false +} + +// IsDataURI checks if a string is base64 encoded data URI such as an image +func IsDataURI(str string) bool { + dataURI := strings.Split(str, ",") + if !rxDataURI.MatchString(dataURI[0]) { + return false + } + return IsBase64(dataURI[1]) +} + +// IsMagnetURI checks if a string is valid magnet URI +func IsMagnetURI(str string) bool { + return rxMagnetURI.MatchString(str) +} + +// IsISO3166Alpha2 checks if a string is valid two-letter country code +func IsISO3166Alpha2(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO3166Alpha3 checks if a string is valid three-letter country code +func IsISO3166Alpha3(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha3Code { + return true + } + } + return false +} + +// IsISO693Alpha2 checks if a string is valid two-letter language code +func IsISO693Alpha2(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO693Alpha3b checks if a string is valid three-letter language code +func IsISO693Alpha3b(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha3bCode { + return true + } + } + return false +} + +// IsDNSName will validate the given string as a DNS name +func IsDNSName(str string) bool { + if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { + // constraints already violated + return false + } + return !IsIP(str) && rxDNSName.MatchString(str) +} + +// IsHash checks if a string is a hash of type algorithm. +// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'] +func IsHash(str string, algorithm string) bool { + var len string + algo := strings.ToLower(algorithm) + + if algo == "crc32" || algo == "crc32b" { + len = "8" + } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" { + len = "32" + } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" { + len = "40" + } else if algo == "tiger192" { + len = "48" + } else if algo == "sha3-224" { + len = "56" + } else if algo == "sha256" || algo == "sha3-256" { + len = "64" + } else if algo == "sha384" || algo == "sha3-384" { + len = "96" + } else if algo == "sha512" || algo == "sha3-512" { + len = "128" + } else { + return false + } + + return Matches(str, "^[a-f0-9]{"+len+"}$") +} + +// IsSHA3224 checks is a string is a SHA3-224 hash. Alias for `IsHash(str, "sha3-224")` +func IsSHA3224(str string) bool { + return IsHash(str, "sha3-224") +} + +// IsSHA3256 checks is a string is a SHA3-256 hash. Alias for `IsHash(str, "sha3-256")` +func IsSHA3256(str string) bool { + return IsHash(str, "sha3-256") +} + +// IsSHA3384 checks is a string is a SHA3-384 hash. Alias for `IsHash(str, "sha3-384")` +func IsSHA3384(str string) bool { + return IsHash(str, "sha3-384") +} + +// IsSHA3512 checks is a string is a SHA3-512 hash. Alias for `IsHash(str, "sha3-512")` +func IsSHA3512(str string) bool { + return IsHash(str, "sha3-512") +} + +// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")` +func IsSHA512(str string) bool { + return IsHash(str, "sha512") +} + +// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")` +func IsSHA384(str string) bool { + return IsHash(str, "sha384") +} + +// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")` +func IsSHA256(str string) bool { + return IsHash(str, "sha256") +} + +// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")` +func IsTiger192(str string) bool { + return IsHash(str, "tiger192") +} + +// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")` +func IsTiger160(str string) bool { + return IsHash(str, "tiger160") +} + +// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")` +func IsRipeMD160(str string) bool { + return IsHash(str, "ripemd160") +} + +// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")` +func IsSHA1(str string) bool { + return IsHash(str, "sha1") +} + +// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")` +func IsTiger128(str string) bool { + return IsHash(str, "tiger128") +} + +// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")` +func IsRipeMD128(str string) bool { + return IsHash(str, "ripemd128") +} + +// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")` +func IsCRC32(str string) bool { + return IsHash(str, "crc32") +} + +// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")` +func IsCRC32b(str string) bool { + return IsHash(str, "crc32b") +} + +// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")` +func IsMD5(str string) bool { + return IsHash(str, "md5") +} + +// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")` +func IsMD4(str string) bool { + return IsHash(str, "md4") +} + +// IsDialString validates the given string for usage with the various Dial() functions +func IsDialString(str string) bool { + if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) { + return true + } + + return false +} + +// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP` +func IsIP(str string) bool { + return net.ParseIP(str) != nil +} + +// IsPort checks if a string represents a valid port +func IsPort(str string) bool { + if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 { + return true + } + return false +} + +// IsIPv4 checks if the string is an IP version 4. +func IsIPv4(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ".") +} + +// IsIPv6 checks if the string is an IP version 6. +func IsIPv6(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ":") +} + +// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6) +func IsCIDR(str string) bool { + _, _, err := net.ParseCIDR(str) + return err == nil +} + +// IsMAC checks if a string is valid MAC address. +// Possible MAC formats: +// 01:23:45:67:89:ab +// 01:23:45:67:89:ab:cd:ef +// 01-23-45-67-89-ab +// 01-23-45-67-89-ab-cd-ef +// 0123.4567.89ab +// 0123.4567.89ab.cdef +func IsMAC(str string) bool { + _, err := net.ParseMAC(str) + return err == nil +} + +// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name +func IsHost(str string) bool { + return IsIP(str) || IsDNSName(str) +} + +// IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. +func IsMongoID(str string) bool { + return rxHexadecimal.MatchString(str) && (len(str) == 24) +} + +// IsLatitude checks if a string is valid latitude. +func IsLatitude(str string) bool { + return rxLatitude.MatchString(str) +} + +// IsLongitude checks if a string is valid longitude. +func IsLongitude(str string) bool { + return rxLongitude.MatchString(str) +} + +// IsIMEI checks if a string is valid IMEI +func IsIMEI(str string) bool { + return rxIMEI.MatchString(str) +} + +// IsIMSI checks if a string is valid IMSI +func IsIMSI(str string) bool { + if !rxIMSI.MatchString(str) { + return false + } + + mcc, err := strconv.ParseInt(str[0:3], 10, 32) + if err != nil { + return false + } + + switch mcc { + case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219: + case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235: + case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257: + case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278: + case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293: + case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314: + case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346: + case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364: + case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402: + case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417: + case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428: + case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441: + case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467: + case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528: + case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545: + case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555: + case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611: + case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621: + case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631: + case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641: + case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652: + case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708: + case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736: + case 738, 740, 742, 744, 746, 748, 750, 995: + return true + default: + return false + } + return true +} + +// IsRsaPublicKey checks if a string is valid public key with provided length +func IsRsaPublicKey(str string, keylen int) bool { + bb := bytes.NewBufferString(str) + pemBytes, err := ioutil.ReadAll(bb) + if err != nil { + return false + } + block, _ := pem.Decode(pemBytes) + if block != nil && block.Type != "PUBLIC KEY" { + return false + } + var der []byte + + if block != nil { + der = block.Bytes + } else { + der, err = base64.StdEncoding.DecodeString(str) + if err != nil { + return false + } + } + + key, err := x509.ParsePKIXPublicKey(der) + if err != nil { + return false + } + pubkey, ok := key.(*rsa.PublicKey) + if !ok { + return false + } + bitlen := len(pubkey.N.Bytes()) * 8 + return bitlen == int(keylen) +} + +// IsRegex checks if a give string is a valid regex with RE2 syntax or not +func IsRegex(str string) bool { + if _, err := regexp.Compile(str); err == nil { + return true + } + return false +} + +func toJSONName(tag string) string { + if tag == "" { + return "" + } + + // JSON name always comes first. If there's no options then split[0] is + // JSON name, if JSON name is not set, then split[0] is an empty string. + split := strings.SplitN(tag, ",", 2) + + name := split[0] + + // However it is possible that the field is skipped when + // (de-)serializing from/to JSON, in which case assume that there is no + // tag name to use + if name == "-" { + return "" + } + return name +} + +func prependPathToErrors(err error, path string) error { + switch err2 := err.(type) { + case Error: + err2.Path = append([]string{path}, err2.Path...) + return err2 + case Errors: + errors := err2.Errors() + for i, err3 := range errors { + errors[i] = prependPathToErrors(err3, path) + } + return err2 + } + return err +} + +// ValidateArray performs validation according to condition iterator that validates every element of the array +func ValidateArray(array []interface{}, iterator ConditionIterator) bool { + return Every(array, iterator) +} + +// ValidateMap use validation map for fields. +// result will be equal to `false` if there are any errors. +// s is the map containing the data to be validated. +// m is the validation map in the form: +// map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}} +func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) { + if s == nil { + return true, nil + } + result := true + var err error + var errs Errors + var index int + val := reflect.ValueOf(s) + for key, value := range s { + presentResult := true + validator, ok := m[key] + if !ok { + presentResult = false + var err error + err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key) + err = prependPathToErrors(err, key) + errs = append(errs, err) + } + valueField := reflect.ValueOf(value) + mapResult := true + typeResult := true + structResult := true + resultField := true + switch subValidator := validator.(type) { + case map[string]interface{}: + var err error + if v, ok := value.(map[string]interface{}); !ok { + mapResult = false + err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String()) + err = prependPathToErrors(err, key) + errs = append(errs, err) + } else { + mapResult, err = ValidateMap(v, subValidator) + if err != nil { + mapResult = false + err = prependPathToErrors(err, key) + errs = append(errs, err) + } + } + case string: + if (valueField.Kind() == reflect.Struct || + (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && + subValidator != "-" { + var err error + structResult, err = ValidateStruct(valueField.Interface()) + if err != nil { + err = prependPathToErrors(err, key) + errs = append(errs, err) + } + } + resultField, err = typeCheck(valueField, reflect.StructField{ + Name: key, + PkgPath: "", + Type: val.Type(), + Tag: reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)), + Offset: 0, + Index: []int{index}, + Anonymous: false, + }, val, nil) + if err != nil { + errs = append(errs, err) + } + case nil: + // already handlerd when checked before + default: + typeResult = false + err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String()) + err = prependPathToErrors(err, key) + errs = append(errs, err) + } + result = result && presentResult && typeResult && resultField && structResult && mapResult + index++ + } + // checks required keys + requiredResult := true + for key, value := range m { + if schema, ok := value.(string); ok { + tags := parseTagIntoMap(schema) + if required, ok := tags["required"]; ok { + if _, ok := s[key]; !ok { + requiredResult = false + if required.customErrorMessage != "" { + err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}} + } else { + err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}} + } + errs = append(errs, err) + } + } + } + } + + if len(errs) > 0 { + err = errs + } + return result && requiredResult, err +} + +// ValidateStruct use tags for fields. +// result will be equal to `false` if there are any errors. +// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail) +func ValidateStruct(s interface{}) (bool, error) { + if s == nil { + return true, nil + } + result := true + var err error + val := reflect.ValueOf(s) + if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { + val = val.Elem() + } + // we only accept structs + if val.Kind() != reflect.Struct { + return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) + } + var errs Errors + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + if typeField.PkgPath != "" { + continue // Private field + } + structResult := true + if valueField.Kind() == reflect.Interface { + valueField = valueField.Elem() + } + if (valueField.Kind() == reflect.Struct || + (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && + typeField.Tag.Get(tagName) != "-" { + var err error + structResult, err = ValidateStruct(valueField.Interface()) + if err != nil { + err = prependPathToErrors(err, typeField.Name) + errs = append(errs, err) + } + } + resultField, err2 := typeCheck(valueField, typeField, val, nil) + if err2 != nil { + + // Replace structure name with JSON name if there is a tag on the variable + jsonTag := toJSONName(typeField.Tag.Get("json")) + if jsonTag != "" { + switch jsonError := err2.(type) { + case Error: + jsonError.Name = jsonTag + err2 = jsonError + case Errors: + for i2, err3 := range jsonError { + switch customErr := err3.(type) { + case Error: + customErr.Name = jsonTag + jsonError[i2] = customErr + } + } + + err2 = jsonError + } + } + + errs = append(errs, err2) + } + result = result && resultField && structResult + } + if len(errs) > 0 { + err = errs + } + return result, err +} + +// ValidateStructAsync performs async validation of the struct and returns results through the channels +func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) { + res := make(chan bool) + errors := make(chan error) + + go func() { + defer close(res) + defer close(errors) + + isValid, isFailed := ValidateStruct(s) + + res <- isValid + errors <- isFailed + }() + + return res, errors +} + +// ValidateMapAsync performs async validation of the map and returns results through the channels +func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) { + res := make(chan bool) + errors := make(chan error) + + go func() { + defer close(res) + defer close(errors) + + isValid, isFailed := ValidateMap(s, m) + + res <- isValid + errors <- isFailed + }() + + return res, errors +} + +// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} +func parseTagIntoMap(tag string) tagOptionsMap { + optionsMap := make(tagOptionsMap) + options := strings.Split(tag, ",") + + for i, option := range options { + option = strings.TrimSpace(option) + + validationOptions := strings.Split(option, "~") + if !isValidTag(validationOptions[0]) { + continue + } + if len(validationOptions) == 2 { + optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i} + } else { + optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i} + } + } + return optionsMap +} + +func isValidTag(s string) bool { + if s == "" { + return false + } + for _, c := range s { + switch { + case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + // Backslash and quote chars are reserved, but + // otherwise any punctuation chars are allowed + // in a tag name. + default: + if !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + return true +} + +// IsSSN will validate the given string as a U.S. Social Security Number +func IsSSN(str string) bool { + if str == "" || len(str) != 11 { + return false + } + return rxSSN.MatchString(str) +} + +// IsSemver checks if string is valid semantic version +func IsSemver(str string) bool { + return rxSemver.MatchString(str) +} + +// IsType checks if interface is of some type +func IsType(v interface{}, params ...string) bool { + if len(params) == 1 { + typ := params[0] + return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1) + } + return false +} + +// IsTime checks if string is valid according to given format +func IsTime(str string, format string) bool { + _, err := time.Parse(format, str) + return err == nil +} + +// IsUnixTime checks if string is valid unix timestamp value +func IsUnixTime(str string) bool { + if _, err := strconv.Atoi(str); err == nil { + return true + } + return false +} + +// IsRFC3339 checks if string is valid timestamp value according to RFC3339 +func IsRFC3339(str string) bool { + return IsTime(str, time.RFC3339) +} + +// IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone. +func IsRFC3339WithoutZone(str string) bool { + return IsTime(str, rfc3339WithoutZone) +} + +// IsISO4217 checks if string is valid ISO currency code +func IsISO4217(str string) bool { + for _, currency := range ISO4217List { + if str == currency { + return true + } + } + + return false +} + +// ByteLength checks string's length +func ByteLength(str string, params ...string) bool { + if len(params) == 2 { + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return len(str) >= int(min) && len(str) <= int(max) + } + + return false +} + +// RuneLength checks string's length +// Alias for StringLength +func RuneLength(str string, params ...string) bool { + return StringLength(str, params...) +} + +// IsRsaPub checks whether string is valid RSA key +// Alias for IsRsaPublicKey +func IsRsaPub(str string, params ...string) bool { + if len(params) == 1 { + len, _ := ToInt(params[0]) + return IsRsaPublicKey(str, int(len)) + } + + return false +} + +// StringMatches checks if a string matches a given pattern. +func StringMatches(s string, params ...string) bool { + if len(params) == 1 { + pattern := params[0] + return Matches(s, pattern) + } + return false +} + +// StringLength checks string's length (including multi byte strings) +func StringLength(str string, params ...string) bool { + + if len(params) == 2 { + strLength := utf8.RuneCountInString(str) + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return strLength >= int(min) && strLength <= int(max) + } + + return false +} + +// MinStringLength checks string's minimum length (including multi byte strings) +func MinStringLength(str string, params ...string) bool { + + if len(params) == 1 { + strLength := utf8.RuneCountInString(str) + min, _ := ToInt(params[0]) + return strLength >= int(min) + } + + return false +} + +// MaxStringLength checks string's maximum length (including multi byte strings) +func MaxStringLength(str string, params ...string) bool { + + if len(params) == 1 { + strLength := utf8.RuneCountInString(str) + max, _ := ToInt(params[0]) + return strLength <= int(max) + } + + return false +} + +// Range checks string's length +func Range(str string, params ...string) bool { + if len(params) == 2 { + value, _ := ToFloat(str) + min, _ := ToFloat(params[0]) + max, _ := ToFloat(params[1]) + return InRange(value, min, max) + } + + return false +} + +// IsInRaw checks if string is in list of allowed values +func IsInRaw(str string, params ...string) bool { + if len(params) == 1 { + rawParams := params[0] + + parsedParams := strings.Split(rawParams, "|") + + return IsIn(str, parsedParams...) + } + + return false +} + +// IsIn checks if string str is a member of the set of strings params +func IsIn(str string, params ...string) bool { + for _, param := range params { + if str == param { + return true + } + } + + return false +} + +func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { + if nilPtrAllowedByRequired { + k := v.Kind() + if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() { + return true, nil + } + } + + if requiredOption, isRequired := options["required"]; isRequired { + if len(requiredOption.customErrorMessage) > 0 { + return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}} + } + return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}} + } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { + return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}} + } + // not required and empty is valid + return true, nil +} + +func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { + if !v.IsValid() { + return false, nil + } + + tag := t.Tag.Get(tagName) + + // checks if the field should be ignored + switch tag { + case "": + if v.Kind() != reflect.Slice && v.Kind() != reflect.Map { + if !fieldsRequiredByDefault { + return true, nil + } + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}} + } + case "-": + return true, nil + } + + isRootType := false + if options == nil { + isRootType = true + options = parseTagIntoMap(tag) + } + + if isEmptyValue(v) { + // an empty value is not validated, checks only required + isValid, resultErr = checkRequired(v, t, options) + for key := range options { + delete(options, key) + } + return isValid, resultErr + } + + var customTypeErrors Errors + optionsOrder := options.orderedKeys() + for _, validatorName := range optionsOrder { + validatorStruct := options[validatorName] + if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { + delete(options, validatorName) + + if result := validatefunc(v.Interface(), o.Interface()); !result { + if len(validatorStruct.customErrorMessage) > 0 { + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)}) + continue + } + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)}) + } + } + } + + if len(customTypeErrors.Errors()) > 0 { + return false, customTypeErrors + } + + if isRootType { + // Ensure that we've checked the value by all specified validators before report that the value is valid + defer func() { + delete(options, "optional") + delete(options, "required") + + if isValid && resultErr == nil && len(options) != 0 { + optionsOrder := options.orderedKeys() + for _, validator := range optionsOrder { + isValid = false + resultErr = Error{t.Name, fmt.Errorf( + "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}} + return + } + } + }() + } + + for _, validatorSpec := range optionsOrder { + validatorStruct := options[validatorSpec] + var negate bool + validator := validatorSpec + customMsgExists := len(validatorStruct.customErrorMessage) > 0 + + // checks whether the tag looks like '!something' or 'something' + if validator[0] == '!' { + validator = validator[1:] + negate = true + } + + // checks for interface param validators + for key, value := range InterfaceParamTagRegexMap { + ps := value.FindStringSubmatch(validator) + if len(ps) == 0 { + continue + } + + validatefunc, ok := InterfaceParamTagMap[key] + if !ok { + continue + } + + delete(options, validatorSpec) + + field := fmt.Sprint(v) + if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) { + if customMsgExists { + return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + } + } + + switch v.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, + reflect.String: + // for each tag option checks the map of validator functions + for _, validatorSpec := range optionsOrder { + validatorStruct := options[validatorSpec] + var negate bool + validator := validatorSpec + customMsgExists := len(validatorStruct.customErrorMessage) > 0 + + // checks whether the tag looks like '!something' or 'something' + if validator[0] == '!' { + validator = validator[1:] + negate = true + } + + // checks for param validators + for key, value := range ParamTagRegexMap { + ps := value.FindStringSubmatch(validator) + if len(ps) == 0 { + continue + } + + validatefunc, ok := ParamTagMap[key] + if !ok { + continue + } + + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { + if customMsgExists { + return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + default: + // type not yet supported, fail + return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}} + } + } + + if validatefunc, ok := TagMap[validator]; ok { + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field); !result && !negate || result && negate { + if customMsgExists { + return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + default: + //Not Yet Supported Types (Fail here!) + err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) + return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}} + } + } + } + return true, nil + case reflect.Map: + if v.Type().Key().Kind() != reflect.String { + return false, &UnsupportedTypeError{v.Type()} + } + var sv stringValues + sv = v.MapKeys() + sort.Sort(sv) + result := true + for i, k := range sv { + var resultItem bool + var err error + if v.MapIndex(k).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.MapIndex(k), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.MapIndex(k).Interface()) + if err != nil { + err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string)) + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Slice, reflect.Array: + result := true + for i := 0; i < v.Len(); i++ { + var resultItem bool + var err error + if v.Index(i).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.Index(i), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.Index(i).Interface()) + if err != nil { + err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i)) + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Interface: + // If the value is an interface then encode its element + if v.IsNil() { + return true, nil + } + return ValidateStruct(v.Interface()) + case reflect.Ptr: + // If the value is a pointer then checks its element + if v.IsNil() { + return true, nil + } + return typeCheck(v.Elem(), t, o, options) + case reflect.Struct: + return true, nil + default: + return false, &UnsupportedTypeError{v.Type()} + } +} + +func stripParams(validatorString string) string { + return paramsRegexp.ReplaceAllString(validatorString, "") +} + +// isEmptyValue checks whether value empty or not +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.String, reflect.Array: + return v.Len() == 0 + case reflect.Map, reflect.Slice: + return v.Len() == 0 || v.IsNil() + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) +} + +// ErrorByField returns error for specified field of the struct +// validated by ValidateStruct or empty string if there are no errors +// or this field doesn't exists or doesn't have any errors. +func ErrorByField(e error, field string) string { + if e == nil { + return "" + } + return ErrorsByField(e)[field] +} + +// ErrorsByField returns map of errors of the struct validated +// by ValidateStruct or empty map if there are no errors. +func ErrorsByField(e error) map[string]string { + m := make(map[string]string) + if e == nil { + return m + } + // prototype for ValidateStruct + + switch e := e.(type) { + case Error: + m[e.Name] = e.Err.Error() + case Errors: + for _, item := range e.Errors() { + n := ErrorsByField(item) + for k, v := range n { + m[k] = v + } + } + } + + return m +} + +// Error returns string equivalent for reflect.Type +func (e *UnsupportedTypeError) Error() string { + return "validator: unsupported type: " + e.Type.String() +} + +func (sv stringValues) Len() int { return len(sv) } +func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } +func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } +func (sv stringValues) get(i int) string { return sv[i].String() } + +func IsE164(str string) bool { + return rxE164.MatchString(str) +} diff --git a/vendor/github.com/asaskevich/govalidator/wercker.yml b/vendor/github.com/asaskevich/govalidator/wercker.yml new file mode 100644 index 00000000..bc5f7b08 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/wercker.yml @@ -0,0 +1,15 @@ +box: golang +build: + steps: + - setup-go-workspace + + - script: + name: go get + code: | + go version + go get -t ./... + + - script: + name: go test + code: | + go test -race -v ./... diff --git a/vendor/github.com/blang/semver/.travis.yml b/vendor/github.com/blang/semver/.travis.yml deleted file mode 100644 index 102fb9a6..00000000 --- a/vendor/github.com/blang/semver/.travis.yml +++ /dev/null @@ -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= diff --git a/vendor/github.com/blang/semver/README.md b/vendor/github.com/blang/semver/README.md deleted file mode 100644 index 08b2e4a3..00000000 --- a/vendor/github.com/blang/semver/README.md +++ /dev/null @@ -1,194 +0,0 @@ -semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](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. diff --git a/vendor/github.com/blang/semver/json.go b/vendor/github.com/blang/semver/json.go deleted file mode 100644 index a74bf7c4..00000000 --- a/vendor/github.com/blang/semver/json.go +++ /dev/null @@ -1,23 +0,0 @@ -package semver - -import ( - "encoding/json" -) - -// MarshalJSON implements the encoding/json.Marshaler interface. -func (v Version) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -// UnmarshalJSON implements the encoding/json.Unmarshaler interface. -func (v *Version) UnmarshalJSON(data []byte) (err error) { - var versionString string - - if err = json.Unmarshal(data, &versionString); err != nil { - return - } - - *v, err = Parse(versionString) - - return -} diff --git a/vendor/github.com/blang/semver/package.json b/vendor/github.com/blang/semver/package.json deleted file mode 100644 index 1cf8ebdd..00000000 --- a/vendor/github.com/blang/semver/package.json +++ /dev/null @@ -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" -} - diff --git a/vendor/github.com/blang/semver/range.go b/vendor/github.com/blang/semver/range.go deleted file mode 100644 index fca406d4..00000000 --- a/vendor/github.com/blang/semver/range.go +++ /dev/null @@ -1,416 +0,0 @@ -package semver - -import ( - "fmt" - "strconv" - "strings" - "unicode" -) - -type wildcardType int - -const ( - noneWildcard wildcardType = iota - majorWildcard wildcardType = 1 - minorWildcard wildcardType = 2 - patchWildcard wildcardType = 3 -) - -func wildcardTypefromInt(i int) wildcardType { - switch i { - case 1: - return majorWildcard - case 2: - return minorWildcard - case 3: - return patchWildcard - default: - return noneWildcard - } -} - -type comparator func(Version, Version) bool - -var ( - compEQ comparator = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) == 0 - } - compNE = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) != 0 - } - compGT = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) == 1 - } - compGE = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) >= 0 - } - compLT = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) == -1 - } - compLE = func(v1 Version, v2 Version) bool { - return v1.Compare(v2) <= 0 - } -) - -type versionRange struct { - v Version - c comparator -} - -// rangeFunc creates a Range from the given versionRange. -func (vr *versionRange) rangeFunc() Range { - return Range(func(v Version) bool { - return vr.c(v, vr.v) - }) -} - -// Range represents a range of versions. -// A Range can be used to check if a Version satisfies it: -// -// range, err := semver.ParseRange(">1.0.0 <2.0.0") -// range(semver.MustParse("1.1.1") // returns true -type Range func(Version) bool - -// OR combines the existing Range with another Range using logical OR. -func (rf Range) OR(f Range) Range { - return Range(func(v Version) bool { - return rf(v) || f(v) - }) -} - -// AND combines the existing Range with another Range using logical AND. -func (rf Range) AND(f Range) Range { - return Range(func(v Version) bool { - return rf(v) && f(v) - }) -} - -// ParseRange parses a range and returns a Range. -// If the range could not be parsed an error is returned. -// -// Valid ranges are: -// - "<1.0.0" -// - "<=1.0.0" -// - ">1.0.0" -// - ">=1.0.0" -// - "1.0.0", "=1.0.0", "==1.0.0" -// - "!1.0.0", "!=1.0.0" -// -// A Range can consist of 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` -func ParseRange(s string) (Range, error) { - parts := splitAndTrim(s) - orParts, err := splitORParts(parts) - if err != nil { - return nil, err - } - expandedParts, err := expandWildcardVersion(orParts) - if err != nil { - return nil, err - } - var orFn Range - for _, p := range expandedParts { - var andFn Range - for _, ap := range p { - opStr, vStr, err := splitComparatorVersion(ap) - if err != nil { - return nil, err - } - vr, err := buildVersionRange(opStr, vStr) - if err != nil { - return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) - } - rf := vr.rangeFunc() - - // Set function - if andFn == nil { - andFn = rf - } else { // Combine with existing function - andFn = andFn.AND(rf) - } - } - if orFn == nil { - orFn = andFn - } else { - orFn = orFn.OR(andFn) - } - - } - return orFn, nil -} - -// splitORParts splits the already cleaned parts by '||'. -// Checks for invalid positions of the operator and returns an -// error if found. -func splitORParts(parts []string) ([][]string, error) { - var ORparts [][]string - last := 0 - for i, p := range parts { - if p == "||" { - if i == 0 { - return nil, fmt.Errorf("First element in range is '||'") - } - ORparts = append(ORparts, parts[last:i]) - last = i + 1 - } - } - if last == len(parts) { - return nil, fmt.Errorf("Last element in range is '||'") - } - ORparts = append(ORparts, parts[last:]) - return ORparts, nil -} - -// buildVersionRange takes a slice of 2: operator and version -// and builds a versionRange, otherwise an error. -func buildVersionRange(opStr, vStr string) (*versionRange, error) { - c := parseComparator(opStr) - if c == nil { - return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) - } - v, err := Parse(vStr) - if err != nil { - return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) - } - - return &versionRange{ - v: v, - c: c, - }, nil - -} - -// inArray checks if a byte is contained in an array of bytes -func inArray(s byte, list []byte) bool { - for _, el := range list { - if el == s { - return true - } - } - return false -} - -// splitAndTrim splits a range string by spaces and cleans whitespaces -func splitAndTrim(s string) (result []string) { - last := 0 - var lastChar byte - excludeFromSplit := []byte{'>', '<', '='} - for i := 0; i < len(s); i++ { - if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { - if last < i-1 { - result = append(result, s[last:i]) - } - last = i + 1 - } else if s[i] != ' ' { - lastChar = s[i] - } - } - if last < len(s)-1 { - result = append(result, s[last:]) - } - - for i, v := range result { - result[i] = strings.Replace(v, " ", "", -1) - } - - // parts := strings.Split(s, " ") - // for _, x := range parts { - // if s := strings.TrimSpace(x); len(s) != 0 { - // result = append(result, s) - // } - // } - return -} - -// splitComparatorVersion splits the comparator from the version. -// Input must be free of leading or trailing spaces. -func splitComparatorVersion(s string) (string, string, error) { - i := strings.IndexFunc(s, unicode.IsDigit) - if i == -1 { - return "", "", fmt.Errorf("Could not get version from string: %q", s) - } - return strings.TrimSpace(s[0:i]), s[i:], nil -} - -// getWildcardType will return the type of wildcard that the -// passed version contains -func getWildcardType(vStr string) wildcardType { - parts := strings.Split(vStr, ".") - nparts := len(parts) - wildcard := parts[nparts-1] - - possibleWildcardType := wildcardTypefromInt(nparts) - if wildcard == "x" { - return possibleWildcardType - } - - return noneWildcard -} - -// createVersionFromWildcard will convert a wildcard version -// into a regular version, replacing 'x's with '0's, handling -// special cases like '1.x.x' and '1.x' -func createVersionFromWildcard(vStr string) string { - // handle 1.x.x - vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) - vStr2 = strings.Replace(vStr2, ".x", ".0", 1) - parts := strings.Split(vStr2, ".") - - // handle 1.x - if len(parts) == 2 { - return vStr2 + ".0" - } - - return vStr2 -} - -// incrementMajorVersion will increment the major version -// of the passed version -func incrementMajorVersion(vStr string) (string, error) { - parts := strings.Split(vStr, ".") - i, err := strconv.Atoi(parts[0]) - if err != nil { - return "", err - } - parts[0] = strconv.Itoa(i + 1) - - return strings.Join(parts, "."), nil -} - -// incrementMajorVersion will increment the minor version -// of the passed version -func incrementMinorVersion(vStr string) (string, error) { - parts := strings.Split(vStr, ".") - i, err := strconv.Atoi(parts[1]) - if err != nil { - return "", err - } - parts[1] = strconv.Itoa(i + 1) - - return strings.Join(parts, "."), nil -} - -// expandWildcardVersion will expand wildcards inside versions -// following these rules: -// -// * when dealing with patch wildcards: -// >= 1.2.x will become >= 1.2.0 -// <= 1.2.x will become < 1.3.0 -// > 1.2.x will become >= 1.3.0 -// < 1.2.x will become < 1.2.0 -// != 1.2.x will become < 1.2.0 >= 1.3.0 -// -// * when dealing with minor wildcards: -// >= 1.x will become >= 1.0.0 -// <= 1.x will become < 2.0.0 -// > 1.x will become >= 2.0.0 -// < 1.0 will become < 1.0.0 -// != 1.x will become < 1.0.0 >= 2.0.0 -// -// * when dealing with wildcards without -// version operator: -// 1.2.x will become >= 1.2.0 < 1.3.0 -// 1.x will become >= 1.0.0 < 2.0.0 -func expandWildcardVersion(parts [][]string) ([][]string, error) { - var expandedParts [][]string - for _, p := range parts { - var newParts []string - for _, ap := range p { - if strings.Index(ap, "x") != -1 { - opStr, vStr, err := splitComparatorVersion(ap) - if err != nil { - return nil, err - } - - versionWildcardType := getWildcardType(vStr) - flatVersion := createVersionFromWildcard(vStr) - - var resultOperator string - var shouldIncrementVersion bool - switch opStr { - case ">": - resultOperator = ">=" - shouldIncrementVersion = true - case ">=": - resultOperator = ">=" - case "<": - resultOperator = "<" - case "<=": - resultOperator = "<" - shouldIncrementVersion = true - case "", "=", "==": - newParts = append(newParts, ">="+flatVersion) - resultOperator = "<" - shouldIncrementVersion = true - case "!=", "!": - newParts = append(newParts, "<"+flatVersion) - resultOperator = ">=" - shouldIncrementVersion = true - } - - var resultVersion string - if shouldIncrementVersion { - switch versionWildcardType { - case patchWildcard: - resultVersion, _ = incrementMinorVersion(flatVersion) - case minorWildcard: - resultVersion, _ = incrementMajorVersion(flatVersion) - } - } else { - resultVersion = flatVersion - } - - ap = resultOperator + resultVersion - } - newParts = append(newParts, ap) - } - expandedParts = append(expandedParts, newParts) - } - - return expandedParts, nil -} - -func parseComparator(s string) comparator { - switch s { - case "==": - fallthrough - case "": - fallthrough - case "=": - return compEQ - case ">": - return compGT - case ">=": - return compGE - case "<": - return compLT - case "<=": - return compLE - case "!": - fallthrough - case "!=": - return compNE - } - - return nil -} - -// MustParseRange is like ParseRange but panics if the range cannot be parsed. -func MustParseRange(s string) Range { - r, err := ParseRange(s) - if err != nil { - panic(`semver: ParseRange(` + s + `): ` + err.Error()) - } - return r -} diff --git a/vendor/github.com/blang/semver/semver.go b/vendor/github.com/blang/semver/semver.go deleted file mode 100644 index 8ee0842e..00000000 --- a/vendor/github.com/blang/semver/semver.go +++ /dev/null @@ -1,418 +0,0 @@ -package semver - -import ( - "errors" - "fmt" - "strconv" - "strings" -) - -const ( - numbers string = "0123456789" - alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" - alphanum = alphas + numbers -) - -// SpecVersion is the latest fully supported spec version of semver -var SpecVersion = Version{ - Major: 2, - Minor: 0, - Patch: 0, -} - -// Version represents a semver compatible version -type Version struct { - Major uint64 - Minor uint64 - Patch uint64 - Pre []PRVersion - Build []string //No Precendence -} - -// Version to string -func (v Version) String() 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) - - if len(v.Pre) > 0 { - b = append(b, '-') - b = append(b, v.Pre[0].String()...) - - for _, pre := range v.Pre[1:] { - b = append(b, '.') - b = append(b, pre.String()...) - } - } - - if len(v.Build) > 0 { - b = append(b, '+') - b = append(b, v.Build[0]...) - - for _, build := range v.Build[1:] { - b = append(b, '.') - b = append(b, build...) - } - } - - return string(b) -} - -// Equals checks if v is equal to o. -func (v Version) Equals(o Version) bool { - return (v.Compare(o) == 0) -} - -// EQ checks if v is equal to o. -func (v Version) EQ(o Version) bool { - return (v.Compare(o) == 0) -} - -// NE checks if v is not equal to o. -func (v Version) NE(o Version) bool { - return (v.Compare(o) != 0) -} - -// GT checks if v is greater than o. -func (v Version) GT(o Version) bool { - return (v.Compare(o) == 1) -} - -// GTE checks if v is greater than or equal to o. -func (v Version) GTE(o Version) bool { - return (v.Compare(o) >= 0) -} - -// GE checks if v is greater than or equal to o. -func (v Version) GE(o Version) bool { - return (v.Compare(o) >= 0) -} - -// LT checks if v is less than o. -func (v Version) LT(o Version) bool { - return (v.Compare(o) == -1) -} - -// LTE checks if v is less than or equal to o. -func (v Version) LTE(o Version) bool { - return (v.Compare(o) <= 0) -} - -// LE checks if v is less than or equal to o. -func (v Version) LE(o Version) bool { - return (v.Compare(o) <= 0) -} - -// Compare compares Versions v to o: -// -1 == v is less than o -// 0 == v is equal to o -// 1 == v is greater than o -func (v Version) Compare(o Version) int { - if v.Major != o.Major { - if v.Major > o.Major { - return 1 - } - return -1 - } - if v.Minor != o.Minor { - if v.Minor > o.Minor { - return 1 - } - return -1 - } - if v.Patch != o.Patch { - if v.Patch > o.Patch { - return 1 - } - return -1 - } - - // Quick comparison if a version has no prerelease versions - if len(v.Pre) == 0 && len(o.Pre) == 0 { - return 0 - } else if len(v.Pre) == 0 && len(o.Pre) > 0 { - return 1 - } else if len(v.Pre) > 0 && len(o.Pre) == 0 { - return -1 - } - - i := 0 - for ; i < len(v.Pre) && i < len(o.Pre); i++ { - if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { - continue - } else if comp == 1 { - return 1 - } else { - return -1 - } - } - - // If all pr versions are the equal but one has further prversion, this one greater - if i == len(v.Pre) && i == len(o.Pre) { - return 0 - } else if i == len(v.Pre) && i < len(o.Pre) { - return -1 - } else { - return 1 - } - -} - -// Validate validates v and returns error in case -func (v Version) Validate() error { - // Major, Minor, Patch already validated using uint64 - - for _, pre := range v.Pre { - if !pre.IsNum { //Numeric prerelease versions already uint64 - if len(pre.VersionStr) == 0 { - return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) - } - if !containsOnly(pre.VersionStr, alphanum) { - return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) - } - } - } - - for _, build := range v.Build { - if len(build) == 0 { - return fmt.Errorf("Build meta data can not be empty %q", build) - } - if !containsOnly(build, alphanum) { - return fmt.Errorf("Invalid character(s) found in build meta data %q", build) - } - } - - return nil -} - -// 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) { - v, err := Parse(s) - vp = &v - return -} - -// Make is an alias for Parse, parses version string and returns a validated Version or error -func Make(s string) (Version, error) { - return Parse(s) -} - -// 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 -// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions -// with only major and minor components specified -func ParseTolerant(s string) (Version, error) { - s = strings.TrimSpace(s) - s = strings.TrimPrefix(s, "v") - - // Split into major.minor.(patch+pr+meta) - parts := strings.SplitN(s, ".", 3) - if len(parts) < 3 { - if strings.ContainsAny(parts[len(parts)-1], "+-") { - return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") - } - for len(parts) < 3 { - parts = append(parts, "0") - } - s = strings.Join(parts, ".") - } - - return Parse(s) -} - -// Parse parses version string and returns a validated Version or error -func Parse(s string) (Version, error) { - if len(s) == 0 { - return Version{}, errors.New("Version string empty") - } - - // Split into major.minor.(patch+pr+meta) - parts := strings.SplitN(s, ".", 3) - if len(parts) != 3 { - return Version{}, errors.New("No Major.Minor.Patch elements found") - } - - // Major - if !containsOnly(parts[0], numbers) { - return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) - } - if hasLeadingZeroes(parts[0]) { - return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) - } - major, err := strconv.ParseUint(parts[0], 10, 64) - if err != nil { - return Version{}, err - } - - // Minor - if !containsOnly(parts[1], numbers) { - return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) - } - if hasLeadingZeroes(parts[1]) { - return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) - } - minor, err := strconv.ParseUint(parts[1], 10, 64) - if err != nil { - return Version{}, err - } - - v := Version{} - v.Major = major - v.Minor = minor - - var build, prerelease []string - patchStr := parts[2] - - if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { - build = strings.Split(patchStr[buildIndex+1:], ".") - patchStr = patchStr[:buildIndex] - } - - if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { - prerelease = strings.Split(patchStr[preIndex+1:], ".") - patchStr = patchStr[:preIndex] - } - - if !containsOnly(patchStr, numbers) { - return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) - } - if hasLeadingZeroes(patchStr) { - return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) - } - patch, err := strconv.ParseUint(patchStr, 10, 64) - if err != nil { - return Version{}, err - } - - v.Patch = patch - - // Prerelease - for _, prstr := range prerelease { - parsedPR, err := NewPRVersion(prstr) - if err != nil { - return Version{}, err - } - v.Pre = append(v.Pre, parsedPR) - } - - // Build meta data - for _, str := range build { - if len(str) == 0 { - return Version{}, errors.New("Build meta data is empty") - } - if !containsOnly(str, alphanum) { - return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) - } - v.Build = append(v.Build, str) - } - - return v, nil -} - -// MustParse is like Parse but panics if the version cannot be parsed. -func MustParse(s string) Version { - v, err := Parse(s) - if err != nil { - panic(`semver: Parse(` + s + `): ` + err.Error()) - } - return v -} - -// PRVersion represents a PreRelease Version -type PRVersion struct { - VersionStr string - VersionNum uint64 - IsNum bool -} - -// NewPRVersion creates a new valid prerelease version -func NewPRVersion(s string) (PRVersion, error) { - if len(s) == 0 { - return PRVersion{}, errors.New("Prerelease is empty") - } - v := PRVersion{} - if containsOnly(s, numbers) { - if hasLeadingZeroes(s) { - return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) - } - num, err := strconv.ParseUint(s, 10, 64) - - // Might never be hit, but just in case - if err != nil { - return PRVersion{}, err - } - v.VersionNum = num - v.IsNum = true - } else if containsOnly(s, alphanum) { - v.VersionStr = s - v.IsNum = false - } else { - return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) - } - return v, nil -} - -// IsNumeric checks if prerelease-version is numeric -func (v PRVersion) IsNumeric() bool { - return v.IsNum -} - -// Compare compares two PreRelease Versions v and o: -// -1 == v is less than o -// 0 == v is equal to o -// 1 == v is greater than o -func (v PRVersion) Compare(o PRVersion) int { - if v.IsNum && !o.IsNum { - return -1 - } else if !v.IsNum && o.IsNum { - return 1 - } else if v.IsNum && o.IsNum { - if v.VersionNum == o.VersionNum { - return 0 - } else if v.VersionNum > o.VersionNum { - return 1 - } else { - return -1 - } - } else { // both are Alphas - if v.VersionStr == o.VersionStr { - return 0 - } else if v.VersionStr > o.VersionStr { - return 1 - } else { - return -1 - } - } -} - -// PreRelease version to string -func (v PRVersion) String() string { - if v.IsNum { - return strconv.FormatUint(v.VersionNum, 10) - } - return v.VersionStr -} - -func containsOnly(s string, set string) bool { - return strings.IndexFunc(s, func(r rune) bool { - return !strings.ContainsRune(set, r) - }) == -1 -} - -func hasLeadingZeroes(s string) bool { - return len(s) > 1 && s[0] == '0' -} - -// NewBuildVersion creates a new valid build version -func NewBuildVersion(s string) (string, error) { - if len(s) == 0 { - return "", errors.New("Buildversion is empty") - } - if !containsOnly(s, alphanum) { - return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) - } - return s, nil -} diff --git a/vendor/github.com/blang/semver/sort.go b/vendor/github.com/blang/semver/sort.go deleted file mode 100644 index e18f8808..00000000 --- a/vendor/github.com/blang/semver/sort.go +++ /dev/null @@ -1,28 +0,0 @@ -package semver - -import ( - "sort" -) - -// Versions represents multiple versions. -type Versions []Version - -// Len returns length of version collection -func (s Versions) Len() int { - return len(s) -} - -// Swap swaps two versions inside the collection by its indices -func (s Versions) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Less checks if version at index i is less than version at index j -func (s Versions) Less(i, j int) bool { - return s[i].LT(s[j]) -} - -// Sort sorts a slice of versions -func Sort(versions []Version) { - sort.Sort(Versions(versions)) -} diff --git a/vendor/github.com/blang/semver/sql.go b/vendor/github.com/blang/semver/sql.go deleted file mode 100644 index eb4d8026..00000000 --- a/vendor/github.com/blang/semver/sql.go +++ /dev/null @@ -1,30 +0,0 @@ -package semver - -import ( - "database/sql/driver" - "fmt" -) - -// Scan implements the database/sql.Scanner interface. -func (v *Version) Scan(src interface{}) (err error) { - var str string - switch src := src.(type) { - case string: - str = src - case []byte: - str = string(src) - default: - return fmt.Errorf("Version.Scan: cannot convert %T to string.", src) - } - - if t, err := Parse(str); err == nil { - *v = t - } - - return -} - -// Value implements the database/sql/driver.Valuer interface. -func (v Version) Value() (driver.Value, error) { - return v.String(), nil -} diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore b/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore deleted file mode 100644 index 1b87ff10..00000000 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -test/test -test/test.coverage -test/piggie/piggie -test/phaul/phaul -test/phaul/phaul.coverage -image diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile b/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile deleted file mode 100644 index 67c43a05..00000000 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile +++ /dev/null @@ -1,107 +0,0 @@ -SHELL = /bin/bash -GO ?= go -CC ?= gcc -COVERAGE_PATH ?= $(shell pwd)/.coverage -CRIU_FEATURE_MEM_TRACK = $(shell if criu check --feature mem_dirty_track > /dev/null; then echo 1; else echo 0; fi) -CRIU_FEATURE_LAZY_PAGES = $(shell if criu check --feature uffd-noncoop > /dev/null; then echo 1; else echo 0; fi) -CRIU_FEATURE_PIDFD_STORE = $(shell if criu check --feature pidfd_store > /dev/null; then echo 1; else echo 0; fi) - -export CRIU_FEATURE_MEM_TRACK CRIU_FEATURE_LAZY_PAGES CRIU_FEATURE_PIDFD_STORE - -all: build test phaul-test - -lint: - golangci-lint run ./... - -build: - $(GO) build -v ./... - -TEST_PAYLOAD := test/piggie/piggie -TEST_BINARIES := test/test $(TEST_PAYLOAD) test/phaul/phaul -COVERAGE_BINARIES := test/test.coverage test/phaul/phaul.coverage -test-bin: $(TEST_BINARIES) - -test/piggie/piggie: test/piggie/piggie.c - $(CC) $^ -o $@ - -test/test: test/main.go - $(GO) build -v -o $@ $^ - -test: $(TEST_BINARIES) - mkdir -p image - PID=$$(test/piggie/piggie) && { \ - test/test dump $$PID image && \ - test/test restore image; \ - pkill -9 piggie; \ - } - rm -rf image - -test/phaul/phaul: test/phaul/main.go - $(GO) build -v -o $@ $^ - -phaul-test: $(TEST_BINARIES) - rm -rf image - PID=$$(test/piggie/piggie) && { \ - test/phaul/phaul $$PID; \ - pkill -9 piggie; \ - } - -test/test.coverage: test/*.go - $(GO) test \ - -covermode=count \ - -coverpkg=./... \ - -mod=vendor \ - -tags coverage \ - -buildmode=pie -c -o $@ $^ - -test/phaul/phaul.coverage: test/phaul/*.go - $(GO) test \ - -covermode=count \ - -coverpkg=./... \ - -mod=vendor \ - -tags coverage \ - -buildmode=pie -c -o $@ $^ - -coverage: $(COVERAGE_BINARIES) $(TEST_PAYLOAD) - mkdir -p $(COVERAGE_PATH) - mkdir -p image - PID=$$(test/piggie/piggie) && { \ - test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE dump $$PID image && \ - test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE restore image; \ - pkill -9 piggie; \ - } - rm -rf image - PID=$$(test/piggie/piggie) && { \ - test/phaul/phaul.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE $$PID; \ - pkill -9 piggie; \ - } - echo "mode: set" > .coverage/coverage.out && cat .coverage/coverprofile* | \ - grep -v mode: | sort -r | awk '{if($$1 != last) {print $$0;last=$$1}}' >> .coverage/coverage.out - -clean: - @rm -f $(TEST_BINARIES) $(COVERAGE_BINARIES) codecov - @rm -rf image $(COVERAGE_PATH) - -rpc/rpc.proto: - curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@ - -stats/stats.proto: - curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@ - -rpc/rpc.pb.go: rpc/rpc.proto - protoc --go_out=. --go_opt=M$^=rpc/ $^ - -stats/stats.pb.go: stats/stats.proto - protoc --go_out=. --go_opt=M$^=stats/ $^ - -vendor: - GO111MODULE=on $(GO) mod tidy - GO111MODULE=on $(GO) mod vendor - GO111MODULE=on $(GO) mod verify - -codecov: - curl -Os https://uploader.codecov.io/latest/linux/codecov - chmod +x codecov - ./codecov -f '.coverage/coverage.out' - -.PHONY: build test phaul-test test-bin clean lint vendor coverage codecov diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go deleted file mode 100644 index 9044ad97..00000000 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go +++ /dev/null @@ -1,6 +0,0 @@ -package stats - -const ( - StatsDump = "stats-dump" - StatsRestore = "stats-restore" -) diff --git a/vendor/github.com/checkpoint-restore/go-criu/v6/.gitignore b/vendor/github.com/checkpoint-restore/go-criu/v6/.gitignore new file mode 100644 index 00000000..55180601 --- /dev/null +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/.gitignore @@ -0,0 +1,13 @@ +test/test +test/test.coverage +test/piggie/piggie +test/phaul/phaul +test/phaul/phaul.coverage +test/loop/loop +test/crit/crit-test +test/crit/test-imgs +image +scripts/*.h +scripts/expected.go +scripts/output.go +crit/bin diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/.golangci.yml b/vendor/github.com/checkpoint-restore/go-criu/v6/.golangci.yml similarity index 51% rename from vendor/github.com/checkpoint-restore/go-criu/v5/.golangci.yml rename to vendor/github.com/checkpoint-restore/go-criu/v6/.golangci.yml index fbbac4b4..c4515109 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/.golangci.yml +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/.golangci.yml @@ -1,12 +1,10 @@ -run: - skip_dirs: - - rpc - - stats - linters: - disable-all: false presets: - bugs - performance - unused - format + +linters-settings: + exhaustive: + default-signifies-exhaustive: true diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/LICENSE b/vendor/github.com/checkpoint-restore/go-criu/v6/LICENSE similarity index 100% rename from vendor/github.com/checkpoint-restore/go-criu/v5/LICENSE rename to vendor/github.com/checkpoint-restore/go-criu/v6/LICENSE diff --git a/vendor/github.com/checkpoint-restore/go-criu/v6/Makefile b/vendor/github.com/checkpoint-restore/go-criu/v6/Makefile new file mode 100644 index 00000000..0c291600 --- /dev/null +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/Makefile @@ -0,0 +1,41 @@ +SHELL = /bin/bash +GO ?= go +CC ?= gcc + +all: build + +lint: + golangci-lint run ./... + +build: rpc/rpc.pb.go stats/stats.pb.go + $(GO) build -v ./... + # Build crit binary + $(MAKE) -C crit bin/crit + +test: build + $(MAKE) -C test + +coverage: + $(MAKE) -C test coverage + +codecov: + $(MAKE) -C test codecov + +rpc/rpc.proto: + curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@ + +rpc/rpc.pb.go: rpc/rpc.proto + protoc --go_out=. --go_opt=M$^=rpc/ $^ + +stats/stats.proto: + curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@ + +stats/stats.pb.go: stats/stats.proto + protoc --go_out=. --go_opt=M$^=stats/ $^ + +vendor: + GO111MODULE=on $(GO) mod tidy + GO111MODULE=on $(GO) mod vendor + GO111MODULE=on $(GO) mod verify + +.PHONY: build test lint vendor coverage codecov diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/README.md b/vendor/github.com/checkpoint-restore/go-criu/v6/README.md similarity index 82% rename from vendor/github.com/checkpoint-restore/go-criu/v5/README.md rename to vendor/github.com/checkpoint-restore/go-criu/v6/README.md index a7483321..d186cb89 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/README.md +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/README.md @@ -4,19 +4,20 @@ ## go-criu -- Go bindings for CRIU -This repository provides Go bindings for [CRIU](https://criu.org/). The code is based on the Go-based PHaul -implementation from the CRIU repository. For easier inclusion into other Go projects the -CRIU Go bindings have been moved to this repository. +This repository provides Go bindings for [CRIU](https://criu.org/). +The code is based on the Go-based PHaul implementation from the CRIU repository. +For easier inclusion into other Go projects, the CRIU Go bindings have been moved to this repository. -The Go bindings provide an easy way to use the CRIU RPC calls from Go without the need -to set up all the infrastructure to make the actual RPC connection to CRIU. +### CRIU +The Go bindings provide an easy way to use the CRIU RPC calls from Go without +the need to set up all the infrastructure to make the actual RPC connection to CRIU. The following example would print the version of CRIU: ```go import ( "log" - "github.com/checkpoint-restore/go-criu/v5" + "github.com/checkpoint-restore/go-criu/v6" ) func main() { @@ -36,6 +37,13 @@ or to just check if at least a certain CRIU version is installed: result, err := c.IsCriuAtLeast(31100) ``` +### CRIT + +The `crit` package provides bindings to decode, encode, and manipulate +CRIU image files natively within Go. It also provides a CLI tool similar +to the original CRIT Python tool. To get started with this, see the docs +at https://criu.org/CRIT_(Go_library). + ## Releases The first go-criu release was 3.11 based on CRIU 3.11. The initial plan @@ -50,7 +58,8 @@ The following table shows the relation between go-criu and criu versions: | Major version | Latest release | CRIU version | | -------------- | -------------- | ------------ | -| v5             | 5.2.0         | 3.16         | +| v6             | 6.2.0         | 3.17         | +| v5             | 5.3.0         | 3.16         | | v5             | 5.0.0         | 3.15         | | v4             | 4.1.0         | 3.14         | @@ -86,7 +95,7 @@ by adding a "Signed-off-by" line containing the contributor's name and e-mail to every commit message. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. -### License and copyright +## License and copyright Unless mentioned otherwise in a specific file's header, all code in this project is released under the Apache 2.0 license. diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/features.go b/vendor/github.com/checkpoint-restore/go-criu/v6/features.go similarity index 96% rename from vendor/github.com/checkpoint-restore/go-criu/v5/features.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/features.go index c7127f95..4e779d95 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/features.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/features.go @@ -3,7 +3,7 @@ package criu import ( "fmt" - "github.com/checkpoint-restore/go-criu/v5/rpc" + "github.com/checkpoint-restore/go-criu/v6/rpc" ) // Feature checking in go-criu is based on the libcriu feature checking function. diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/main.go b/vendor/github.com/checkpoint-restore/go-criu/v6/main.go similarity index 99% rename from vendor/github.com/checkpoint-restore/go-criu/v5/main.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/main.go index 88b1b245..2e099c85 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/main.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/main.go @@ -8,7 +8,7 @@ import ( "strconv" "syscall" - "github.com/checkpoint-restore/go-criu/v5/rpc" + "github.com/checkpoint-restore/go-criu/v6/rpc" "google.golang.org/protobuf/proto" ) diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/notify.go b/vendor/github.com/checkpoint-restore/go-criu/v6/notify.go similarity index 100% rename from vendor/github.com/checkpoint-restore/go-criu/v5/notify.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/notify.go diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.pb.go similarity index 72% rename from vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.pb.go index 15e33fea..67bd8593 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.pb.go @@ -2,8 +2,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.12.4 +// protoc-gen-go v1.28.1 +// protoc v3.19.4 // source: rpc/rpc.proto package rpc @@ -93,6 +93,62 @@ func (CriuCgMode) EnumDescriptor() ([]byte, []int) { return file_rpc_rpc_proto_rawDescGZIP(), []int{0} } +type CriuNetworkLockMethod int32 + +const ( + CriuNetworkLockMethod_IPTABLES CriuNetworkLockMethod = 1 + CriuNetworkLockMethod_NFTABLES CriuNetworkLockMethod = 2 +) + +// Enum value maps for CriuNetworkLockMethod. +var ( + CriuNetworkLockMethod_name = map[int32]string{ + 1: "IPTABLES", + 2: "NFTABLES", + } + CriuNetworkLockMethod_value = map[string]int32{ + "IPTABLES": 1, + "NFTABLES": 2, + } +) + +func (x CriuNetworkLockMethod) Enum() *CriuNetworkLockMethod { + p := new(CriuNetworkLockMethod) + *p = x + return p +} + +func (x CriuNetworkLockMethod) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CriuNetworkLockMethod) Descriptor() protoreflect.EnumDescriptor { + return file_rpc_rpc_proto_enumTypes[1].Descriptor() +} + +func (CriuNetworkLockMethod) Type() protoreflect.EnumType { + return &file_rpc_rpc_proto_enumTypes[1] +} + +func (x CriuNetworkLockMethod) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *CriuNetworkLockMethod) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) + if err != nil { + return err + } + *x = CriuNetworkLockMethod(num) + return nil +} + +// Deprecated: Use CriuNetworkLockMethod.Descriptor instead. +func (CriuNetworkLockMethod) EnumDescriptor() ([]byte, []int) { + return file_rpc_rpc_proto_rawDescGZIP(), []int{1} +} + type CriuPreDumpMode int32 const ( @@ -123,11 +179,11 @@ func (x CriuPreDumpMode) String() string { } func (CriuPreDumpMode) Descriptor() protoreflect.EnumDescriptor { - return file_rpc_rpc_proto_enumTypes[1].Descriptor() + return file_rpc_rpc_proto_enumTypes[2].Descriptor() } func (CriuPreDumpMode) Type() protoreflect.EnumType { - return &file_rpc_rpc_proto_enumTypes[1] + return &file_rpc_rpc_proto_enumTypes[2] } func (x CriuPreDumpMode) Number() protoreflect.EnumNumber { @@ -146,7 +202,7 @@ func (x *CriuPreDumpMode) UnmarshalJSON(b []byte) error { // Deprecated: Use CriuPreDumpMode.Descriptor instead. func (CriuPreDumpMode) EnumDescriptor() ([]byte, []int) { - return file_rpc_rpc_proto_rawDescGZIP(), []int{1} + return file_rpc_rpc_proto_rawDescGZIP(), []int{2} } type CriuReqType int32 @@ -165,6 +221,7 @@ const ( CriuReqType_VERSION CriuReqType = 10 CriuReqType_WAIT_PID CriuReqType = 11 CriuReqType_PAGE_SERVER_CHLD CriuReqType = 12 + CriuReqType_SINGLE_PRE_DUMP CriuReqType = 13 ) // Enum value maps for CriuReqType. @@ -183,6 +240,7 @@ var ( 10: "VERSION", 11: "WAIT_PID", 12: "PAGE_SERVER_CHLD", + 13: "SINGLE_PRE_DUMP", } CriuReqType_value = map[string]int32{ "EMPTY": 0, @@ -198,6 +256,7 @@ var ( "VERSION": 10, "WAIT_PID": 11, "PAGE_SERVER_CHLD": 12, + "SINGLE_PRE_DUMP": 13, } ) @@ -212,11 +271,11 @@ func (x CriuReqType) String() string { } func (CriuReqType) Descriptor() protoreflect.EnumDescriptor { - return file_rpc_rpc_proto_enumTypes[2].Descriptor() + return file_rpc_rpc_proto_enumTypes[3].Descriptor() } func (CriuReqType) Type() protoreflect.EnumType { - return &file_rpc_rpc_proto_enumTypes[2] + return &file_rpc_rpc_proto_enumTypes[3] } func (x CriuReqType) Number() protoreflect.EnumNumber { @@ -235,7 +294,7 @@ func (x *CriuReqType) UnmarshalJSON(b []byte) error { // Deprecated: Use CriuReqType.Descriptor instead. func (CriuReqType) EnumDescriptor() ([]byte, []int) { - return file_rpc_rpc_proto_rawDescGZIP(), []int{2} + return file_rpc_rpc_proto_rawDescGZIP(), []int{3} } type CriuPageServerInfo struct { @@ -644,68 +703,70 @@ type CriuOpts struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd,json=imagesDirFd" json:"images_dir_fd,omitempty"` - Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` // if not set on dump, will dump requesting process - LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running,json=leaveRunning" json:"leave_running,omitempty"` - ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk,json=extUnixSk" json:"ext_unix_sk,omitempty"` - TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established,json=tcpEstablished" json:"tcp_established,omitempty"` - EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices,json=evasiveDevices" json:"evasive_devices,omitempty"` - ShellJob *bool `protobuf:"varint,7,opt,name=shell_job,json=shellJob" json:"shell_job,omitempty"` - FileLocks *bool `protobuf:"varint,8,opt,name=file_locks,json=fileLocks" json:"file_locks,omitempty"` - LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,json=logLevel,def=2" json:"log_level,omitempty"` - LogFile *string `protobuf:"bytes,10,opt,name=log_file,json=logFile" json:"log_file,omitempty"` // No subdirs are allowed. Consider using work-dir - Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"` - NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts,json=notifyScripts" json:"notify_scripts,omitempty"` - Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"` - ParentImg *string `protobuf:"bytes,14,opt,name=parent_img,json=parentImg" json:"parent_img,omitempty"` - TrackMem *bool `protobuf:"varint,15,opt,name=track_mem,json=trackMem" json:"track_mem,omitempty"` - AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup,json=autoDedup" json:"auto_dedup,omitempty"` - WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd,json=workDirFd" json:"work_dir_fd,omitempty"` - LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap,json=linkRemap" json:"link_remap,omitempty"` - Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"` // DEPRECATED, use external instead - CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,json=cpuCap,def=4294967295" json:"cpu_cap,omitempty"` - ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap,json=forceIrmap" json:"force_irmap,omitempty"` - ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd,json=execCmd" json:"exec_cmd,omitempty"` - ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt,json=extMnt" json:"ext_mnt,omitempty"` // DEPRECATED, use external instead - ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups,json=manageCgroups" json:"manage_cgroups,omitempty"` // backward compatibility - CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root,json=cgRoot" json:"cg_root,omitempty"` - RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling,json=rstSibling" json:"rst_sibling,omitempty"` // swrk only - InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd,json=inheritFd" json:"inherit_fd,omitempty"` // swrk only - AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt,json=autoExtMnt" json:"auto_ext_mnt,omitempty"` - ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing,json=extSharing" json:"ext_sharing,omitempty"` - ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters,json=extMasters" json:"ext_masters,omitempty"` - SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt,json=skipMnt" json:"skip_mnt,omitempty"` - EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs,json=enableFs" json:"enable_fs,omitempty"` - UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino,json=unixSkIno" json:"unix_sk_ino,omitempty"` // DEPRECATED, use external instead - ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,json=manageCgroupsMode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"` - GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,json=ghostLimit,def=1048576" json:"ghost_limit,omitempty"` - IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths,json=irmapScanPaths" json:"irmap_scan_paths,omitempty"` - External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"` - EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns,json=emptyNs" json:"empty_ns,omitempty"` - JoinNs []*JoinNamespace `protobuf:"bytes,39,rep,name=join_ns,json=joinNs" json:"join_ns,omitempty"` - CgroupProps *string `protobuf:"bytes,41,opt,name=cgroup_props,json=cgroupProps" json:"cgroup_props,omitempty"` - CgroupPropsFile *string `protobuf:"bytes,42,opt,name=cgroup_props_file,json=cgroupPropsFile" json:"cgroup_props_file,omitempty"` - CgroupDumpController []string `protobuf:"bytes,43,rep,name=cgroup_dump_controller,json=cgroupDumpController" json:"cgroup_dump_controller,omitempty"` - FreezeCgroup *string `protobuf:"bytes,44,opt,name=freeze_cgroup,json=freezeCgroup" json:"freeze_cgroup,omitempty"` - Timeout *uint32 `protobuf:"varint,45,opt,name=timeout" json:"timeout,omitempty"` - TcpSkipInFlight *bool `protobuf:"varint,46,opt,name=tcp_skip_in_flight,json=tcpSkipInFlight" json:"tcp_skip_in_flight,omitempty"` - WeakSysctls *bool `protobuf:"varint,47,opt,name=weak_sysctls,json=weakSysctls" json:"weak_sysctls,omitempty"` - LazyPages *bool `protobuf:"varint,48,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"` - StatusFd *int32 `protobuf:"varint,49,opt,name=status_fd,json=statusFd" json:"status_fd,omitempty"` - OrphanPtsMaster *bool `protobuf:"varint,50,opt,name=orphan_pts_master,json=orphanPtsMaster" json:"orphan_pts_master,omitempty"` - ConfigFile *string `protobuf:"bytes,51,opt,name=config_file,json=configFile" json:"config_file,omitempty"` - TcpClose *bool `protobuf:"varint,52,opt,name=tcp_close,json=tcpClose" json:"tcp_close,omitempty"` - LsmProfile *string `protobuf:"bytes,53,opt,name=lsm_profile,json=lsmProfile" json:"lsm_profile,omitempty"` - TlsCacert *string `protobuf:"bytes,54,opt,name=tls_cacert,json=tlsCacert" json:"tls_cacert,omitempty"` - TlsCacrl *string `protobuf:"bytes,55,opt,name=tls_cacrl,json=tlsCacrl" json:"tls_cacrl,omitempty"` - TlsCert *string `protobuf:"bytes,56,opt,name=tls_cert,json=tlsCert" json:"tls_cert,omitempty"` - TlsKey *string `protobuf:"bytes,57,opt,name=tls_key,json=tlsKey" json:"tls_key,omitempty"` - Tls *bool `protobuf:"varint,58,opt,name=tls" json:"tls,omitempty"` - TlsNoCnVerify *bool `protobuf:"varint,59,opt,name=tls_no_cn_verify,json=tlsNoCnVerify" json:"tls_no_cn_verify,omitempty"` - CgroupYard *string `protobuf:"bytes,60,opt,name=cgroup_yard,json=cgroupYard" json:"cgroup_yard,omitempty"` - PreDumpMode *CriuPreDumpMode `protobuf:"varint,61,opt,name=pre_dump_mode,json=preDumpMode,enum=CriuPreDumpMode,def=1" json:"pre_dump_mode,omitempty"` - PidfdStoreSk *int32 `protobuf:"varint,62,opt,name=pidfd_store_sk,json=pidfdStoreSk" json:"pidfd_store_sk,omitempty"` - LsmMountContext *string `protobuf:"bytes,63,opt,name=lsm_mount_context,json=lsmMountContext" json:"lsm_mount_context,omitempty"` // optional bool check_mounts = 128; + ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd,json=imagesDirFd" json:"images_dir_fd,omitempty"` + Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` // if not set on dump, will dump requesting process + LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running,json=leaveRunning" json:"leave_running,omitempty"` + ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk,json=extUnixSk" json:"ext_unix_sk,omitempty"` + TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established,json=tcpEstablished" json:"tcp_established,omitempty"` + EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices,json=evasiveDevices" json:"evasive_devices,omitempty"` + ShellJob *bool `protobuf:"varint,7,opt,name=shell_job,json=shellJob" json:"shell_job,omitempty"` + FileLocks *bool `protobuf:"varint,8,opt,name=file_locks,json=fileLocks" json:"file_locks,omitempty"` + LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,json=logLevel,def=2" json:"log_level,omitempty"` + LogFile *string `protobuf:"bytes,10,opt,name=log_file,json=logFile" json:"log_file,omitempty"` // No subdirs are allowed. Consider using work-dir + Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"` + NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts,json=notifyScripts" json:"notify_scripts,omitempty"` + Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"` + ParentImg *string `protobuf:"bytes,14,opt,name=parent_img,json=parentImg" json:"parent_img,omitempty"` + TrackMem *bool `protobuf:"varint,15,opt,name=track_mem,json=trackMem" json:"track_mem,omitempty"` + AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup,json=autoDedup" json:"auto_dedup,omitempty"` + WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd,json=workDirFd" json:"work_dir_fd,omitempty"` + LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap,json=linkRemap" json:"link_remap,omitempty"` + Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"` // DEPRECATED, use external instead + CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,json=cpuCap,def=4294967295" json:"cpu_cap,omitempty"` + ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap,json=forceIrmap" json:"force_irmap,omitempty"` + ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd,json=execCmd" json:"exec_cmd,omitempty"` + ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt,json=extMnt" json:"ext_mnt,omitempty"` // DEPRECATED, use external instead + ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups,json=manageCgroups" json:"manage_cgroups,omitempty"` // backward compatibility + CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root,json=cgRoot" json:"cg_root,omitempty"` + RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling,json=rstSibling" json:"rst_sibling,omitempty"` // swrk only + InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd,json=inheritFd" json:"inherit_fd,omitempty"` // swrk only + AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt,json=autoExtMnt" json:"auto_ext_mnt,omitempty"` + ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing,json=extSharing" json:"ext_sharing,omitempty"` + ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters,json=extMasters" json:"ext_masters,omitempty"` + SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt,json=skipMnt" json:"skip_mnt,omitempty"` + EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs,json=enableFs" json:"enable_fs,omitempty"` + UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino,json=unixSkIno" json:"unix_sk_ino,omitempty"` // DEPRECATED, use external instead + ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,json=manageCgroupsMode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"` + GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,json=ghostLimit,def=1048576" json:"ghost_limit,omitempty"` + IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths,json=irmapScanPaths" json:"irmap_scan_paths,omitempty"` + External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"` + EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns,json=emptyNs" json:"empty_ns,omitempty"` + JoinNs []*JoinNamespace `protobuf:"bytes,39,rep,name=join_ns,json=joinNs" json:"join_ns,omitempty"` + CgroupProps *string `protobuf:"bytes,41,opt,name=cgroup_props,json=cgroupProps" json:"cgroup_props,omitempty"` + CgroupPropsFile *string `protobuf:"bytes,42,opt,name=cgroup_props_file,json=cgroupPropsFile" json:"cgroup_props_file,omitempty"` + CgroupDumpController []string `protobuf:"bytes,43,rep,name=cgroup_dump_controller,json=cgroupDumpController" json:"cgroup_dump_controller,omitempty"` + FreezeCgroup *string `protobuf:"bytes,44,opt,name=freeze_cgroup,json=freezeCgroup" json:"freeze_cgroup,omitempty"` + Timeout *uint32 `protobuf:"varint,45,opt,name=timeout" json:"timeout,omitempty"` + TcpSkipInFlight *bool `protobuf:"varint,46,opt,name=tcp_skip_in_flight,json=tcpSkipInFlight" json:"tcp_skip_in_flight,omitempty"` + WeakSysctls *bool `protobuf:"varint,47,opt,name=weak_sysctls,json=weakSysctls" json:"weak_sysctls,omitempty"` + LazyPages *bool `protobuf:"varint,48,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"` + StatusFd *int32 `protobuf:"varint,49,opt,name=status_fd,json=statusFd" json:"status_fd,omitempty"` + OrphanPtsMaster *bool `protobuf:"varint,50,opt,name=orphan_pts_master,json=orphanPtsMaster" json:"orphan_pts_master,omitempty"` + ConfigFile *string `protobuf:"bytes,51,opt,name=config_file,json=configFile" json:"config_file,omitempty"` + TcpClose *bool `protobuf:"varint,52,opt,name=tcp_close,json=tcpClose" json:"tcp_close,omitempty"` + LsmProfile *string `protobuf:"bytes,53,opt,name=lsm_profile,json=lsmProfile" json:"lsm_profile,omitempty"` + TlsCacert *string `protobuf:"bytes,54,opt,name=tls_cacert,json=tlsCacert" json:"tls_cacert,omitempty"` + TlsCacrl *string `protobuf:"bytes,55,opt,name=tls_cacrl,json=tlsCacrl" json:"tls_cacrl,omitempty"` + TlsCert *string `protobuf:"bytes,56,opt,name=tls_cert,json=tlsCert" json:"tls_cert,omitempty"` + TlsKey *string `protobuf:"bytes,57,opt,name=tls_key,json=tlsKey" json:"tls_key,omitempty"` + Tls *bool `protobuf:"varint,58,opt,name=tls" json:"tls,omitempty"` + TlsNoCnVerify *bool `protobuf:"varint,59,opt,name=tls_no_cn_verify,json=tlsNoCnVerify" json:"tls_no_cn_verify,omitempty"` + CgroupYard *string `protobuf:"bytes,60,opt,name=cgroup_yard,json=cgroupYard" json:"cgroup_yard,omitempty"` + PreDumpMode *CriuPreDumpMode `protobuf:"varint,61,opt,name=pre_dump_mode,json=preDumpMode,enum=CriuPreDumpMode,def=1" json:"pre_dump_mode,omitempty"` + PidfdStoreSk *int32 `protobuf:"varint,62,opt,name=pidfd_store_sk,json=pidfdStoreSk" json:"pidfd_store_sk,omitempty"` + LsmMountContext *string `protobuf:"bytes,63,opt,name=lsm_mount_context,json=lsmMountContext" json:"lsm_mount_context,omitempty"` + NetworkLock *CriuNetworkLockMethod `protobuf:"varint,64,opt,name=network_lock,json=networkLock,enum=CriuNetworkLockMethod,def=1" json:"network_lock,omitempty"` + MntnsCompatMode *bool `protobuf:"varint,65,opt,name=mntns_compat_mode,json=mntnsCompatMode" json:"mntns_compat_mode,omitempty"` // optional bool check_mounts = 128; } // Default values for CriuOpts fields. @@ -714,6 +775,7 @@ const ( Default_CriuOpts_CpuCap = uint32(4294967295) Default_CriuOpts_GhostLimit = uint32(1048576) Default_CriuOpts_PreDumpMode = CriuPreDumpMode_SPLICE + Default_CriuOpts_NetworkLock = CriuNetworkLockMethod_IPTABLES ) func (x *CriuOpts) Reset() { @@ -1182,6 +1244,20 @@ func (x *CriuOpts) GetLsmMountContext() string { return "" } +func (x *CriuOpts) GetNetworkLock() CriuNetworkLockMethod { + if x != nil && x.NetworkLock != nil { + return *x.NetworkLock + } + return Default_CriuOpts_NetworkLock +} + +func (x *CriuOpts) GetMntnsCompatMode() bool { + if x != nil && x.MntnsCompatMode != nil { + return *x.MntnsCompatMode + } + return false +} + type CriuDumpResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1739,7 +1815,7 @@ var file_rpc_rpc_proto_rawDesc = []byte{ 0x52, 0x04, 0x63, 0x74, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x07, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, - 0x20, 0x02, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x8c, 0x11, 0x0a, 0x09, + 0x20, 0x02, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x80, 0x12, 0x0a, 0x09, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x66, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x44, 0x69, 0x72, 0x46, 0x64, 0x12, 0x10, 0x0a, @@ -1876,95 +1952,107 @@ var file_rpc_rpc_proto_rawDesc = []byte{ 0x52, 0x0c, 0x70, 0x69, 0x64, 0x66, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x73, 0x6d, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x73, 0x6d, 0x4d, 0x6f, - 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2c, 0x0a, 0x0e, 0x63, 0x72, - 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x63, 0x72, 0x69, 0x75, - 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, - 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, - 0x37, 0x0a, 0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x6c, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75, - 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, - 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x65, - 0x6d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x70, - 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x7a, 0x79, - 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, 0x64, 0x66, 0x64, 0x5f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x69, 0x64, 0x66, - 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x75, 0x5f, - 0x72, 0x65, 0x71, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, - 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, - 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x79, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1b, - 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x8f, 0x03, 0x0a, 0x09, 0x63, 0x72, - 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x46, 0x0a, 0x0c, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x40, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x19, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x08, 0x49, 0x50, 0x54, + 0x41, 0x42, 0x4c, 0x45, 0x53, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x6f, + 0x63, 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6e, 0x74, 0x6e, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x41, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6d, + 0x6e, 0x74, 0x6e, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2c, + 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x25, 0x0a, 0x11, + 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, + 0x70, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x6c, 0x0a, 0x0d, + 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, + 0x7a, 0x79, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x6c, 0x61, 0x7a, 0x79, 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, 0x64, + 0x66, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x70, 0x69, 0x64, 0x66, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x63, + 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, - 0x72, 0x65, 0x73, 0x70, 0x52, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x72, - 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52, - 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x26, - 0x0a, 0x02, 0x70, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x72, 0x69, - 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x52, 0x02, 0x70, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, - 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6e, - 0x6f, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x12, 0x27, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x72, - 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x0c, - 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x02, - 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, - 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5f, - 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x63, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a, - 0x0a, 0x06, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x47, - 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x50, 0x53, - 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, - 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, - 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x06, 0x2a, - 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x10, - 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x4d, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xd0, - 0x01, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, - 0x55, 0x4d, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, - 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x12, 0x0c, 0x0a, - 0x08, 0x50, 0x52, 0x45, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x50, - 0x41, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, - 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x50, 0x55, 0x49, - 0x4e, 0x46, 0x4f, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x50, - 0x55, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x11, 0x0a, - 0x0d, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x09, - 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x0c, 0x0a, - 0x08, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x50, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x50, - 0x41, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x48, 0x4c, 0x44, 0x10, - 0x0c, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x6f, + 0x70, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x72, 0x69, 0x75, + 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x12, + 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, + 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x8f, 0x03, + 0x0a, 0x09, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x22, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, + 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, + 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x64, 0x75, 0x6d, + 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, + 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x12, 0x2c, + 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x24, 0x0a, 0x06, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, + 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x12, 0x26, 0x0a, 0x02, 0x70, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x02, 0x70, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, + 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, + 0x45, 0x72, 0x72, 0x6e, 0x6f, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x12, 0x27, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0xb0, 0x01, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x2a, 0x5f, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x63, 0x67, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x00, 0x12, 0x0b, + 0x0a, 0x07, 0x43, 0x47, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, + 0x52, 0x4f, 0x50, 0x53, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x03, + 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, + 0x52, 0x49, 0x43, 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, + 0x54, 0x10, 0x06, 0x2a, 0x36, 0x0a, 0x18, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, + 0x0c, 0x0a, 0x08, 0x49, 0x50, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x12, 0x0c, 0x0a, + 0x08, 0x4e, 0x46, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x02, 0x2a, 0x2d, 0x0a, 0x12, 0x63, + 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, + 0x07, 0x56, 0x4d, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xe5, 0x01, 0x0a, 0x0d, 0x63, + 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, + 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x55, 0x4d, 0x50, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x02, 0x12, 0x09, + 0x0a, 0x05, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, + 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x47, 0x45, 0x5f, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, + 0x46, 0x59, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x46, 0x4f, 0x5f, + 0x44, 0x55, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x46, + 0x4f, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x45, 0x41, + 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, + 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x41, 0x49, + 0x54, 0x5f, 0x50, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x47, 0x45, 0x5f, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x48, 0x4c, 0x44, 0x10, 0x0c, 0x12, 0x13, 0x0a, + 0x0f, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x5f, 0x44, 0x55, 0x4d, 0x50, + 0x10, 0x0d, } var ( @@ -1979,53 +2067,55 @@ func file_rpc_rpc_proto_rawDescGZIP() []byte { return file_rpc_rpc_proto_rawDescData } -var file_rpc_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_rpc_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 4) var file_rpc_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_rpc_rpc_proto_goTypes = []interface{}{ (CriuCgMode)(0), // 0: criu_cg_mode - (CriuPreDumpMode)(0), // 1: criu_pre_dump_mode - (CriuReqType)(0), // 2: criu_req_type - (*CriuPageServerInfo)(nil), // 3: criu_page_server_info - (*CriuVethPair)(nil), // 4: criu_veth_pair - (*ExtMountMap)(nil), // 5: ext_mount_map - (*JoinNamespace)(nil), // 6: join_namespace - (*InheritFd)(nil), // 7: inherit_fd - (*CgroupRoot)(nil), // 8: cgroup_root - (*UnixSk)(nil), // 9: unix_sk - (*CriuOpts)(nil), // 10: criu_opts - (*CriuDumpResp)(nil), // 11: criu_dump_resp - (*CriuRestoreResp)(nil), // 12: criu_restore_resp - (*CriuNotify)(nil), // 13: criu_notify - (*CriuFeatures)(nil), // 14: criu_features - (*CriuReq)(nil), // 15: criu_req - (*CriuResp)(nil), // 16: criu_resp - (*CriuVersion)(nil), // 17: criu_version + (CriuNetworkLockMethod)(0), // 1: criu_network_lock_method + (CriuPreDumpMode)(0), // 2: criu_pre_dump_mode + (CriuReqType)(0), // 3: criu_req_type + (*CriuPageServerInfo)(nil), // 4: criu_page_server_info + (*CriuVethPair)(nil), // 5: criu_veth_pair + (*ExtMountMap)(nil), // 6: ext_mount_map + (*JoinNamespace)(nil), // 7: join_namespace + (*InheritFd)(nil), // 8: inherit_fd + (*CgroupRoot)(nil), // 9: cgroup_root + (*UnixSk)(nil), // 10: unix_sk + (*CriuOpts)(nil), // 11: criu_opts + (*CriuDumpResp)(nil), // 12: criu_dump_resp + (*CriuRestoreResp)(nil), // 13: criu_restore_resp + (*CriuNotify)(nil), // 14: criu_notify + (*CriuFeatures)(nil), // 15: criu_features + (*CriuReq)(nil), // 16: criu_req + (*CriuResp)(nil), // 17: criu_resp + (*CriuVersion)(nil), // 18: criu_version } var file_rpc_rpc_proto_depIdxs = []int32{ - 3, // 0: criu_opts.ps:type_name -> criu_page_server_info - 4, // 1: criu_opts.veths:type_name -> criu_veth_pair - 5, // 2: criu_opts.ext_mnt:type_name -> ext_mount_map - 8, // 3: criu_opts.cg_root:type_name -> cgroup_root - 7, // 4: criu_opts.inherit_fd:type_name -> inherit_fd - 9, // 5: criu_opts.unix_sk_ino:type_name -> unix_sk + 4, // 0: criu_opts.ps:type_name -> criu_page_server_info + 5, // 1: criu_opts.veths:type_name -> criu_veth_pair + 6, // 2: criu_opts.ext_mnt:type_name -> ext_mount_map + 9, // 3: criu_opts.cg_root:type_name -> cgroup_root + 8, // 4: criu_opts.inherit_fd:type_name -> inherit_fd + 10, // 5: criu_opts.unix_sk_ino:type_name -> unix_sk 0, // 6: criu_opts.manage_cgroups_mode:type_name -> criu_cg_mode - 6, // 7: criu_opts.join_ns:type_name -> join_namespace - 1, // 8: criu_opts.pre_dump_mode:type_name -> criu_pre_dump_mode - 2, // 9: criu_req.type:type_name -> criu_req_type - 10, // 10: criu_req.opts:type_name -> criu_opts - 14, // 11: criu_req.features:type_name -> criu_features - 2, // 12: criu_resp.type:type_name -> criu_req_type - 11, // 13: criu_resp.dump:type_name -> criu_dump_resp - 12, // 14: criu_resp.restore:type_name -> criu_restore_resp - 13, // 15: criu_resp.notify:type_name -> criu_notify - 3, // 16: criu_resp.ps:type_name -> criu_page_server_info - 14, // 17: criu_resp.features:type_name -> criu_features - 17, // 18: criu_resp.version:type_name -> criu_version - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 7, // 7: criu_opts.join_ns:type_name -> join_namespace + 2, // 8: criu_opts.pre_dump_mode:type_name -> criu_pre_dump_mode + 1, // 9: criu_opts.network_lock:type_name -> criu_network_lock_method + 3, // 10: criu_req.type:type_name -> criu_req_type + 11, // 11: criu_req.opts:type_name -> criu_opts + 15, // 12: criu_req.features:type_name -> criu_features + 3, // 13: criu_resp.type:type_name -> criu_req_type + 12, // 14: criu_resp.dump:type_name -> criu_dump_resp + 13, // 15: criu_resp.restore:type_name -> criu_restore_resp + 14, // 16: criu_resp.notify:type_name -> criu_notify + 4, // 17: criu_resp.ps:type_name -> criu_page_server_info + 15, // 18: criu_resp.features:type_name -> criu_features + 18, // 19: criu_resp.version:type_name -> criu_version + 20, // [20:20] is the sub-list for method output_type + 20, // [20:20] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name } func init() { file_rpc_rpc_proto_init() } @@ -2220,7 +2310,7 @@ func file_rpc_rpc_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_rpc_proto_rawDesc, - NumEnums: 3, + NumEnums: 4, NumMessages: 15, NumExtensions: 0, NumServices: 0, diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.proto b/vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.proto similarity index 96% rename from vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.proto rename to vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.proto index 61e1b24f..a6cc5da4 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.proto +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/rpc/rpc.proto @@ -49,6 +49,11 @@ enum criu_cg_mode { DEFAULT = 6; }; +enum criu_network_lock_method { + IPTABLES = 1; + NFTABLES = 2; +}; + enum criu_pre_dump_mode { SPLICE = 1; VM_READ = 2; @@ -131,6 +136,8 @@ message criu_opts { optional criu_pre_dump_mode pre_dump_mode = 61 [default = SPLICE]; optional int32 pidfd_store_sk = 62; optional string lsm_mount_context = 63; + optional criu_network_lock_method network_lock = 64 [default = IPTABLES]; + optional bool mntns_compat_mode = 65; /* optional bool check_mounts = 128; */ } @@ -166,6 +173,8 @@ enum criu_req_type { WAIT_PID = 11; PAGE_SERVER_CHLD = 12; + + SINGLE_PRE_DUMP = 13; } /* diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/stats.pb.go similarity index 99% rename from vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/stats/stats.pb.go index ff011fc2..0be0ceda 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/stats.pb.go @@ -2,8 +2,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.12.4 +// protoc-gen-go v1.28.1 +// protoc v3.19.4 // source: stats/stats.proto package stats diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/stats.proto similarity index 100% rename from vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto rename to vendor/github.com/checkpoint-restore/go-criu/v6/stats/stats.proto diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/types.go similarity index 73% rename from vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/stats/types.go index 24cc3989..69d5b0ce 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/types.go @@ -1,7 +1,9 @@ -package magic +package stats const ( - ImgCommonMagic = 0x54564319 /* Sarov (a.k.a. Arzamas-16) */ + StatsDump = "stats-dump" + StatsRestore = "stats-restore" + ImgServiceMagic = 0x55105940 /* Zlatoust */ StatsMagic = 0x57093306 /* Ostashkov */ diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/utils.go similarity index 65% rename from vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go rename to vendor/github.com/checkpoint-restore/go-criu/v6/stats/utils.go index 368b2039..933e887d 100644 --- a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go +++ b/vendor/github.com/checkpoint-restore/go-criu/v6/stats/utils.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" - "github.com/checkpoint-restore/go-criu/v5/magic" "google.golang.org/protobuf/proto" ) @@ -17,18 +16,18 @@ func readStatisticsFile(imgDir *os.File, fileName string) (*StatsEntry, error) { return nil, err } - if binary.LittleEndian.Uint32(buf[magic.PrimaryMagicOffset:magic.SecondaryMagicOffset]) != magic.ImgServiceMagic { + if binary.LittleEndian.Uint32(buf[PrimaryMagicOffset:SecondaryMagicOffset]) != ImgServiceMagic { return nil, errors.New("Primary magic not found") } - if binary.LittleEndian.Uint32(buf[magic.SecondaryMagicOffset:magic.SizeOffset]) != magic.StatsMagic { + if binary.LittleEndian.Uint32(buf[SecondaryMagicOffset:SizeOffset]) != StatsMagic { return nil, errors.New("Secondary magic not found") } - payloadSize := binary.LittleEndian.Uint32(buf[magic.SizeOffset:magic.PayloadOffset]) + payloadSize := binary.LittleEndian.Uint32(buf[SizeOffset:PayloadOffset]) st := &StatsEntry{} - if err := proto.Unmarshal(buf[magic.PayloadOffset:magic.PayloadOffset+payloadSize], st); err != nil { + if err := proto.Unmarshal(buf[PayloadOffset:PayloadOffset+payloadSize], st); err != nil { return nil, err } diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go new file mode 100644 index 00000000..07aca4a1 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go @@ -0,0 +1,82 @@ +/* + Copyright © 2022 The CDI Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package multierror + +import ( + "strings" +) + +// New combines several errors into a single error. Parameters that are nil are +// ignored. If no errors are passed in or all parameters are nil, then the +// result is also nil. +func New(errors ...error) error { + // Filter out nil entries. + numErrors := 0 + for _, err := range errors { + if err != nil { + errors[numErrors] = err + numErrors++ + } + } + if numErrors == 0 { + return nil + } + return multiError(errors[0:numErrors]) +} + +// multiError is the underlying implementation used by New. +// +// Beware that a null multiError is not the same as a nil error. +type multiError []error + +// multiError returns all individual error strings concatenated with "\n" +func (e multiError) Error() string { + var builder strings.Builder + for i, err := range e { + if i > 0 { + _, _ = builder.WriteString("\n") + } + _, _ = builder.WriteString(err.Error()) + } + return builder.String() +} + +// Append returns a new multi error all errors concatenated. Errors that are +// multi errors get flattened, nil is ignored. +func Append(err error, errors ...error) error { + var result multiError + if m, ok := err.(multiError); ok { + result = m + } else if err != nil { + result = append(result, err) + } + + for _, e := range errors { + if e == nil { + continue + } + if m, ok := e.(multiError); ok { + result = append(result, m...) + } else { + result = append(result, e) + } + } + if len(result) == 0 { + return nil + } + return result +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go index d653ac38..dee85486 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go @@ -17,14 +17,18 @@ package cdi import ( + "io/fs" + "os" "path/filepath" "sort" "strings" "sync" + stderr "errors" + + "github.com/container-orchestrated-devices/container-device-interface/internal/multierror" cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" "github.com/fsnotify/fsnotify" - "github.com/hashicorp/go-multierror" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -123,8 +127,8 @@ func (c *Cache) Refresh() error { // collect and return cached errors, much like refresh() does it var result error - for _, err := range c.errors { - result = multierror.Append(result, err...) + for _, errors := range c.errors { + result = multierror.Append(result, errors...) } return result } @@ -194,11 +198,7 @@ func (c *Cache) refresh() error { c.devices = devices c.errors = specErrors - if len(result) > 0 { - return multierror.Append(nil, result...) - } - - return nil + return multierror.New(result...) } // RefreshIfRequired triggers a refresh if necessary. @@ -255,11 +255,22 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e return nil, nil } -// WriteSpec writes a Spec file with the given content. Priority is used -// as an index into the list of Spec directories to pick a directory for -// the file, adjusting for any under- or overflows. If name has a "json" -// or "yaml" extension it choses the encoding. Otherwise JSON encoding -// is used with a "json" extension. +// highestPrioritySpecDir returns the Spec directory with highest priority +// and its priority. +func (c *Cache) highestPrioritySpecDir() (string, int) { + if len(c.specDirs) == 0 { + return "", -1 + } + + prio := len(c.specDirs) - 1 + dir := c.specDirs[prio] + + return dir, prio +} + +// WriteSpec writes a Spec file with the given content into the highest +// priority Spec directory. If name has a "json" or "yaml" extension it +// choses the encoding. Otherwise the default YAML encoding is used. func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { var ( specDir string @@ -269,23 +280,51 @@ func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { err error ) - if len(c.specDirs) == 0 { + specDir, prio = c.highestPrioritySpecDir() + if specDir == "" { return errors.New("no Spec directories to write to") } - prio = len(c.specDirs) - 1 - specDir = c.specDirs[prio] path = filepath.Join(specDir, name) if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { - path += ".json" + path += defaultSpecExt } - spec, err = NewSpec(raw, path, prio) + spec, err = newSpec(raw, path, prio) if err != nil { return err } - return spec.Write(true) + return spec.write(true) +} + +// RemoveSpec removes a Spec with the given name from the highest +// priority Spec directory. This function can be used to remove a +// Spec previously written by WriteSpec(). If the file exists and +// its removal fails RemoveSpec returns an error. +func (c *Cache) RemoveSpec(name string) error { + var ( + specDir string + path string + err error + ) + + specDir, _ = c.highestPrioritySpecDir() + if specDir == "" { + return errors.New("no Spec directories to remove from") + } + + path = filepath.Join(specDir, name) + if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { + path += defaultSpecExt + } + + err = os.Remove(path) + if err != nil && stderr.Is(err, fs.ErrNotExist) { + err = nil + } + + return err } // GetDevice returns the cached device for the given qualified name. diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go index 847e5125..96461229 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go @@ -124,10 +124,15 @@ // // Generated Spec Files, Multiple Directories, Device Precedence // -// There are systems where the set of available or usable CDI devices -// changes dynamically and this needs to be reflected in the CDI Specs. -// This is done by dynamically regenerating CDI Spec files which are -// affected by these changes. +// It is often necessary to generate Spec files dynamically. On some +// systems the available or usable set of CDI devices might change +// dynamically which then needs to be reflected in CDI Specs. For +// some device classes it makes sense to enumerate the available +// devices at every boot and generate Spec file entries for each +// device found. Some CDI devices might need special client- or +// request-specific configuration which can only be fulfilled by +// dynamically generated client-specific entries in transient Spec +// files. // // CDI can collect Spec files from multiple directories. Spec files are // automatically assigned priorities according to which directory they @@ -141,7 +146,111 @@ // separating dynamically generated CDI Spec files from static ones. // The default directories are '/etc/cdi' and '/var/run/cdi'. By putting // dynamically generated Spec files under '/var/run/cdi', those take -// precedence over static ones in '/etc/cdi'. +// precedence over static ones in '/etc/cdi'. With this scheme, static +// Spec files, typically installed by distro-specific packages, go into +// '/etc/cdi' while all the dynamically generated Spec files, transient +// or other, go into '/var/run/cdi'. +// +// Spec File Generation +// +// CDI offers two functions for writing and removing dynamically generated +// Specs from CDI Spec directories. These functions, WriteSpec() and +// RemoveSpec() implicitly follow the principle of separating dynamic Specs +// from the rest and therefore always write to and remove Specs from the +// last configured directory. +// +// Corresponding functions are also provided for generating names for Spec +// files. These functions follow a simple naming convention to ensure that +// multiple entities generating Spec files simultaneously on the same host +// do not end up using conflicting Spec file names. GenerateSpecName(), +// GenerateNameForSpec(), GenerateTransientSpecName(), and +// GenerateTransientNameForSpec() all generate names which can be passed +// as such to WriteSpec() and subsequently to RemoveSpec(). +// +// Generating a Spec file for a vendor/device class can be done with a +// code snippet similar to the following: +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateDeviceSpecs() error { +// registry := cdi.GetRegistry() +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range enumerateDevices() { +// spec.Devices = append(spec.Devices, specs.Device{ +// Name: dev.Name, +// ContainerEdits: getContainerEditsForDevice(dev), +// }) +// } +// +// specName, err := cdi.GenerateNameForSpec(spec) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.WriteSpec(spec, specName) +// } +// +// Similary, generating and later cleaning up transient Spec files can be +// done with code fragments similar to the following. These transient Spec +// files are temporary Spec files with container-specific parametrization. +// They are typically created before the associated container is created +// and removed once that container is removed. +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// devices := getContainerDevs(ctr, vendor, class) +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range devices { +// spec.Devices = append(spec.Devices, specs.Device{ +// // the generated name needs to be unique within the +// // vendor/class domain on the host/node. +// Name: generateUniqueDevName(dev, ctr), +// ContainerEdits: getEditsForContainer(dev), +// }) +// } +// +// // transientID is expected to guarantee that the Spec file name +// // generated using is unique within +// // the host/node. If more than one device is allocated with the +// // same vendor/class domain, either all generated Spec entries +// // should go to a single Spec file (like in this sample snippet), +// // or transientID should be unique for each generated Spec file. +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.WriteSpec(spec, specName) +// } +// +// func removeTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// +// return registry.RemoveSpec(specName) +// } // // CDI Spec Validation // diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go index d5bd54b0..10fab899 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go @@ -107,6 +107,7 @@ type RegistrySpecDB interface { GetVendorSpecs(vendor string) []*Spec GetSpecErrors(*Spec) []error WriteSpec(raw *cdi.Spec, name string) error + RemoveSpec(name string) error } type registry struct { diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go index 3dfbab2f..c558b8ef 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "sync" oci "github.com/opencontainers/runtime-spec/specs-go" @@ -30,6 +31,14 @@ import ( cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" ) +const ( + // CurrentVersion is the current vesion of the CDI Spec. + CurrentVersion = cdi.CurrentVersion + + // defaultSpecExt is the file extension for the default encoding. + defaultSpecExt = ".yaml" +) + var ( // Valid CDI Spec versions. validSpecVersions = map[string]struct{}{ @@ -80,7 +89,7 @@ func ReadSpec(path string, priority int) (*Spec, error) { return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path) } - spec, err := NewSpec(raw, path, priority) + spec, err := newSpec(raw, path, priority) if err != nil { return nil, err } @@ -88,11 +97,11 @@ func ReadSpec(path string, priority int) (*Spec, error) { return spec, nil } -// NewSpec creates a new Spec from the given CDI Spec data. The +// newSpec creates a new Spec from the given CDI Spec data. The // Spec is marked as loaded from the given path with the given -// priority. If Spec data validation fails NewSpec returns a nil +// priority. If Spec data validation fails newSpec returns a nil // Spec and an error. -func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { +func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { err := validateSpec(raw) if err != nil { return nil, err @@ -104,6 +113,10 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { priority: priority, } + if ext := filepath.Ext(spec.path); ext != ".yaml" && ext != ".json" { + spec.path += defaultSpecExt + } + spec.vendor, spec.class = ParseQualifier(spec.Kind) if spec.devices, err = spec.validate(); err != nil { @@ -114,8 +127,8 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { } // Write the CDI Spec to the file associated with it during instantiation -// by NewSpec() or ReadSpec(). -func (s *Spec) Write(overwrite bool) error { +// by newSpec() or ReadSpec(). +func (s *Spec) write(overwrite bool) error { var ( data []byte dir string @@ -249,7 +262,7 @@ func ParseSpec(data []byte) (*cdi.Spec, error) { // SetSpecValidator sets a CDI Spec validator function. This function // is used for extra CDI Spec content validation whenever a Spec file -// loaded (using ReadSpec() or NewSpec()) or written (Spec.Write()). +// loaded (using ReadSpec() or written (using WriteSpec()). func SetSpecValidator(fn func(*cdi.Spec) error) { validatorLock.Lock() defer validatorLock.Unlock() @@ -270,3 +283,68 @@ func validateSpec(raw *cdi.Spec) error { } return nil } + +// GenerateSpecName generates a vendor+class scoped Spec file name. The +// name can be passed to WriteSpec() to write a Spec file to the file +// system. +// +// vendor and class should match the vendor and class of the CDI Spec. +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +// +// This function always returns the same name for the same vendor/class +// combination. Therefore it cannot be used as such to generate multiple +// Spec file names for a single vendor and class. +func GenerateSpecName(vendor, class string) string { + return vendor + "-" + class +} + +// GenerateTransientSpecName generates a vendor+class scoped transient +// Spec file name. The name can be passed to WriteSpec() to write a Spec +// file to the file system. +// +// Transient Specs are those whose lifecycle is tied to that of some +// external entity, for instance a container. vendor and class should +// match the vendor and class of the CDI Spec. transientID should be +// unique among all CDI users on the same host that might generate +// transient Spec files using the same vendor/class combination. If +// the external entity to which the lifecycle of the tranient Spec +// is tied to has a unique ID of its own, then this is usually a +// good choice for transientID. +// +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +func GenerateTransientSpecName(vendor, class, transientID string) string { + transientID = strings.ReplaceAll(transientID, "/", "_") + return GenerateSpecName(vendor, class) + "_" + transientID +} + +// GenerateNameForSpec generates a name for the given Spec using +// GenerateSpecName with the vendor and class taken from the Spec. +// On success it returns the generated name and a nil error. If +// the Spec does not contain a valid vendor or class, it returns +// an empty name and a non-nil error. +func GenerateNameForSpec(raw *cdi.Spec) (string, error) { + vendor, class := ParseQualifier(raw.Kind) + if vendor == "" { + return "", errors.Errorf("invalid vendor/class %q in Spec", raw.Kind) + } + + return GenerateSpecName(vendor, class), nil +} + +// GenerateNameForTransientSpec generates a name for the given transient +// Spec using GenerateTransientSpecName with the vendor and class taken +// from the Spec. On success it returns the generated name and a nil error. +// If the Spec does not contain a valid vendor or class, it returns an +// an empty name and a non-nil error. +func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) { + vendor, class := ParseQualifier(raw.Kind) + if vendor == "" { + return "", errors.Errorf("invalid vendor/class %q in Spec", raw.Kind) + } + + return GenerateTransientSpecName(vendor, class, transientID), nil +} diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go index 8f955d03..23430994 100644 --- a/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -27,40 +27,40 @@ // The vast majority of use cases should simply use the match function with // user input. The first step is to parse a specifier into a matcher: // -// m, err := Parse("linux") -// if err != nil { ... } +// m, err := Parse("linux") +// if err != nil { ... } // // Once you have a matcher, use it to match against the platform declared by a // component, typically from an image or runtime. Since extracting an images // platform is a little more involved, we'll use an example against the // platform default: // -// if ok := m.Match(Default()); !ok { /* doesn't match */ } +// if ok := m.Match(Default()); !ok { /* doesn't match */ } // // This can be composed in loops for resolving runtimes or used as a filter for // fetch and select images. // // More details of the specifier syntax and platform spec follow. // -// Declaring Platform Support +// # Declaring Platform Support // // Components that have strict platform requirements should use the OCI // platform specification to declare their support. Typically, this will be // images and runtimes that should make these declaring which platform they // support specifically. This looks roughly as follows: // -// type Platform struct { -// Architecture string -// OS string -// Variant string -// } +// type Platform struct { +// Architecture string +// OS string +// Variant string +// } // // Most images and runtimes should at least set Architecture and OS, according // to their GOARCH and GOOS values, respectively (follow the OCI image // specification when in doubt). ARM should set variant under certain // discussions, which are outlined below. // -// Platform Specifiers +// # Platform Specifiers // // While the OCI platform specifications provide a tool for components to // specify structured information, user input typically doesn't need the full @@ -77,7 +77,7 @@ // where the architecture may be known but a runtime may support images from // different operating systems. // -// Normalization +// # Normalization // // Because not all users are familiar with the way the Go runtime represents // platforms, several normalizations have been provided to make this package @@ -85,17 +85,17 @@ // // The following are performed for architectures: // -// Value Normalized -// aarch64 arm64 -// armhf arm -// armel arm/v6 -// i386 386 -// x86_64 amd64 -// x86-64 amd64 +// Value Normalized +// aarch64 arm64 +// armhf arm +// armel arm/v6 +// i386 386 +// x86_64 amd64 +// x86-64 amd64 // // We also normalize the operating system `macos` to `darwin`. // -// ARM Support +// # ARM Support // // To qualify ARM architecture, the Variant field is used to qualify the arm // version. The most common arm version, v7, is represented without the variant diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go index 0da3efe4..b071cea5 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go @@ -49,6 +49,7 @@ type options struct { missedPrioritizedFiles *[]string compression Compression ctx context.Context + minChunkSize int } type Option func(o *options) error @@ -63,6 +64,7 @@ func WithChunkSize(chunkSize int) Option { // WithCompressionLevel option specifies the gzip compression level. // The default is gzip.BestCompression. +// This option will be ignored if WithCompression option is used. // See also: https://godoc.org/compress/gzip#pkg-constants func WithCompressionLevel(level int) Option { return func(o *options) error { @@ -113,6 +115,18 @@ func WithContext(ctx context.Context) Option { } } +// WithMinChunkSize option specifies the minimal number of bytes of data +// must be written in one gzip stream. +// By increasing this number, one gzip stream can contain multiple files +// and it hopefully leads to smaller result blob. +// NOTE: This adds a TOC property that old reader doesn't understand. +func WithMinChunkSize(minChunkSize int) Option { + return func(o *options) error { + o.minChunkSize = minChunkSize + return nil + } +} + // Blob is an eStargz blob. type Blob struct { io.ReadCloser @@ -180,7 +194,14 @@ func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { if err != nil { return nil, err } - tarParts := divideEntries(entries, runtime.GOMAXPROCS(0)) + var tarParts [][]*entry + if opts.minChunkSize > 0 { + // Each entry needs to know the size of the current gzip stream so they + // cannot be processed in parallel. + tarParts = [][]*entry{entries} + } else { + tarParts = divideEntries(entries, runtime.GOMAXPROCS(0)) + } writers := make([]*Writer, len(tarParts)) payloads := make([]*os.File, len(tarParts)) var mu sync.Mutex @@ -195,6 +216,13 @@ func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { } sw := NewWriterWithCompressor(esgzFile, opts.compression) sw.ChunkSize = opts.chunkSize + sw.MinChunkSize = opts.minChunkSize + if sw.needsOpenGzEntries == nil { + sw.needsOpenGzEntries = make(map[string]struct{}) + } + for _, f := range []string{PrefetchLandmark, NoPrefetchLandmark} { + sw.needsOpenGzEntries[f] = struct{}{} + } if err := sw.AppendTar(readerFromEntries(parts...)); err != nil { return err } @@ -209,7 +237,7 @@ func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { rErr = err return nil, err } - tocAndFooter, tocDgst, err := closeWithCombine(opts.compressionLevel, writers...) + tocAndFooter, tocDgst, err := closeWithCombine(writers...) if err != nil { rErr = err return nil, err @@ -252,7 +280,7 @@ func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { // Writers doesn't write TOC and footer to the underlying writers so they can be // combined into a single eStargz and tocAndFooter returned by this function can // be appended at the tail of that combined blob. -func closeWithCombine(compressionLevel int, ws ...*Writer) (tocAndFooterR io.Reader, tocDgst digest.Digest, err error) { +func closeWithCombine(ws ...*Writer) (tocAndFooterR io.Reader, tocDgst digest.Digest, err error) { if len(ws) == 0 { return nil, "", fmt.Errorf("at least one writer must be passed") } @@ -395,7 +423,7 @@ func readerFromEntries(entries ...*entry) io.Reader { func importTar(in io.ReaderAt) (*tarFile, error) { tf := &tarFile{} - pw, err := newCountReader(in) + pw, err := newCountReadSeeker(in) if err != nil { return nil, fmt.Errorf("failed to make position watcher: %w", err) } @@ -571,19 +599,19 @@ func (tf *tempFiles) cleanupAll() error { return errorutil.Aggregate(allErr) } -func newCountReader(r io.ReaderAt) (*countReader, error) { +func newCountReadSeeker(r io.ReaderAt) (*countReadSeeker, error) { pos := int64(0) - return &countReader{r: r, cPos: &pos}, nil + return &countReadSeeker{r: r, cPos: &pos}, nil } -type countReader struct { +type countReadSeeker struct { r io.ReaderAt cPos *int64 mu sync.Mutex } -func (cr *countReader) Read(p []byte) (int, error) { +func (cr *countReadSeeker) Read(p []byte) (int, error) { cr.mu.Lock() defer cr.mu.Unlock() @@ -594,7 +622,7 @@ func (cr *countReader) Read(p []byte) (int, error) { return n, err } -func (cr *countReader) Seek(offset int64, whence int) (int64, error) { +func (cr *countReadSeeker) Seek(offset int64, whence int) (int64, error) { cr.mu.Lock() defer cr.mu.Unlock() @@ -615,7 +643,7 @@ func (cr *countReader) Seek(offset int64, whence int) (int64, error) { return offset, nil } -func (cr *countReader) currentPos() int64 { +func (cr *countReadSeeker) currentPos() int64 { cr.mu.Lock() defer cr.mu.Unlock() diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go index 921e59ec..f4d55465 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go @@ -150,10 +150,10 @@ func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { allErr = append(allErr, err) continue } - if tocSize <= 0 { + if tocOffset >= 0 && tocSize <= 0 { tocSize = sr.Size() - tocOffset - fSize } - if tocSize < int64(len(maybeTocBytes)) { + if tocOffset >= 0 && tocSize < int64(len(maybeTocBytes)) { maybeTocBytes = maybeTocBytes[:tocSize] } r, err = parseTOC(d, sr, tocOffset, tocSize, maybeTocBytes, opts) @@ -207,8 +207,16 @@ func (r *Reader) initFields() error { uname := map[int]string{} gname := map[int]string{} var lastRegEnt *TOCEntry - for _, ent := range r.toc.Entries { + var chunkTopIndex int + for i, ent := range r.toc.Entries { ent.Name = cleanEntryName(ent.Name) + switch ent.Type { + case "reg", "chunk": + if ent.Offset != r.toc.Entries[chunkTopIndex].Offset { + chunkTopIndex = i + } + ent.chunkTopIndex = chunkTopIndex + } if ent.Type == "reg" { lastRegEnt = ent } @@ -294,7 +302,7 @@ func (r *Reader) initFields() error { if e.isDataType() { e.nextOffset = lastOffset } - if e.Offset != 0 { + if e.Offset != 0 && e.InnerOffset == 0 { lastOffset = e.Offset } } @@ -488,6 +496,14 @@ func (r *Reader) Lookup(path string) (e *TOCEntry, ok bool) { // // Name must be absolute path or one that is relative to root. func (r *Reader) OpenFile(name string) (*io.SectionReader, error) { + fr, err := r.newFileReader(name) + if err != nil { + return nil, err + } + return io.NewSectionReader(fr, 0, fr.size), nil +} + +func (r *Reader) newFileReader(name string) (*fileReader, error) { name = cleanEntryName(name) ent, ok := r.Lookup(name) if !ok { @@ -505,11 +521,19 @@ func (r *Reader) OpenFile(name string) (*io.SectionReader, error) { Err: errors.New("not a regular file"), } } - fr := &fileReader{ + return &fileReader{ r: r, size: ent.Size, ents: r.getChunks(ent), + }, nil +} + +func (r *Reader) OpenFileWithPreReader(name string, preRead func(*TOCEntry, io.Reader) error) (*io.SectionReader, error) { + fr, err := r.newFileReader(name) + if err != nil { + return nil, err } + fr.preRead = preRead return io.NewSectionReader(fr, 0, fr.size), nil } @@ -521,9 +545,10 @@ func (r *Reader) getChunks(ent *TOCEntry) []*TOCEntry { } type fileReader struct { - r *Reader - size int64 - ents []*TOCEntry // 1 or more reg/chunk entries + r *Reader + size int64 + ents []*TOCEntry // 1 or more reg/chunk entries + preRead func(*TOCEntry, io.Reader) error } func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { @@ -578,10 +603,48 @@ func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { return 0, fmt.Errorf("fileReader.ReadAt.decompressor.Reader: %v", err) } defer dr.Close() - if n, err := io.CopyN(io.Discard, dr, off); n != off || err != nil { - return 0, fmt.Errorf("discard of %d bytes = %v, %v", off, n, err) + + if fr.preRead == nil { + if n, err := io.CopyN(io.Discard, dr, ent.InnerOffset+off); n != ent.InnerOffset+off || err != nil { + return 0, fmt.Errorf("discard of %d bytes != %v, %v", ent.InnerOffset+off, n, err) + } + return io.ReadFull(dr, p) + } + + var retN int + var retErr error + var found bool + var nr int64 + for _, e := range fr.r.toc.Entries[ent.chunkTopIndex:] { + if !e.isDataType() { + continue + } + if e.Offset != fr.r.toc.Entries[ent.chunkTopIndex].Offset { + break + } + if in, err := io.CopyN(io.Discard, dr, e.InnerOffset-nr); err != nil || in != e.InnerOffset-nr { + return 0, fmt.Errorf("discard of remaining %d bytes != %v, %v", e.InnerOffset-nr, in, err) + } + nr = e.InnerOffset + if e == ent { + found = true + if n, err := io.CopyN(io.Discard, dr, off); n != off || err != nil { + return 0, fmt.Errorf("discard of offset %d bytes != %v, %v", off, n, err) + } + retN, retErr = io.ReadFull(dr, p) + nr += off + int64(retN) + continue + } + cr := &countReader{r: io.LimitReader(dr, e.ChunkSize)} + if err := fr.preRead(e, cr); err != nil { + return 0, fmt.Errorf("failed to pre read: %w", err) + } + nr += cr.n + } + if !found { + return 0, fmt.Errorf("fileReader.ReadAt: target entry not found") } - return io.ReadFull(dr, p) + return retN, retErr } // A Writer writes stargz files. @@ -599,11 +662,20 @@ type Writer struct { lastGroupname map[int]string compressor Compressor + uncompressedCounter *countWriteFlusher + // ChunkSize optionally controls the maximum number of bytes // of data of a regular file that can be written in one gzip // stream before a new gzip stream is started. // Zero means to use a default, currently 4 MiB. ChunkSize int + + // MinChunkSize optionally controls the minimum number of bytes + // of data must be written in one gzip stream before a new gzip + // NOTE: This adds a TOC property that stargz snapshotter < v0.13.0 doesn't understand. + MinChunkSize int + + needsOpenGzEntries map[string]struct{} } // currentCompressionWriter writes to the current w.gz field, which can @@ -646,6 +718,9 @@ func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) { if err != nil { return nil, fmt.Errorf("failed to parse footer: %w", err) } + if blobPayloadSize < 0 { + blobPayloadSize = sr.Size() + } return c.Reader(io.LimitReader(sr, blobPayloadSize)) } @@ -672,11 +747,12 @@ func NewWriterWithCompressor(w io.Writer, c Compressor) *Writer { bw := bufio.NewWriter(w) cw := &countWriter{w: bw} return &Writer{ - bw: bw, - cw: cw, - toc: &JTOC{Version: 1}, - diffHash: sha256.New(), - compressor: c, + bw: bw, + cw: cw, + toc: &JTOC{Version: 1}, + diffHash: sha256.New(), + compressor: c, + uncompressedCounter: &countWriteFlusher{}, } } @@ -717,6 +793,20 @@ func (w *Writer) closeGz() error { return nil } +func (w *Writer) flushGz() error { + if w.closed { + return errors.New("flush on closed Writer") + } + if w.gz != nil { + if f, ok := w.gz.(interface { + Flush() error + }); ok { + return f.Flush() + } + } + return nil +} + // nameIfChanged returns name, unless it was the already the value of (*mp)[id], // in which case it returns the empty string. func (w *Writer) nameIfChanged(mp *map[int]string, id int, name string) string { @@ -736,6 +826,9 @@ func (w *Writer) nameIfChanged(mp *map[int]string, id int, name string) string { func (w *Writer) condOpenGz() (err error) { if w.gz == nil { w.gz, err = w.compressor.Writer(w.cw) + if w.gz != nil { + w.gz = w.uncompressedCounter.register(w.gz) + } } return } @@ -784,6 +877,8 @@ func (w *Writer) appendTar(r io.Reader, lossless bool) error { if lossless { tr.RawAccounting = true } + prevOffset := w.cw.n + var prevOffsetUncompressed int64 for { h, err := tr.Next() if err == io.EOF { @@ -883,10 +978,6 @@ func (w *Writer) appendTar(r io.Reader, lossless bool) error { totalSize := ent.Size // save it before we destroy ent tee := io.TeeReader(tr, payloadDigest.Hash()) for written < totalSize { - if err := w.closeGz(); err != nil { - return err - } - chunkSize := int64(w.chunkSize()) remain := totalSize - written if remain < chunkSize { @@ -894,7 +985,23 @@ func (w *Writer) appendTar(r io.Reader, lossless bool) error { } else { ent.ChunkSize = chunkSize } - ent.Offset = w.cw.n + + // We flush the underlying compression writer here to correctly calculate "w.cw.n". + if err := w.flushGz(); err != nil { + return err + } + if w.needsOpenGz(ent) || w.cw.n-prevOffset >= int64(w.MinChunkSize) { + if err := w.closeGz(); err != nil { + return err + } + ent.Offset = w.cw.n + prevOffset = ent.Offset + prevOffsetUncompressed = w.uncompressedCounter.n + } else { + ent.Offset = prevOffset + ent.InnerOffset = w.uncompressedCounter.n - prevOffsetUncompressed + } + ent.ChunkOffset = written chunkDigest := digest.Canonical.Digester() @@ -940,6 +1047,17 @@ func (w *Writer) appendTar(r io.Reader, lossless bool) error { return err } +func (w *Writer) needsOpenGz(ent *TOCEntry) bool { + if ent.Type != "reg" { + return false + } + if w.needsOpenGzEntries == nil { + return false + } + _, ok := w.needsOpenGzEntries[ent.Name] + return ok +} + // DiffID returns the SHA-256 of the uncompressed tar bytes. // It is only valid to call DiffID after Close. func (w *Writer) DiffID() string { @@ -956,6 +1074,28 @@ func maxFooterSize(blobSize int64, decompressors ...Decompressor) (res int64) { } func parseTOC(d Decompressor, sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte, opts openOpts) (*Reader, error) { + if tocOff < 0 { + // This means that TOC isn't contained in the blob. + // We pass nil reader to ParseTOC and expect that ParseTOC acquire TOC from + // the external location. + start := time.Now() + toc, tocDgst, err := d.ParseTOC(nil) + if err != nil { + return nil, err + } + if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil { + opts.telemetry.GetTocLatency(start) + } + if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { + opts.telemetry.DeserializeTocLatency(start) + } + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: d, + }, nil + } if len(tocBytes) > 0 { start := time.Now() toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes)) @@ -1021,6 +1161,37 @@ func (cw *countWriter) Write(p []byte) (n int, err error) { return } +type countWriteFlusher struct { + io.WriteCloser + n int64 +} + +func (wc *countWriteFlusher) register(w io.WriteCloser) io.WriteCloser { + wc.WriteCloser = w + return wc +} + +func (wc *countWriteFlusher) Write(p []byte) (n int, err error) { + n, err = wc.WriteCloser.Write(p) + wc.n += int64(n) + return +} + +func (wc *countWriteFlusher) Flush() error { + if f, ok := wc.WriteCloser.(interface { + Flush() error + }); ok { + return f.Flush() + } + return nil +} + +func (wc *countWriteFlusher) Close() error { + err := wc.WriteCloser.Close() + wc.WriteCloser = nil + return err +} + // isGzip reports whether br is positioned right before an upcoming gzip stream. // It does not consume any bytes from br. func isGzip(br *bufio.Reader) bool { @@ -1039,3 +1210,14 @@ func positive(n int64) int64 { } return n } + +type countReader struct { + r io.Reader + n int64 +} + +func (cr *countReader) Read(p []byte) (n int, err error) { + n, err = cr.r.Read(p) + cr.n += int64(n) + return +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go index 591d7a62..f24afe32 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go @@ -60,7 +60,7 @@ type GzipCompressor struct { compressionLevel int } -func (gc *GzipCompressor) Writer(w io.Writer) (io.WriteCloser, error) { +func (gc *GzipCompressor) Writer(w io.Writer) (WriteFlushCloser, error) { return gzip.NewWriterLevel(w, gc.compressionLevel) } diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go index 37448cae..0ca6fd75 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go @@ -31,6 +31,7 @@ import ( "errors" "fmt" "io" + "math/rand" "os" "path/filepath" "reflect" @@ -44,21 +45,27 @@ import ( digest "github.com/opencontainers/go-digest" ) +func init() { + rand.Seed(time.Now().UnixNano()) +} + // TestingController is Compression with some helper methods necessary for testing. type TestingController interface { Compression - CountStreams(*testing.T, []byte) int + TestStreams(t *testing.T, b []byte, streams []int64) DiffIDOf(*testing.T, []byte) string String() string } // CompressionTestSuite tests this pkg with controllers can build valid eStargz blobs and parse them. -func CompressionTestSuite(t *testing.T, controllers ...TestingController) { +func CompressionTestSuite(t *testing.T, controllers ...TestingControllerFactory) { t.Run("testBuild", func(t *testing.T) { t.Parallel(); testBuild(t, controllers...) }) t.Run("testDigestAndVerify", func(t *testing.T) { t.Parallel(); testDigestAndVerify(t, controllers...) }) t.Run("testWriteAndOpen", func(t *testing.T) { t.Parallel(); testWriteAndOpen(t, controllers...) }) } +type TestingControllerFactory func() TestingController + const ( uncompressedType int = iota gzipType @@ -75,11 +82,12 @@ var allowedPrefix = [4]string{"", "./", "/", "../"} // testBuild tests the resulting stargz blob built by this pkg has the same // contents as the normal stargz blob. -func testBuild(t *testing.T, controllers ...TestingController) { +func testBuild(t *testing.T, controllers ...TestingControllerFactory) { tests := []struct { - name string - chunkSize int - in []tarEntry + name string + chunkSize int + minChunkSize []int + in []tarEntry }{ { name: "regfiles and directories", @@ -108,11 +116,14 @@ func testBuild(t *testing.T, controllers ...TestingController) { ), }, { - name: "various files", - chunkSize: 4, + name: "various files", + chunkSize: 4, + minChunkSize: []int{0, 64000}, in: tarOf( file("baz.txt", "bazbazbazbazbazbazbaz"), - file("foo.txt", "a"), + file("foo1.txt", "a"), + file("bar/foo2.txt", "b"), + file("foo3.txt", "c"), symlink("barlink", "test/bar.txt"), dir("test/"), dir("dev/"), @@ -144,99 +155,112 @@ func testBuild(t *testing.T, controllers ...TestingController) { }, } for _, tt := range tests { + if len(tt.minChunkSize) == 0 { + tt.minChunkSize = []int{0} + } for _, srcCompression := range srcCompressions { srcCompression := srcCompression - for _, cl := range controllers { - cl := cl + for _, newCL := range controllers { + newCL := newCL for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat for _, prefix := range allowedPrefix { prefix := prefix - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s", cl, prefix, srcCompression, srcTarFormat), func(t *testing.T) { - tarBlob := buildTar(t, tt.in, prefix, srcTarFormat) - // Test divideEntries() - entries, err := sortEntries(tarBlob, nil, nil) // identical order - if err != nil { - t.Fatalf("failed to parse tar: %v", err) - } - var merged []*entry - for _, part := range divideEntries(entries, 4) { - merged = append(merged, part...) - } - if !reflect.DeepEqual(entries, merged) { - for _, e := range entries { - t.Logf("Original: %v", e.header) + for _, minChunkSize := range tt.minChunkSize { + minChunkSize := minChunkSize + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s,minChunkSize=%d", newCL(), prefix, srcCompression, srcTarFormat, minChunkSize), func(t *testing.T) { + tarBlob := buildTar(t, tt.in, prefix, srcTarFormat) + // Test divideEntries() + entries, err := sortEntries(tarBlob, nil, nil) // identical order + if err != nil { + t.Fatalf("failed to parse tar: %v", err) } - for _, e := range merged { - t.Logf("Merged: %v", e.header) + var merged []*entry + for _, part := range divideEntries(entries, 4) { + merged = append(merged, part...) + } + if !reflect.DeepEqual(entries, merged) { + for _, e := range entries { + t.Logf("Original: %v", e.header) + } + for _, e := range merged { + t.Logf("Merged: %v", e.header) + } + t.Errorf("divided entries couldn't be merged") + return } - t.Errorf("divided entries couldn't be merged") - return - } - // Prepare sample data - wantBuf := new(bytes.Buffer) - sw := NewWriterWithCompressor(wantBuf, cl) - sw.ChunkSize = tt.chunkSize - if err := sw.AppendTar(tarBlob); err != nil { - t.Fatalf("failed to append tar to want stargz: %v", err) - } - if _, err := sw.Close(); err != nil { - t.Fatalf("failed to prepare want stargz: %v", err) - } - wantData := wantBuf.Bytes() - want, err := Open(io.NewSectionReader( - bytes.NewReader(wantData), 0, int64(len(wantData))), - WithDecompressors(cl), - ) - if err != nil { - t.Fatalf("failed to parse the want stargz: %v", err) - } + // Prepare sample data + cl1 := newCL() + wantBuf := new(bytes.Buffer) + sw := NewWriterWithCompressor(wantBuf, cl1) + sw.MinChunkSize = minChunkSize + sw.ChunkSize = tt.chunkSize + if err := sw.AppendTar(tarBlob); err != nil { + t.Fatalf("failed to append tar to want stargz: %v", err) + } + if _, err := sw.Close(); err != nil { + t.Fatalf("failed to prepare want stargz: %v", err) + } + wantData := wantBuf.Bytes() + want, err := Open(io.NewSectionReader( + bytes.NewReader(wantData), 0, int64(len(wantData))), + WithDecompressors(cl1), + ) + if err != nil { + t.Fatalf("failed to parse the want stargz: %v", err) + } - // Prepare testing data - rc, err := Build(compressBlob(t, tarBlob, srcCompression), - WithChunkSize(tt.chunkSize), WithCompression(cl)) - if err != nil { - t.Fatalf("failed to build stargz: %v", err) - } - defer rc.Close() - gotBuf := new(bytes.Buffer) - if _, err := io.Copy(gotBuf, rc); err != nil { - t.Fatalf("failed to copy built stargz blob: %v", err) - } - gotData := gotBuf.Bytes() - got, err := Open(io.NewSectionReader( - bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), - WithDecompressors(cl), - ) - if err != nil { - t.Fatalf("failed to parse the got stargz: %v", err) - } + // Prepare testing data + var opts []Option + if minChunkSize > 0 { + opts = append(opts, WithMinChunkSize(minChunkSize)) + } + cl2 := newCL() + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + append(opts, WithChunkSize(tt.chunkSize), WithCompression(cl2))...) + if err != nil { + t.Fatalf("failed to build stargz: %v", err) + } + defer rc.Close() + gotBuf := new(bytes.Buffer) + if _, err := io.Copy(gotBuf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + gotData := gotBuf.Bytes() + got, err := Open(io.NewSectionReader( + bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), + WithDecompressors(cl2), + ) + if err != nil { + t.Fatalf("failed to parse the got stargz: %v", err) + } - // Check DiffID is properly calculated - rc.Close() - diffID := rc.DiffID() - wantDiffID := cl.DiffIDOf(t, gotData) - if diffID.String() != wantDiffID { - t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) - } + // Check DiffID is properly calculated + rc.Close() + diffID := rc.DiffID() + wantDiffID := cl2.DiffIDOf(t, gotData) + if diffID.String() != wantDiffID { + t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) + } - // Compare as stargz - if !isSameVersion(t, cl, wantData, gotData) { - t.Errorf("built stargz hasn't same json") - return - } - if !isSameEntries(t, want, got) { - t.Errorf("built stargz isn't same as the original") - return - } + // Compare as stargz + if !isSameVersion(t, cl1, wantData, cl2, gotData) { + t.Errorf("built stargz hasn't same json") + return + } + if !isSameEntries(t, want, got) { + t.Errorf("built stargz isn't same as the original") + return + } - // Compare as tar.gz - if !isSameTarGz(t, cl, wantData, gotData) { - t.Errorf("built stargz isn't same tar.gz") - return - } - }) + // Compare as tar.gz + if !isSameTarGz(t, cl1, wantData, cl2, gotData) { + t.Errorf("built stargz isn't same tar.gz") + return + } + }) + } } } } @@ -244,13 +268,13 @@ func testBuild(t *testing.T, controllers ...TestingController) { } } -func isSameTarGz(t *testing.T, controller TestingController, a, b []byte) bool { - aGz, err := controller.Reader(bytes.NewReader(a)) +func isSameTarGz(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool { + aGz, err := cla.Reader(bytes.NewReader(a)) if err != nil { t.Fatalf("failed to read A") } defer aGz.Close() - bGz, err := controller.Reader(bytes.NewReader(b)) + bGz, err := clb.Reader(bytes.NewReader(b)) if err != nil { t.Fatalf("failed to read B") } @@ -304,12 +328,12 @@ func isSameTarGz(t *testing.T, controller TestingController, a, b []byte) bool { return true } -func isSameVersion(t *testing.T, controller TestingController, a, b []byte) bool { - aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), controller) +func isSameVersion(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool { + aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), cla) if err != nil { t.Fatalf("failed to parse A: %v", err) } - bJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), controller) + bJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), clb) if err != nil { t.Fatalf("failed to parse B: %v", err) } @@ -463,7 +487,7 @@ func equalEntry(a, b *TOCEntry) bool { a.GID == b.GID && a.Uname == b.Uname && a.Gname == b.Gname && - (a.Offset > 0) == (b.Offset > 0) && + (a.Offset >= 0) == (b.Offset >= 0) && (a.NextOffset() > 0) == (b.NextOffset() > 0) && a.DevMajor == b.DevMajor && a.DevMinor == b.DevMinor && @@ -510,14 +534,15 @@ func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string { const chunkSize = 3 // type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, compressionLevel int) -type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) +type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) // testDigestAndVerify runs specified checks against sample stargz blobs. -func testDigestAndVerify(t *testing.T, controllers ...TestingController) { +func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory) { tests := []struct { - name string - tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) - checks []check + name string + tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) + checks []check + minChunkSize []int }{ { name: "no-regfile", @@ -544,6 +569,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { regDigest(t, "test/bar.txt", "bbb", dgstMap), ) }, + minChunkSize: []int{0, 64000}, checks: []check{ checkStargzTOC, checkVerifyTOC, @@ -581,11 +607,14 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { }, }, { - name: "with-non-regfiles", + name: "with-non-regfiles", + minChunkSize: []int{0, 64000}, tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { return tarOf( regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap), regDigest(t, "foo.txt", "a", dgstMap), + regDigest(t, "bar/foo2.txt", "b", dgstMap), + regDigest(t, "foo3.txt", "c", dgstMap), symlink("barlink", "test/bar.txt"), dir("test/"), regDigest(t, "test/bar.txt", "testbartestbar", dgstMap), @@ -599,6 +628,8 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", "bazbazbazbazbazbazbaz"), file("foo.txt", "a"), + file("bar/foo2.txt", "b"), + file("foo3.txt", "c"), symlink("barlink", "test/bar.txt"), dir("test/"), file("test/bar.txt", "testbartestbar"), @@ -612,38 +643,45 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { } for _, tt := range tests { + if len(tt.minChunkSize) == 0 { + tt.minChunkSize = []int{0} + } for _, srcCompression := range srcCompressions { srcCompression := srcCompression - for _, cl := range controllers { - cl := cl + for _, newCL := range controllers { + newCL := newCL for _, prefix := range allowedPrefix { prefix := prefix for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s", cl, prefix, srcTarFormat), func(t *testing.T) { - // Get original tar file and chunk digests - dgstMap := make(map[string]digest.Digest) - tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat) - - rc, err := Build(compressBlob(t, tarBlob, srcCompression), - WithChunkSize(chunkSize), WithCompression(cl)) - if err != nil { - t.Fatalf("failed to convert stargz: %v", err) - } - tocDigest := rc.TOCDigest() - defer rc.Close() - buf := new(bytes.Buffer) - if _, err := io.Copy(buf, rc); err != nil { - t.Fatalf("failed to copy built stargz blob: %v", err) - } - newStargz := buf.Bytes() - // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. - dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) + for _, minChunkSize := range tt.minChunkSize { + minChunkSize := minChunkSize + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s,minChunkSize=%d", newCL(), prefix, srcTarFormat, minChunkSize), func(t *testing.T) { + // Get original tar file and chunk digests + dgstMap := make(map[string]digest.Digest) + tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat) + + cl := newCL() + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + WithChunkSize(chunkSize), WithCompression(cl)) + if err != nil { + t.Fatalf("failed to convert stargz: %v", err) + } + tocDigest := rc.TOCDigest() + defer rc.Close() + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + newStargz := buf.Bytes() + // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. + dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) - for _, check := range tt.checks { - check(t, newStargz, tocDigest, dgstMap, cl) - } - }) + for _, check := range tt.checks { + check(t, newStargz, tocDigest, dgstMap, cl, newCL) + } + }) + } } } } @@ -654,7 +692,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingController) { // checkStargzTOC checks the TOC JSON of the passed stargz has the expected // digest and contains valid chunks. It walks all entries in the stargz and // checks all chunk digests stored to the TOC JSON match the actual contents. -func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { +func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), WithDecompressors(controller), @@ -765,7 +803,7 @@ func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstM // checkVerifyTOC checks the verification works for the TOC JSON of the passed // stargz. It walks all entries in the stargz and checks the verifications for // all chunks work. -func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { +func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), WithDecompressors(controller), @@ -846,7 +884,7 @@ func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstM // checkVerifyInvalidTOCEntryFail checks if misconfigured TOC JSON can be // detected during the verification and the verification returns an error. func checkVerifyInvalidTOCEntryFail(filename string) check { - return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { funcs := map[string]rewriteFunc{ "lost digest in a entry": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) { var found bool @@ -920,8 +958,9 @@ func checkVerifyInvalidTOCEntryFail(filename string) check { // checkVerifyInvalidStargzFail checks if the verification detects that the // given stargz file doesn't match to the expected digest and returns error. func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check { - return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { - rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(controller)) + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { + cl := newController() + rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(cl)) if err != nil { t.Fatalf("failed to convert stargz: %v", err) } @@ -934,7 +973,7 @@ func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check { sgz, err := Open( io.NewSectionReader(bytes.NewReader(mStargz), 0, int64(len(mStargz))), - WithDecompressors(controller), + WithDecompressors(cl), ) if err != nil { t.Fatalf("failed to parse converted stargz: %v", err) @@ -951,7 +990,7 @@ func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check { // checkVerifyBrokenContentFail checks if the verifier detects broken contents // that doesn't match to the expected digest and returns error. func checkVerifyBrokenContentFail(filename string) check { - return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { // Parse stargz file sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), @@ -1070,7 +1109,10 @@ func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJT } // Decode the TOC JSON - tocReader := io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize) + var tocReader io.Reader + if tocOffset >= 0 { + tocReader = io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize) + } decodedJTOC, _, err = controller.ParseTOC(tocReader) if err != nil { return nil, 0, fmt.Errorf("failed to parse TOC: %w", err) @@ -1078,28 +1120,31 @@ func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJT return decodedJTOC, tocOffset, nil } -func testWriteAndOpen(t *testing.T, controllers ...TestingController) { +func testWriteAndOpen(t *testing.T, controllers ...TestingControllerFactory) { const content = "Some contents" invalidUtf8 := "\xff\xfe\xfd" xAttrFile := xAttr{"foo": "bar", "invalid-utf8": invalidUtf8} sampleOwner := owner{uid: 50, gid: 100} + data64KB := randomContents(64000) + tests := []struct { - name string - chunkSize int - in []tarEntry - want []stargzCheck - wantNumGz int // expected number of streams + name string + chunkSize int + minChunkSize int + in []tarEntry + want []stargzCheck + wantNumGz int // expected number of streams wantNumGzLossLess int // expected number of streams (> 0) in lossless mode if it's different from wantNumGz wantFailOnLossLess bool + wantTOCVersion int // default = 1 }{ { - name: "empty", - in: tarOf(), - wantNumGz: 2, // empty tar + TOC + footer - wantNumGzLossLess: 3, // empty tar + TOC + footer + name: "empty", + in: tarOf(), + wantNumGz: 2, // (empty tar) + TOC + footer want: checks( numTOCEntries(0), ), @@ -1195,7 +1240,7 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { dir("foo/"), file("foo/big.txt", "This "+"is s"+"uch "+"a bi"+"g fi"+"le"), ), - wantNumGz: 9, + wantNumGz: 9, // dir + big.txt(6 chunks) + TOC + footer want: checks( numTOCEntries(7), // 1 for foo dir, 6 for the foo/big.txt file hasDir("foo/"), @@ -1326,23 +1371,108 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { mustSameEntry("foo/foo1", "foolink"), ), }, + { + name: "several_files_in_chunk", + minChunkSize: 8000, + in: tarOf( + dir("foo/"), + file("foo/foo1", data64KB), + file("foo2", "bb"), + file("foo22", "ccc"), + dir("bar/"), + file("bar/bar.txt", "aaa"), + file("foo3", data64KB), + ), + // NOTE: we assume that the compressed "data64KB" is still larger than 8KB + wantNumGz: 4, // dir+foo1, foo2+foo22+dir+bar.txt+foo3, TOC, footer + want: checks( + numTOCEntries(7), // dir, foo1, foo2, foo22, dir, bar.txt, foo3 + hasDir("foo/"), + hasDir("bar/"), + hasFileLen("foo/foo1", len(data64KB)), + hasFileLen("foo2", len("bb")), + hasFileLen("foo22", len("ccc")), + hasFileLen("bar/bar.txt", len("aaa")), + hasFileLen("foo3", len(data64KB)), + hasFileDigest("foo/foo1", digestFor(data64KB)), + hasFileDigest("foo2", digestFor("bb")), + hasFileDigest("foo22", digestFor("ccc")), + hasFileDigest("bar/bar.txt", digestFor("aaa")), + hasFileDigest("foo3", digestFor(data64KB)), + hasFileContentsWithPreRead("foo22", 0, "ccc", chunkInfo{"foo2", "bb"}, chunkInfo{"bar/bar.txt", "aaa"}, chunkInfo{"foo3", data64KB}), + hasFileContentsRange("foo/foo1", 0, data64KB), + hasFileContentsRange("foo2", 0, "bb"), + hasFileContentsRange("foo2", 1, "b"), + hasFileContentsRange("foo22", 0, "ccc"), + hasFileContentsRange("foo22", 1, "cc"), + hasFileContentsRange("foo22", 2, "c"), + hasFileContentsRange("bar/bar.txt", 0, "aaa"), + hasFileContentsRange("bar/bar.txt", 1, "aa"), + hasFileContentsRange("bar/bar.txt", 2, "a"), + hasFileContentsRange("foo3", 0, data64KB), + hasFileContentsRange("foo3", 1, data64KB[1:]), + hasFileContentsRange("foo3", 2, data64KB[2:]), + hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]), + hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]), + ), + }, + { + name: "several_files_in_chunk_chunked", + minChunkSize: 8000, + chunkSize: 32000, + in: tarOf( + dir("foo/"), + file("foo/foo1", data64KB), + file("foo2", "bb"), + dir("bar/"), + file("foo3", data64KB), + ), + // NOTE: we assume that the compressed chunk of "data64KB" is still larger than 8KB + wantNumGz: 6, // dir+foo1(1), foo1(2), foo2+dir+foo3(1), foo3(2), TOC, footer + want: checks( + numTOCEntries(7), // dir, foo1(2 chunks), foo2, dir, foo3(2 chunks) + hasDir("foo/"), + hasDir("bar/"), + hasFileLen("foo/foo1", len(data64KB)), + hasFileLen("foo2", len("bb")), + hasFileLen("foo3", len(data64KB)), + hasFileDigest("foo/foo1", digestFor(data64KB)), + hasFileDigest("foo2", digestFor("bb")), + hasFileDigest("foo3", digestFor(data64KB)), + hasFileContentsWithPreRead("foo2", 0, "bb", chunkInfo{"foo3", data64KB[:32000]}), + hasFileContentsRange("foo/foo1", 0, data64KB), + hasFileContentsRange("foo/foo1", 1, data64KB[1:]), + hasFileContentsRange("foo/foo1", 2, data64KB[2:]), + hasFileContentsRange("foo/foo1", len(data64KB)/2, data64KB[len(data64KB)/2:]), + hasFileContentsRange("foo/foo1", len(data64KB)-1, data64KB[len(data64KB)-1:]), + hasFileContentsRange("foo2", 0, "bb"), + hasFileContentsRange("foo2", 1, "b"), + hasFileContentsRange("foo3", 0, data64KB), + hasFileContentsRange("foo3", 1, data64KB[1:]), + hasFileContentsRange("foo3", 2, data64KB[2:]), + hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]), + hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]), + ), + }, } for _, tt := range tests { - for _, cl := range controllers { - cl := cl + for _, newCL := range controllers { + newCL := newCL for _, prefix := range allowedPrefix { prefix := prefix for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat for _, lossless := range []bool{true, false} { - t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", cl, prefix, lossless, srcTarFormat), func(t *testing.T) { + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", newCL(), prefix, lossless, srcTarFormat), func(t *testing.T) { var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat) origTarDgstr := digest.Canonical.Digester() tr = io.TeeReader(tr, origTarDgstr.Hash()) var stargzBuf bytes.Buffer - w := NewWriterWithCompressor(&stargzBuf, cl) + cl1 := newCL() + w := NewWriterWithCompressor(&stargzBuf, cl1) w.ChunkSize = tt.chunkSize + w.MinChunkSize = tt.minChunkSize if lossless { err := w.AppendTarLossLess(tr) if tt.wantFailOnLossLess { @@ -1366,7 +1496,7 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { if lossless { // Check if the result blob reserves original tar metadata - rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl) + rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl1) if err != nil { t.Errorf("failed to decompress blob: %v", err) return @@ -1385,32 +1515,71 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { } diffID := w.DiffID() - wantDiffID := cl.DiffIDOf(t, b) + wantDiffID := cl1.DiffIDOf(t, b) if diffID != wantDiffID { t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) } - got := cl.CountStreams(t, b) - wantNumGz := tt.wantNumGz - if lossless && tt.wantNumGzLossLess > 0 { - wantNumGz = tt.wantNumGzLossLess - } - if got != wantNumGz { - t.Errorf("number of streams = %d; want %d", got, wantNumGz) - } - telemetry, checkCalled := newCalledTelemetry() + sr := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) r, err := Open( - io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), - WithDecompressors(cl), + sr, + WithDecompressors(cl1), WithTelemetry(telemetry), ) if err != nil { t.Fatalf("stargz.Open: %v", err) } - if err := checkCalled(); err != nil { + wantTOCVersion := 1 + if tt.wantTOCVersion > 0 { + wantTOCVersion = tt.wantTOCVersion + } + if r.toc.Version != wantTOCVersion { + t.Fatalf("invalid TOC Version %d; wanted %d", r.toc.Version, wantTOCVersion) + } + + footerSize := cl1.FooterSize() + footerOffset := sr.Size() - footerSize + footer := make([]byte, footerSize) + if _, err := sr.ReadAt(footer, footerOffset); err != nil { + t.Errorf("failed to read footer: %v", err) + } + _, tocOffset, _, err := cl1.ParseFooter(footer) + if err != nil { + t.Errorf("failed to parse footer: %v", err) + } + if err := checkCalled(tocOffset >= 0); err != nil { t.Errorf("telemetry failure: %v", err) } + + wantNumGz := tt.wantNumGz + if lossless && tt.wantNumGzLossLess > 0 { + wantNumGz = tt.wantNumGzLossLess + } + streamOffsets := []int64{0} + prevOffset := int64(-1) + streams := 0 + for _, e := range r.toc.Entries { + if e.Offset > prevOffset { + streamOffsets = append(streamOffsets, e.Offset) + prevOffset = e.Offset + streams++ + } + } + streams++ // TOC + if tocOffset >= 0 { + // toc is in the blob + streamOffsets = append(streamOffsets, tocOffset) + } + streams++ // footer + streamOffsets = append(streamOffsets, footerOffset) + if streams != wantNumGz { + t.Errorf("number of streams in TOC = %d; want %d", streams, wantNumGz) + } + + t.Logf("testing streams: %+v", streamOffsets) + cl1.TestStreams(t, b, streamOffsets) + for _, want := range tt.want { want.check(t, r) } @@ -1422,7 +1591,12 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingController) { } } -func newCalledTelemetry() (telemetry *Telemetry, check func() error) { +type chunkInfo struct { + name string + data string +} + +func newCalledTelemetry() (telemetry *Telemetry, check func(needsGetTOC bool) error) { var getFooterLatencyCalled bool var getTocLatencyCalled bool var deserializeTocLatencyCalled bool @@ -1430,13 +1604,15 @@ func newCalledTelemetry() (telemetry *Telemetry, check func() error) { func(time.Time) { getFooterLatencyCalled = true }, func(time.Time) { getTocLatencyCalled = true }, func(time.Time) { deserializeTocLatencyCalled = true }, - }, func() error { + }, func(needsGetTOC bool) error { var allErr []error if !getFooterLatencyCalled { allErr = append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called")) } - if !getTocLatencyCalled { - allErr = append(allErr, fmt.Errorf("metrics GetTocLatency isn't called")) + if needsGetTOC { + if !getTocLatencyCalled { + allErr = append(allErr, fmt.Errorf("metrics GetTocLatency isn't called")) + } } if !deserializeTocLatencyCalled { allErr = append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called")) @@ -1573,6 +1749,53 @@ func hasFileDigest(file string, digest string) stargzCheck { }) } +func hasFileContentsWithPreRead(file string, offset int, want string, extra ...chunkInfo) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + extraMap := make(map[string]chunkInfo) + for _, e := range extra { + extraMap[e.name] = e + } + var extraNames []string + for n := range extraMap { + extraNames = append(extraNames, n) + } + f, err := r.OpenFileWithPreReader(file, func(e *TOCEntry, cr io.Reader) error { + t.Logf("On %q: got preread of %q", file, e.Name) + ex, ok := extraMap[e.Name] + if !ok { + t.Fatalf("fail on %q: unexpected entry %q: %+v, %+v", file, e.Name, e, extraNames) + } + got, err := io.ReadAll(cr) + if err != nil { + t.Fatalf("fail on %q: failed to read %q: %v", file, e.Name, err) + } + if ex.data != string(got) { + t.Fatalf("fail on %q: unexpected contents of %q: len=%d; want=%d", file, e.Name, len(got), len(ex.data)) + } + delete(extraMap, e.Name) + return nil + }) + if err != nil { + t.Fatal(err) + } + got := make([]byte, len(want)) + n, err := f.ReadAt(got, int64(offset)) + if err != nil { + t.Fatalf("ReadAt(len %d, offset %d, size %d) = %v, %v", len(got), offset, f.Size(), n, err) + } + if string(got) != want { + t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want))) + } + if len(extraMap) != 0 { + var exNames []string + for _, ex := range extraMap { + exNames = append(exNames, ex.name) + } + t.Fatalf("fail on %q: some entries aren't read: %+v", file, exNames) + } + }) +} + func hasFileContentsRange(file string, offset int, want string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { f, err := r.OpenFile(file) @@ -1585,7 +1808,7 @@ func hasFileContentsRange(file string, offset int, want string) stargzCheck { t.Fatalf("ReadAt(len %d, offset %d) = %v, %v", len(got), offset, n, err) } if string(got) != want { - t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, got, want) + t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want))) } }) } @@ -1797,6 +2020,13 @@ func mustSameEntry(files ...string) stargzCheck { }) } +func viewContent(c []byte) string { + if len(c) < 100 { + return string(c) + } + return string(c[:50]) + "...(omit)..." + string(c[50:100]) +} + func tarOf(s ...tarEntry) []tarEntry { return s } type tarEntry interface { @@ -2056,6 +2286,16 @@ func regDigest(t *testing.T, name string, contentStr string, digestMap map[strin }) } +var runes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randomContents(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = runes[rand.Intn(len(runes))] + } + return string(b) +} + func fileModeToTarMode(mode os.FileMode) (int64, error) { h, err := tar.FileInfoHeader(fileInfoOnlyMode(mode), "") if err != nil { @@ -2073,3 +2313,54 @@ func (f fileInfoOnlyMode) Mode() os.FileMode { return os.FileMode(f) } func (f fileInfoOnlyMode) ModTime() time.Time { return time.Now() } func (f fileInfoOnlyMode) IsDir() bool { return os.FileMode(f).IsDir() } func (f fileInfoOnlyMode) Sys() interface{} { return nil } + +func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) { + if len(streams) == 0 { + return // nop + } + + wants := map[int64]struct{}{} + for _, s := range streams { + wants[s] = struct{}{} + } + + len0 := len(b) + br := bytes.NewReader(b) + zr := new(gzip.Reader) + t.Logf("got gzip streams:") + numStreams := 0 + for { + zoff := len0 - br.Len() + if err := zr.Reset(br); err != nil { + if err == io.EOF { + return + } + t.Fatalf("countStreams(gzip), Reset: %v", err) + } + zr.Multistream(false) + n, err := io.Copy(io.Discard, zr) + if err != nil { + t.Fatalf("countStreams(gzip), Copy: %v", err) + } + var extra string + if len(zr.Header.Extra) > 0 { + extra = fmt.Sprintf("; extra=%q", zr.Header.Extra) + } + t.Logf(" [%d] at %d in stargz, uncompressed length %d%s", numStreams, zoff, n, extra) + delete(wants, int64(zoff)) + numStreams++ + } +} + +func GzipDiffIDOf(t *testing.T, b []byte) string { + h := sha256.New() + zr, err := gzip.NewReader(bytes.NewReader(b)) + if err != nil { + t.Fatalf("diffIDOf(gzip): %v", err) + } + defer zr.Close() + if _, err := io.Copy(h, zr); err != nil { + t.Fatalf("diffIDOf(gzip).Copy: %v", err) + } + return fmt.Sprintf("sha256:%x", h.Sum(nil)) +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go index 3bc74463..57e0aa61 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go @@ -149,6 +149,12 @@ type TOCEntry struct { // ChunkSize. Offset int64 `json:"offset,omitempty"` + // InnerOffset is an optional field indicates uncompressed offset + // of this "reg" or "chunk" payload in a stream starts from Offset. + // This field enables to put multiple "reg" or "chunk" payloads + // in one chunk with having the same Offset but different InnerOffset. + InnerOffset int64 `json:"innerOffset,omitempty"` + nextOffset int64 // the Offset of the next entry with a non-zero Offset // DevMajor is the major device number for "char" and "block" types. @@ -186,6 +192,9 @@ type TOCEntry struct { ChunkDigest string `json:"chunkDigest,omitempty"` children map[string]*TOCEntry + + // chunkTopIndex is index of the entry where Offset starts in the blob. + chunkTopIndex int } // ModTime returns the entry's modification time. @@ -279,7 +288,10 @@ type Compressor interface { // Writer returns WriteCloser to be used for writing a chunk to eStargz. // Everytime a chunk is written, the WriteCloser is closed and Writer is // called again for writing the next chunk. - Writer(w io.Writer) (io.WriteCloser, error) + // + // The returned writer should implement "Flush() error" function that flushes + // any pending compressed data to the underlying writer. + Writer(w io.Writer) (WriteFlushCloser, error) // WriteTOCAndFooter is called to write JTOC to the passed Writer. // diffHash calculates the DiffID (uncompressed sha256 hash) of the blob @@ -303,8 +315,12 @@ type Decompressor interface { // payloadBlobSize is the (compressed) size of the blob payload (i.e. the size between // the top until the TOC JSON). // - // Here, tocSize is optional. If tocSize <= 0, it's by default the size of the range - // from tocOffset until the beginning of the footer (blob size - tocOff - FooterSize). + // If tocOffset < 0, we assume that TOC isn't contained in the blob and pass nil reader + // to ParseTOC. We expect that ParseTOC acquire TOC from the external location and return it. + // + // tocSize is optional. If tocSize <= 0, it's by default the size of the range from tocOffset until the beginning of the + // footer (blob size - tocOff - FooterSize). + // If blobPayloadSize < 0, blobPayloadSize become the blob size. ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) // ParseTOC parses TOC from the passed reader. The reader provides the partial contents @@ -313,5 +329,14 @@ type Decompressor interface { // This function returns tocDgst that represents the digest of TOC that will be used // to verify this blob. This must match to the value returned from // Compressor.WriteTOCAndFooter that is used when creating this blob. + // + // If tocOffset returned by ParseFooter is < 0, we assume that TOC isn't contained in the blob. + // Pass nil reader to ParseTOC then we expect that ParseTOC acquire TOC from the external location + // and return it. ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) } + +type WriteFlushCloser interface { + io.WriteCloser + Flush() error +} diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index 114ee03c..292161c3 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -27,13 +27,13 @@ env: #### # GCE project where images live IMAGE_PROJECT: "libpod-218412" - FEDORA_NAME: "fedora-36" - #PRIOR_FEDORA_NAME: "fedora-35" + FEDORA_NAME: "fedora-37" + PRIOR_FEDORA_NAME: "fedora-36" UBUNTU_NAME: "ubuntu-2204" - IMAGE_SUFFIX: "c5124654741323776" + IMAGE_SUFFIX: "c6300530360713216" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" - #PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" + PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" IN_PODMAN_IMAGE: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}" @@ -72,9 +72,9 @@ meta_task: env: # Space-separated list of images used by this repository state - # TODO: Re-add ${PRIOR_FEDORA_CACHE_IMAGE_NAME} when place back in use IMGNAMES: |- ${FEDORA_CACHE_IMAGE_NAME} + ${PRIOR_FEDORA_CACHE_IMAGE_NAME} ${UBUNTU_CACHE_IMAGE_NAME} build-push-${IMAGE_SUFFIX} BUILDID: "${CIRRUS_BUILD_ID}" @@ -140,7 +140,7 @@ cross_build_task: $CIRRUS_CRON != 'multiarch' osx_instance: - image: 'big-sur-base' + image: ghcr.io/cirruslabs/macos-ventura-base:latest script: - brew update @@ -215,10 +215,10 @@ integration_task: DISTRO_NV: "${FEDORA_NAME}" IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'vfs' - # - env: - # DISTRO_NV: "${PRIOR_FEDORA_NAME}" - # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - # STORAGE_DRIVER: 'vfs' + - env: + DISTRO_NV: "${PRIOR_FEDORA_NAME}" + IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'vfs' - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" @@ -228,10 +228,10 @@ integration_task: DISTRO_NV: "${FEDORA_NAME}" IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' - # - env: - # DISTRO_NV: "${PRIOR_FEDORA_NAME}" - # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - # STORAGE_DRIVER: 'overlay' + - env: + DISTRO_NV: "${PRIOR_FEDORA_NAME}" + IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'overlay' - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" @@ -272,11 +272,11 @@ integration_rootless_task: IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' PRIV_NAME: rootless - # - env: - # DISTRO_NV: "${PRIOR_FEDORA_NAME}" - # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - # STORAGE_DRIVER: 'overlay' - # PRIV_NAME: rootless + - env: + DISTRO_NV: "${PRIOR_FEDORA_NAME}" + IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'overlay' + PRIV_NAME: rootless - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" @@ -322,7 +322,7 @@ in_podman_task: image_build_task: &image-build - name: "Build multi-arch $CTXDIR" + name: "Build multi-arch $FLAVOR" alias: image_build # Some of these container images take > 1h to build, limit # this task to a specific Cirrus-Cron entry with this name. @@ -337,11 +337,11 @@ image_build_task: &image-build type: "n2-standard-4" matrix: - env: - CTXDIR: contrib/buildahimage/upstream + FLAVOR: upstream - env: - CTXDIR: contrib/buildahimage/testing + FLAVOR: testing - env: - CTXDIR: contrib/buildahimage/stable + FLAVOR: stable env: DISTRO_NV: "${FEDORA_NAME}" # Required for repo cache extraction BUILDAH_USERNAME: ENCRYPTED[70e1d4f026cba5d82fc067944baab10f7c71c64bb6b75fce4eeb5c106694b3bbc8e08f8a1b926d6e03e85cf4e21833bb] @@ -350,7 +350,7 @@ image_build_task: &image-build CONTAINERS_PASSWORD: ENCRYPTED[886cf4cc126e50b2fd7f2792235a22bb79e4b81db43f803a6214a38d3fd6c04cd4e64570b562cb32b04e5fbc435404b6] main_script: - source /etc/automation_environment - - main.sh $CIRRUS_REPO_CLONE_URL $CTXDIR + - main.sh $CIRRUS_REPO_CLONE_URL contrib/buildahimage $FLAVOR test_image_build_task: diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md index 32175538..9c35c0b2 100644 --- a/vendor/github.com/containers/buildah/CHANGELOG.md +++ b/vendor/github.com/containers/buildah/CHANGELOG.md @@ -2,6 +2,132 @@ # Changelog +## v1.29.0 (2023-01-25) + + tests: improve build-with-network-test + Bump c/storagev1.45.3, c/imagev5.24.0, c/commonv0.51.0 + build(deps): bump github.com/onsi/gomega from 1.25.0 to 1.26.0 + Flake 3710 has been closed. Reenable the test. + [CI:DOCS] Fix two diversity issues in a tutorial + build(deps): bump github.com/fsouza/go-dockerclient from 1.9.2 to 1.9.3 + vendor in latests containers/(storage, common, image) + fix bud-multiple-platform-with-base-as-default-arg flake + stage_executor: while mounting stages use freshly built stage + build(deps): bump github.com/fsouza/go-dockerclient from 1.9.0 to 1.9.2 + build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.25.0 + vendor in latests containers/(storage, common, image, ocicyrpt) + [Itests: change the runtime-flag test for crun + [CI:DOCS] README: drop sudo + Fix multi-arch manifest-list build timeouts + Cirrus: Update VM Images + bud: Consolidate multiple synthetic LABEL instructions + build, secret: allow realtive mountpoints wrt to work dir + fixed squash documentation + build(deps): bump github.com/containerd/containerd from 1.6.14 to 1.6.15 + Correct minor comment + Vendor in latest containers/(common, image, storage) + system tests: remove unhelpful assertions + buildah: add prune command and expose CleanCacheMount API + vendor: bump c/storage to a747b27 + Add support for --group-add to buildah from + build(deps): bump actions/stale from 6 to 7 + Add documentation for buildah build --pull=missing + build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14 + build(deps): bump github.com/docker/docker + parse: default ignorefile must not point to symlink outside context + buildah: wrap network setup errors + build, mount: allow realtive mountpoints wrt to work dir + Update to F37 CI VM Images, re-enable prior-fedora + Update vendor or containers/(image, storage, common) + build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 + Update contact information + build(deps): bump golang.org/x/term from 0.2.0 to 0.3.0 + Replace io/ioutil calls with os calls + [skip-ci] GHA/Cirrus-cron: Fix execution order + Vendor in containers/common + build(deps): bump golang.org/x/sys from 0.2.0 to 0.3.0 + remote-cache: support multiple sources and destinations + Update c/storage after https://github.com/containers/storage/pull/1436 + util.SortMounts(): make the returned order more stable + version: Bump to 1.29.0-dev + [CI:BUILD] Cirrus: Migrate OSX task to M1 + Update vendor of containers/(common, storage, image) + mount=type=cache: seperate cache parent on host for each user + Fix installation instructions for Gentoo Linux + build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10 + GHA: Reuse both cirrus rerun and check workflows + Vendor in latest containers/(common,image,storage) + build(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1 + copier.Put(): clear up os/syscall mode bit confusion + build(deps): bump golang.org/x/sys from 0.1.0 to 0.2.0 + Use TypeBind consistently to name bind/nullfs mounts + Add no-new-privileges flag + Update vendor of containers/(common, image, storage) + imagebuildah:build with --all-platforms must honor args for base images + codespell code + Expand args and env when using --all-platforms + build(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0 + GHA: Simplify Cirrus-Cron check slightly + Stop using ubi8 + remove unnecessary (hence misleading) rmi + chroot: fix mounting of ro bind mounts + executor: honor default ARG value while eval base name + userns: add arbitrary steps/stage to --userns=auto test + Don't set allow.mount in the vnet jail on Freebsd + copier: Preserve file flags when copying archives on FreeBSD + Remove quiet flag, so that it works in podman-remote + test: fix preserve rootfs with --mount for podman-remote + test: fix prune logic for cache-from after adding content summary + vendor in latest containers/(storage, common, image) + Fix RUN --mount=type=bind,from= not preserving rootfs of stage + Define and use a safe, reliable test image + Fix word missing in Container Tools Guide + Makefile: Use $(MAKE) to start sub-makes in install.tools + imagebuildah: pull cache from remote repo after adding content summary + Makefile: Fix install on FreeBSD + Ensure the cache volume locks are unlocked on all paths + Vendor in latest containers/(common,storage) + Simplify the interface of GetCacheMount and getCacheMount + Fix cache locks with multiple mounts + Remove calls to Lockfile.Locked() + Maintain cache mount locks as lock objects instead of paths + test: cleaning cache must not clean lockfiles + run: honor lockfiles for multiple --mount instruction + mount,cache: lockfiles must not be part of users cache content + Update vendor containers/(common,image,storage) + [CI:BUILD] copr: buildah rpm should depend on containers-common-extra + pr-should-include-tests: allow specfile, golangci + build(deps): bump dawidd6/action-send-mail from 3.7.0 to 3.7.1 + build(deps): bump github.com/docker/docker + build(deps): bump github.com/fsouza/go-dockerclient from 1.8.3 to 1.9.0 + Update vendor containers/(common,image,storage) + build(deps): bump actions/upload-artifact from 2 to 3 + build(deps): bump actions/checkout from 2 to 3 + build(deps): bump actions/stale from 1 to 6 + build(deps): bump dawidd6/action-send-mail from 2.2.2 to 3.7.0 + build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0 + sshagent: LockOSThread before setting SocketLabel + Update tests for error message changes + Update c/image after https://github.com/containers/image/pull/1299 + Fix ident for dependabot gha block + build(deps): bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6 + Fix man pages to match latest cobra settings + build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0 + build(deps): bump github.com/onsi/gomega from 1.20.2 to 1.22.1 + test: retrofit 'bud with undefined build arg directory' + imagebuildah: warnOnUnsetBuildArgs while processing stages from executor + Update contrib/buildahimage/Containerfile + Cirrus CI add flavor parameter + Correction - `FLAVOR` not `FLAVOUR` + Changed build argument from `RELEASE` to `FLAVOUR` + Combine buildahimage Containerfiles + bud.bats refactoring: $TEST_SCRATCH_DIR, part 2 of 2 + bud.bats refactoring: $TEST_SCRATCH_DIR, part 1 of 2 + System test cleanup: document, clarify, fix + test: removing unneeded/expensive COPY + test: warning behaviour for unset/set TARGETOS,TARGETARCH,TARGETPLATFORM + Bump to v1.28.1-dev + ## v1.28.0 (2022-09-30) Update vendor containers/(common,image) diff --git a/vendor/github.com/containers/buildah/CONTRIBUTING.md b/vendor/github.com/containers/buildah/CONTRIBUTING.md index 80b812e3..1104d332 100644 --- a/vendor/github.com/containers/buildah/CONTRIBUTING.md +++ b/vendor/github.com/containers/buildah/CONTRIBUTING.md @@ -143,22 +143,24 @@ is available, showing all PRs awaiting review and approval. ## Communications For general questions or discussions, please use the -IRC group on `irc.freenode.net` called `buildah` -that has been setup. +IRC channel `#podman` on `irc.libera.chat`. If you are unfamiliar with IRC you can start a web client at https://web.libera.chat/#podman. -### For discussions around issues/bugs and features: - -#### Buildah Mailing List +Alternatively, [\[matrix\]](https://matrix.org) can be used to access the same channel via federation at https://matrix.to/#/#podman:chat.fedoraproject.org. -You can join the Buildah mailing list by sending an email to `buildah-join@lists.buildah.io` with the word `subscribe` in the subject. You can also go to this [page](https://lists.podman.io/admin/lists/buildah.lists.buildah.io/), then scroll down to the bottom of the page and enter your email and optionally name, then click on the "Subscribe" button. +### For discussions around issues/bugs and features: #### GitHub -You can also use the github +You can also use GitHub [issues](https://github.com/containers/buildah/issues) and [PRs](https://github.com/containers/buildah/pulls) tracking system. +#### Buildah Mailing List + + +You can join the Buildah mailing list by sending an email to `buildah-join@lists.buildah.io` with the word `subscribe` in the subject. You can also go to this [page](https://lists.podman.io/admin/lists/buildah.lists.buildah.io/), then scroll down to the bottom of the page and enter your email and optionally name, then click on the "Subscribe" button. + ## Becoming a Maintainer To become a maintainer you must first be nominated by an existing maintainer. @@ -172,3 +174,4 @@ inactive for a long period of time or are viewed as disruptive to the community. The current list of maintainers can be found in the [MAINTAINERS](MAINTAINERS) file. + diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index 8b6bb8f4..729456d9 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -123,7 +123,7 @@ validate: install.tools .PHONY: install.tools install.tools: - make -C tests/tools + $(MAKE) -C tests/tools .PHONY: runc runc: gopath @@ -146,7 +146,8 @@ install.cni.sudo: gopath .PHONY: install install: - install -D -m0755 bin/buildah $(DESTDIR)/$(BINDIR)/buildah + install -d -m 755 $(DESTDIR)/$(BINDIR) + install -m 755 bin/buildah $(DESTDIR)/$(BINDIR)/buildah $(MAKE) -C docs install .PHONY: uninstall @@ -157,7 +158,8 @@ uninstall: .PHONY: install.completions install.completions: - install -m 644 -D contrib/completions/bash/buildah $(DESTDIR)/$(BASHINSTALLDIR)/buildah + install -m 755 -d $(DESTDIR)/$(BASHINSTALLDIR) + install -m 644 contrib/completions/bash/buildah $(DESTDIR)/$(BASHINSTALLDIR)/buildah .PHONY: install.runc install.runc: diff --git a/vendor/github.com/containers/buildah/README.md b/vendor/github.com/containers/buildah/README.md index 512d3f87..ded4870b 100644 --- a/vendor/github.com/containers/buildah/README.md +++ b/vendor/github.com/containers/buildah/README.md @@ -99,7 +99,7 @@ buildah commit "$ctr1" "${2:-$USER/lighttpd}" EOF $ chmod +x lighttpd.sh -$ sudo ./lighttpd.sh +$ ./lighttpd.sh ``` ## Commands diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go index 987313e1..1bb6c850 100644 --- a/vendor/github.com/containers/buildah/add.go +++ b/vendor/github.com/containers/buildah/add.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -115,7 +114,7 @@ func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, if size < 0 { // Create a temporary file and copy the content to it, so that // we can figure out how much content there is. - f, err := ioutil.TempFile(mountpoint, "download") + f, err := os.CreateTemp(mountpoint, "download") if err != nil { return fmt.Errorf("creating temporary file to hold %q: %w", src, err) } diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 3802a727..71857384 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "path/filepath" "sort" @@ -159,6 +158,10 @@ type Builder struct { // NetworkInterface is the libnetwork network interface used to setup CNI or netavark networks. NetworkInterface nettypes.ContainerNetwork `json:"-"` + // GroupAdd is a list of groups to add to the primary process within + // the container. 'keep-groups' allows container processes to use + // supplementary groups. + GroupAdd []string // ID mapping options to use when running processes in the container with non-host user namespaces. IDMappingOptions define.IDMappingOptions // Capabilities is a list of capabilities to use when running commands in the container. @@ -191,6 +194,7 @@ type BuilderInfo struct { FromImage string FromImageID string FromImageDigest string + GroupAdd []string Config string Manifest string Container string @@ -230,6 +234,7 @@ func GetBuildInfo(b *Builder) BuilderInfo { Manifest: string(b.Manifest), Container: b.Container, ContainerID: b.ContainerID, + GroupAdd: b.GroupAdd, MountPoint: b.MountPoint, ProcessLabel: b.ProcessLabel, MountLabel: b.MountLabel, @@ -278,6 +283,7 @@ type BuilderOptions struct { // to store copies of layer blobs that we pull down, if any. It should // already exist. BlobDirectory string + GroupAdd []string // Logger is the logrus logger to write log messages with Logger *logrus.Logger `json:"-"` // Mount signals to NewBuilder() that the container should be mounted @@ -402,7 +408,7 @@ func OpenBuilder(store storage.Store, container string) (*Builder, error) { if err != nil { return nil, err } - buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile)) + buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile)) if err != nil { return nil, err } @@ -444,7 +450,7 @@ func OpenBuilderByPath(store storage.Store, path string) (*Builder, error) { if err != nil { return nil, err } - buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile)) + buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile)) if err != nil { if errors.Is(err, os.ErrNotExist) { logrus.Debugf("error reading %q: %v, ignoring container %q", filepath.Join(cdir, stateFile), err, container.ID) @@ -481,7 +487,7 @@ func OpenAllBuilders(store storage.Store) (builders []*Builder, err error) { if err != nil { return nil, err } - buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile)) + buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile)) if err != nil { if errors.Is(err, os.ErrNotExist) { logrus.Debugf("%v, ignoring container %q", err, container.ID) diff --git a/vendor/github.com/containers/buildah/buildah.spec.rpkg b/vendor/github.com/containers/buildah/buildah.spec.rpkg index 8336ab26..1b03855b 100644 --- a/vendor/github.com/containers/buildah/buildah.spec.rpkg +++ b/vendor/github.com/containers/buildah/buildah.spec.rpkg @@ -59,7 +59,7 @@ BuildRequires: btrfs-progs-devel %if 0%{?fedora} <= 35 Requires: containers-common >= 4:1-39 %else -Requires: containers-common >= 4:1-46 +Requires: containers-common-extra %endif %if 0%{?rhel} BuildRequires: libseccomp-devel diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt index 1d066f06..61fc1698 100644 --- a/vendor/github.com/containers/buildah/changelog.txt +++ b/vendor/github.com/containers/buildah/changelog.txt @@ -1,3 +1,128 @@ +- Changelog for v1.29.0 (2023-01-25) + * tests: improve build-with-network-test + * Bump c/storagev1.45.3, c/imagev5.24.0, c/commonv0.51.0 + * build(deps): bump github.com/onsi/gomega from 1.25.0 to 1.26.0 + * Flake 3710 has been closed. Reenable the test. + * [CI:DOCS] Fix two diversity issues in a tutorial + * build(deps): bump github.com/fsouza/go-dockerclient from 1.9.2 to 1.9.3 + * vendor in latests containers/(storage, common, image) + * fix bud-multiple-platform-with-base-as-default-arg flake + * stage_executor: while mounting stages use freshly built stage + * build(deps): bump github.com/fsouza/go-dockerclient from 1.9.0 to 1.9.2 + * build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.25.0 + * vendor in latests containers/(storage, common, image, ocicyrpt) + * [Itests: change the runtime-flag test for crun + * [CI:DOCS] README: drop sudo + * Fix multi-arch manifest-list build timeouts + * Cirrus: Update VM Images + * bud: Consolidate multiple synthetic LABEL instructions + * build, secret: allow realtive mountpoints wrt to work dir + * fixed squash documentation + * build(deps): bump github.com/containerd/containerd from 1.6.14 to 1.6.15 + * Correct minor comment + * Vendor in latest containers/(common, image, storage) + * system tests: remove unhelpful assertions + * buildah: add prune command and expose CleanCacheMount API + * vendor: bump c/storage to a747b27 + * Add support for --group-add to buildah from + * build(deps): bump actions/stale from 6 to 7 + * Add documentation for buildah build --pull=missing + * build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14 + * build(deps): bump github.com/docker/docker + * parse: default ignorefile must not point to symlink outside context + * buildah: wrap network setup errors + * build, mount: allow realtive mountpoints wrt to work dir + * Update to F37 CI VM Images, re-enable prior-fedora + * Update vendor or containers/(image, storage, common) + * build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 + * Update contact information + * build(deps): bump golang.org/x/term from 0.2.0 to 0.3.0 + * Replace io/ioutil calls with os calls + * [skip-ci] GHA/Cirrus-cron: Fix execution order + * Vendor in containers/common + * build(deps): bump golang.org/x/sys from 0.2.0 to 0.3.0 + * remote-cache: support multiple sources and destinations + * Update c/storage after https://github.com/containers/storage/pull/1436 + * util.SortMounts(): make the returned order more stable + * version: Bump to 1.29.0-dev + * [CI:BUILD] Cirrus: Migrate OSX task to M1 + * Update vendor of containers/(common, storage, image) + * mount=type=cache: seperate cache parent on host for each user + * Fix installation instructions for Gentoo Linux + * build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10 + * GHA: Reuse both cirrus rerun and check workflows + * Vendor in latest containers/(common,image,storage) + * build(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1 + * copier.Put(): clear up os/syscall mode bit confusion + * build(deps): bump golang.org/x/sys from 0.1.0 to 0.2.0 + * Use TypeBind consistently to name bind/nullfs mounts + * Add no-new-privileges flag + * Update vendor of containers/(common, image, storage) + * imagebuildah:build with --all-platforms must honor args for base images + * codespell code + * Expand args and env when using --all-platforms + * build(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0 + * GHA: Simplify Cirrus-Cron check slightly + * Stop using ubi8 + * remove unnecessary (hence misleading) rmi + * chroot: fix mounting of ro bind mounts + * executor: honor default ARG value while eval base name + * userns: add arbitrary steps/stage to --userns=auto test + * Don't set allow.mount in the vnet jail on Freebsd + * copier: Preserve file flags when copying archives on FreeBSD + * Remove quiet flag, so that it works in podman-remote + * test: fix preserve rootfs with --mount for podman-remote + * test: fix prune logic for cache-from after adding content summary + * vendor in latest containers/(storage, common, image) + * Fix RUN --mount=type=bind,from= not preserving rootfs of stage + * Define and use a safe, reliable test image + * Fix word missing in Container Tools Guide + * Makefile: Use $(MAKE) to start sub-makes in install.tools + * imagebuildah: pull cache from remote repo after adding content summary + * Makefile: Fix install on FreeBSD + * Ensure the cache volume locks are unlocked on all paths + * Vendor in latest containers/(common,storage) + * Simplify the interface of GetCacheMount and getCacheMount + * Fix cache locks with multiple mounts + * Remove calls to Lockfile.Locked() + * Maintain cache mount locks as lock objects instead of paths + * test: cleaning cache must not clean lockfiles + * run: honor lockfiles for multiple --mount instruction + * mount,cache: lockfiles must not be part of users cache content + * Update vendor containers/(common,image,storage) + * [CI:BUILD] copr: buildah rpm should depend on containers-common-extra + * pr-should-include-tests: allow specfile, golangci + * build(deps): bump dawidd6/action-send-mail from 3.7.0 to 3.7.1 + * build(deps): bump github.com/docker/docker + * build(deps): bump github.com/fsouza/go-dockerclient from 1.8.3 to 1.9.0 + * Update vendor containers/(common,image,storage) + * build(deps): bump actions/upload-artifact from 2 to 3 + * build(deps): bump actions/checkout from 2 to 3 + * build(deps): bump actions/stale from 1 to 6 + * build(deps): bump dawidd6/action-send-mail from 2.2.2 to 3.7.0 + * build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0 + * sshagent: LockOSThread before setting SocketLabel + * Update tests for error message changes + * Update c/image after https://github.com/containers/image/pull/1299 + * Fix ident for dependabot gha block + * build(deps): bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6 + * Fix man pages to match latest cobra settings + * build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0 + * build(deps): bump github.com/onsi/gomega from 1.20.2 to 1.22.1 + * test: retrofit 'bud with undefined build arg directory' + * imagebuildah: warnOnUnsetBuildArgs while processing stages from executor + * Update contrib/buildahimage/Containerfile + * Cirrus CI add flavor parameter + * Correction - `FLAVOR` not `FLAVOUR` + * Changed build argument from `RELEASE` to `FLAVOUR` + * Combine buildahimage Containerfiles + * bud.bats refactoring: $TEST_SCRATCH_DIR, part 2 of 2 + * bud.bats refactoring: $TEST_SCRATCH_DIR, part 1 of 2 + * System test cleanup: document, clarify, fix + * test: removing unneeded/expensive COPY + * test: warning behaviour for unset/set TARGETOS,TARGETARCH,TARGETPLATFORM + * Bump to v1.28.1-dev + - Changelog for v1.28.0 (2022-09-30) * Update vendor containers/(common,image) * [CI:DOCS] Add quay-description update reminder diff --git a/vendor/github.com/containers/buildah/chroot/run_common.go b/vendor/github.com/containers/buildah/chroot/run_common.go index 040b6828..e93af1e6 100644 --- a/vendor/github.com/containers/buildah/chroot/run_common.go +++ b/vendor/github.com/containers/buildah/chroot/run_common.go @@ -8,7 +8,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "os/exec" "os/signal" @@ -690,7 +689,7 @@ func runUsingChrootExecMain() { os.Exit(1) } } else { - setgroups, _ := ioutil.ReadFile("/proc/self/setgroups") + setgroups, _ := os.ReadFile("/proc/self/setgroups") if strings.Trim(string(setgroups), "\n") != "deny" { logrus.Debugf("clearing supplemental groups") if err = syscall.Setgroups([]int{}); err != nil { diff --git a/vendor/github.com/containers/buildah/chroot/run_linux.go b/vendor/github.com/containers/buildah/chroot/run_linux.go index f2a9c903..cb116add 100644 --- a/vendor/github.com/containers/buildah/chroot/run_linux.go +++ b/vendor/github.com/containers/buildah/chroot/run_linux.go @@ -423,7 +423,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( file.Close() } } - requestFlags := bindFlags + requestFlags := uintptr(0) expectedFlags := uintptr(0) for _, option := range m.Options { switch option { @@ -457,9 +457,19 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( case "bind": // Do the bind mount. logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination)) - if err := unix.Mount(m.Source, target, "", requestFlags, ""); err != nil { + if err := unix.Mount(m.Source, target, "", bindFlags|requestFlags, ""); err != nil { return undoBinds, fmt.Errorf("bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err) } + if (requestFlags & unix.MS_RDONLY) != 0 { + if err = unix.Statfs(target, &fs); err != nil { + return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", target, err) + } + // we need to make sure these flags are maintained in the REMOUNT operation + additionalFlags := uintptr(fs.Flags) & (unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV) + if err := unix.Mount("", target, "", unix.MS_REMOUNT|unix.MS_BIND|unix.MS_RDONLY|additionalFlags, ""); err != nil { + return undoBinds, fmt.Errorf("setting flags on the bind mount %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err) + } + } logrus.Debugf("bind mounted %q to %q", m.Source, target) case "tmpfs": // Mount a tmpfs. diff --git a/vendor/github.com/containers/buildah/chroot/seccomp.go b/vendor/github.com/containers/buildah/chroot/seccomp.go index 714dca62..cd203411 100644 --- a/vendor/github.com/containers/buildah/chroot/seccomp.go +++ b/vendor/github.com/containers/buildah/chroot/seccomp.go @@ -5,7 +5,7 @@ package chroot import ( "fmt" - "io/ioutil" + "os" "github.com/containers/common/pkg/seccomp" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -187,7 +187,7 @@ func setupSeccomp(spec *specs.Spec, seccompProfilePath string) error { } spec.Linux.Seccomp = seccompConfig default: - seccompProfile, err := ioutil.ReadFile(seccompProfilePath) + seccompProfile, err := os.ReadFile(seccompProfilePath) if err != nil { return fmt.Errorf("opening seccomp profile failed: %w", err) } diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go index e53fbfe8..21ec6ea5 100644 --- a/vendor/github.com/containers/buildah/commit.go +++ b/vendor/github.com/containers/buildah/commit.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "strings" "time" @@ -392,7 +391,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options dest = dest2 } if options.IIDFile != "" { - if err = ioutil.WriteFile(options.IIDFile, []byte("sha256:"+img.ID), 0644); err != nil { + if err = os.WriteFile(options.IIDFile, []byte("sha256:"+img.ID), 0644); err != nil { return imgID, nil, "", err } } diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index 6d4c81c6..7bfbf9d1 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -8,7 +8,6 @@ import ( "fmt" "io" "io/fs" - "io/ioutil" "net" "os" "os/user" @@ -22,6 +21,7 @@ import ( "github.com/containers/buildah/util" "github.com/containers/image/v5/pkg/compression" + "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/fileutils" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/reexec" @@ -572,7 +572,7 @@ func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req reques bulkReader = bytes.NewReader([]byte{}) } if bulkWriter == nil { - bulkWriter = ioutil.Discard + bulkWriter = io.Discard } cmd := reexec.Command(copierCommand) stdinRead, stdinWrite, err := os.Pipe() @@ -1462,6 +1462,10 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str hdr.Mode = int64(*options.ChmodFiles) } } + // read fflags, if any + if err := archive.ReadFileFlagsToTarHeader(contentPath, hdr); err != nil { + return fmt.Errorf("getting fflags: %w", err) + } var f *os.File if hdr.Typeflag == tar.TypeReg { // open the file first so that we don't write a header for it if we can't actually read it @@ -1566,15 +1570,15 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM return nil } makeDirectoryWriteable := func(directory string) error { - st, err := os.Lstat(directory) - if err != nil { - return fmt.Errorf("copier: put: error reading permissions of directory %q: %w", directory, err) - } - mode := st.Mode() & os.ModePerm if _, ok := directoryModes[directory]; !ok { + st, err := os.Lstat(directory) + if err != nil { + return fmt.Errorf("copier: put: error reading permissions of directory %q: %w", directory, err) + } + mode := st.Mode() directoryModes[directory] = mode } - if err = os.Chmod(directory, 0o700); err != nil { + if err := os.Chmod(directory, 0o700); err != nil { return fmt.Errorf("copier: put: error making directory %q writable: %w", directory, err) } return nil @@ -1862,16 +1866,21 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM // set other bits that might have been reset by chown() if hdr.Typeflag != tar.TypeSymlink { if hdr.Mode&cISUID == cISUID { - mode |= syscall.S_ISUID + mode |= os.ModeSetuid } if hdr.Mode&cISGID == cISGID { - mode |= syscall.S_ISGID + mode |= os.ModeSetgid } if hdr.Mode&cISVTX == cISVTX { - mode |= syscall.S_ISVTX + mode |= os.ModeSticky + } + if hdr.Typeflag == tar.TypeDir { + // if/when we do the final setting of permissions on this + // directory, make sure to incorporate these bits, too + directoryModes[path] = mode } - if err = syscall.Chmod(path, uint32(mode)); err != nil { - return fmt.Errorf("setting additional permissions on %q to 0%o: %w", path, mode, err) + if err = os.Chmod(path, mode); err != nil { + return fmt.Errorf("copier: put: setting additional permissions on %q to 0%o: %w", path, mode, err) } } // set xattrs, including some that might have been reset by chown() @@ -1889,6 +1898,10 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM if err = lutimes(hdr.Typeflag == tar.TypeSymlink, path, hdr.AccessTime, hdr.ModTime); err != nil { return fmt.Errorf("setting access and modify timestamps on %q to %s and %s: %w", path, hdr.AccessTime, hdr.ModTime, err) } + // set fflags if supported + if err := archive.WriteFileFlagsFromTarHeader(path, hdr); err != nil { + return fmt.Errorf("copier: put: error setting fflags on %q: %w", path, err) + } nextHeader: hdr, err = tr.Next() } diff --git a/vendor/github.com/containers/buildah/define/build.go b/vendor/github.com/containers/buildah/define/build.go index 69b2dc9b..42c8fd72 100644 --- a/vendor/github.com/containers/buildah/define/build.go +++ b/vendor/github.com/containers/buildah/define/build.go @@ -67,6 +67,8 @@ type CommonBuildOptions struct { // NoHosts tells the builder not to create /etc/hosts content when running // containers. NoHosts bool + // NoNewPrivileges removes the ability for the container to gain privileges + NoNewPrivileges bool // OmitTimestamp forces epoch 0 as created timestamp to allow for // deterministic, content-addressable builds. OmitTimestamp bool @@ -139,10 +141,10 @@ type BuildOptions struct { TransientMounts []string // CacheFrom specifies any remote repository which can be treated as // potential cache source. - CacheFrom reference.Named + CacheFrom []reference.Named // CacheTo specifies any remote repository which can be treated as // potential cache destination. - CacheTo reference.Named + CacheTo []reference.Named // CacheTTL specifies duration, if specified using `--cache-ttl` then // cache intermediate images under this duration will be considered as // valid cache sources and images outside this duration will be ignored. @@ -296,6 +298,10 @@ type BuildOptions struct { // From is the image name to use to replace the value specified in the first // FROM instruction in the Containerfile From string + // GroupAdd is a list of groups to add to the primary process within + // the container. 'keep-groups' allows container processes to use + // supplementary groups. + GroupAdd []string // Platforms is the list of parsed OS/Arch/Variant triples that we want // to build the image for. If this slice has items in it, the OS and // Architecture fields above are ignored. diff --git a/vendor/github.com/containers/buildah/define/mount_unsupported.go b/vendor/github.com/containers/buildah/define/mount_unsupported.go new file mode 100644 index 00000000..fe09bfcc --- /dev/null +++ b/vendor/github.com/containers/buildah/define/mount_unsupported.go @@ -0,0 +1,17 @@ +//go:build darwin || windows +// +build darwin windows + +package define + +const ( + // TypeBind is the type for mounting host dir + TypeBind = "bind" + + // TempDir is the default for storing temporary files + TempDir = "/var/tmp" +) + +var ( + // Mount potions for bind + BindOptions = []string{""} +) diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go index ae088a43..13256742 100644 --- a/vendor/github.com/containers/buildah/define/types.go +++ b/vendor/github.com/containers/buildah/define/types.go @@ -5,7 +5,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "io" "net/http" urlpkg "net/url" "os" @@ -30,7 +30,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.28.0" + Version = "1.29.0" // DefaultRuntime if containers.conf fails. DefaultRuntime = "runc" @@ -121,7 +121,7 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err url != "-" { return "", "", nil } - name, err = ioutil.TempDir(dir, prefix) + name, err = os.MkdirTemp(dir, prefix) if err != nil { return "", "", fmt.Errorf("creating temporary directory for %q: %w", url, err) } @@ -255,7 +255,7 @@ func downloadToDirectory(url, dir string) error { return err } defer resp1.Body.Close() - body, err := ioutil.ReadAll(resp1.Body) + body, err := io.ReadAll(resp1.Body) if err != nil { return err } @@ -271,7 +271,7 @@ func downloadToDirectory(url, dir string) error { func stdinToDirectory(dir string) error { logrus.Debugf("extracting stdin to %q", dir) r := bufio.NewReader(os.Stdin) - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return fmt.Errorf("failed to read from stdin: %w", err) } diff --git a/vendor/github.com/containers/buildah/image.go b/vendor/github.com/containers/buildah/image.go index cc56ff2d..01c1784d 100644 --- a/vendor/github.com/containers/buildah/image.go +++ b/vendor/github.com/containers/buildah/image.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -309,7 +308,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System logrus.Debugf("layer list: %q", layers) // Make a temporary directory to hold blobs. - path, err := ioutil.TempDir(os.TempDir(), define.Package) + path, err := os.MkdirTemp(os.TempDir(), define.Package) if err != nil { return nil, fmt.Errorf("creating temporary directory to hold layer blobs: %w", err) } diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index 09267f96..ca45501c 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "os" "os/exec" @@ -34,7 +33,6 @@ import ( v1 "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/openshift/imagebuilder" - "github.com/openshift/imagebuilder/dockerfile/parser" "github.com/sirupsen/logrus" "golang.org/x/sync/semaphore" ) @@ -154,7 +152,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B if err != nil { return "", nil, err } - data = ioutil.NopCloser(pData) + data = io.NopCloser(pData) } dockerfiles = append(dockerfiles, data) @@ -412,8 +410,6 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr } } - warnOnUnsetBuildArgs(logger, mainNode, options.Args) - for i, d := range dockerfilecontents[1:] { additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) if err != nil { @@ -423,31 +419,32 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr mainNode.Children = append(mainNode.Children, additionalNode.Children...) } - // Check if any modifications done to labels - // add them to node-layer so it becomes regular - // layer. - // Reason: Docker adds label modification as - // last step which can be processed as regular - // steps and if no modification is done to layers - // its easier to re-use cached layers. + // Check if any labels were passed in via the API, and add a final line + // to the Dockerfile that would provide the same result. + // Reason: Docker adds label modification as a last step which can be + // processed like regular steps, and if no modification is done to + // layers, its easier to re-use cached layers. if len(options.Labels) > 0 { - for _, labelSpec := range options.Labels { + var labelLine string + labels := append([]string{}, options.Labels...) + for _, labelSpec := range labels { label := strings.SplitN(labelSpec, "=", 2) - labelLine := "" key := label[0] value := "" if len(label) > 1 { value = label[1] } - // check from only empty key since docker supports empty value + // check only for an empty key since docker allows empty values if key != "" { - labelLine = fmt.Sprintf("LABEL %q=%q\n", key, value) - additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader(labelLine)) - if err != nil { - return "", nil, fmt.Errorf("while adding additional LABEL steps: %w", err) - } - mainNode.Children = append(mainNode.Children, additionalNode.Children...) + labelLine += fmt.Sprintf(" %q=%q", key, value) + } + } + if len(labelLine) > 0 { + additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader("LABEL" + labelLine + "\n")) + if err != nil { + return "", nil, fmt.Errorf("while adding additional LABEL step: %w", err) } + mainNode.Children = append(mainNode.Children, additionalNode.Children...) } } @@ -475,31 +472,6 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr return exec.Build(ctx, stages) } -func warnOnUnsetBuildArgs(logger *logrus.Logger, node *parser.Node, args map[string]string) { - argFound := make(map[string]bool) - for _, child := range node.Children { - switch strings.ToUpper(child.Value) { - case "ARG": - argName := child.Next.Value - if strings.Contains(argName, "=") { - res := strings.Split(argName, "=") - if res[1] != "" { - argFound[res[0]] = true - } - } - argHasValue := true - if !strings.Contains(argName, "=") { - argHasValue = argFound[argName] - } - if _, ok := args[argName]; !argHasValue && !ok { - logger.Warnf("missing %q build argument. Try adding %q to the command line", argName, fmt.Sprintf("--build-arg %s=", argName)) - } - default: - continue - } - } -} - // preprocessContainerfileContents runs CPP(1) in preprocess-only mode on the input // dockerfile content and will use ctxDir as the base include path. func preprocessContainerfileContents(logger *logrus.Logger, containerfile string, r io.Reader, ctxDir string, cppFlags []string) (stdout io.Reader, err error) { @@ -724,11 +696,17 @@ func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from stri } base := child.Next.Value if base != "scratch" && !nicknames[base] { - // TODO: this didn't undergo variable and arg - // expansion, so if the AS clause in another - // FROM instruction uses argument values, - // we might not record the right value here. - baseImages = append(baseImages, base) + headingArgs := argsMapToSlice(stage.Builder.HeadingArgs) + userArgs := argsMapToSlice(stage.Builder.Args) + // append heading args so if --build-arg key=value is not + // specified but default value is set in Containerfile + // via `ARG key=value` so default value can be used. + userArgs = append(headingArgs, userArgs...) + baseWithArg, err := imagebuilder.ProcessWord(base, userArgs) + if err != nil { + return nil, fmt.Errorf("while replacing arg variables with values for format %q: %w", base, err) + } + baseImages = append(baseImages, baseWithArg) } } } diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go index d30b1356..5c9f1ef8 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "sort" "strconv" @@ -58,8 +57,8 @@ var builtinAllowedBuildArgs = map[string]bool{ // interface. It coordinates the entire build by using one or more // StageExecutors to handle each stage of the build. type Executor struct { - cacheFrom reference.Named - cacheTo reference.Named + cacheFrom []reference.Named + cacheTo []reference.Named cacheTTL time.Duration containerSuffix string logger *logrus.Logger @@ -110,7 +109,9 @@ type Executor struct { rootfsMap map[string]bool // Holds the names of every stage whose rootfs is referenced in a COPY or ADD instruction. blobDirectory string excludes []string + groupAdd []string ignoreFile string + args map[string]string unusedArgs map[string]struct{} capabilities []string devices define.ContainerDevices @@ -199,7 +200,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o writer := options.ReportWriter if options.Quiet { - writer = ioutil.Discard + writer = io.Discard } var rusageLogFile io.Writer @@ -216,6 +217,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o } exec := Executor{ + args: options.Args, cacheFrom: options.CacheFrom, cacheTo: options.CacheTo, cacheTTL: options.CacheTTL, @@ -225,6 +227,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o store: store, contextDir: options.ContextDirectory, excludes: excludes, + groupAdd: options.GroupAdd, ignoreFile: options.IgnoreFile, pullPolicy: options.PullPolicy, registry: options.Registry, @@ -453,6 +456,10 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE ib := stage.Builder node := stage.Node base, err := ib.From(node) + if err != nil { + logrus.Debugf("buildStage(node.Children=%#v)", node.Children) + return "", nil, err + } // If this is the last stage, then the image that we produce at // its end should be given the desired output name. @@ -461,9 +468,30 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE output = b.output } - if err != nil { - logrus.Debugf("buildStage(node.Children=%#v)", node.Children) - return "", nil, err + // If this stage is starting out with environment variables that were + // passed in via our API, we should include them in the history, since + // they affect RUN instructions in this stage. + if len(b.envs) > 0 { + var envLine string + for _, envSpec := range b.envs { + env := strings.SplitN(envSpec, "=", 2) + key := env[0] + if len(env) > 1 { + value := env[1] + envLine += fmt.Sprintf(" %q=%q", key, value) + } else { + value := os.Getenv(key) + envLine += fmt.Sprintf(" %q=%q", key, value) + } + } + if len(envLine) > 0 { + additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader("ENV" + envLine + "\n")) + if err != nil { + return "", nil, fmt.Errorf("while adding additional ENV step: %w", err) + } + // make this the first instruction in the stage after its FROM instruction + stage.Node.Children = append(additionalNode.Children, stage.Node.Children...) + } } b.stagesLock.Lock() @@ -537,6 +565,45 @@ func markDependencyStagesForTarget(dependencyMap map[string]*stageDependencyInfo } } +func (b *Executor) warnOnUnsetBuildArgs(stages imagebuilder.Stages, dependencyMap map[string]*stageDependencyInfo, args map[string]string) { + argFound := make(map[string]bool) + for _, stage := range stages { + node := stage.Node // first line + for node != nil { // each line + for _, child := range node.Children { + switch strings.ToUpper(child.Value) { + case "ARG": + argName := child.Next.Value + if strings.Contains(argName, "=") { + res := strings.Split(argName, "=") + if res[1] != "" { + argFound[res[0]] = true + } + } + argHasValue := true + if !strings.Contains(argName, "=") { + argHasValue = argFound[argName] + } + if _, ok := args[argName]; !argHasValue && !ok { + shouldWarn := true + if stageDependencyInfo, ok := dependencyMap[stage.Name]; ok { + if !stageDependencyInfo.NeededByTarget && b.skipUnusedStages != types.OptionalBoolFalse { + shouldWarn = false + } + } + if shouldWarn { + b.logger.Warnf("missing %q build argument. Try adding %q to the command line", argName, fmt.Sprintf("--build-arg %s=", argName)) + } + } + default: + continue + } + } + node = node.Next + } + } +} + // Build takes care of the details of running Prepare/Execute/Commit/Delete // over each of the one or more parsed Dockerfiles and stages. func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) { @@ -548,7 +615,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image stdout := b.out if b.quiet { - b.out = ioutil.Discard + b.out = io.Discard } cleanup := func() error { @@ -649,7 +716,12 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image base = child.Next.Value } } + headingArgs := argsMapToSlice(stage.Builder.HeadingArgs) userArgs := argsMapToSlice(stage.Builder.Args) + // append heading args so if --build-arg key=value is not + // specified but default value is set in Containerfile + // via `ARG key=value` so default value can be used. + userArgs = append(headingArgs, userArgs...) baseWithArg, err := imagebuilder.ProcessWord(base, userArgs) if err != nil { return "", nil, fmt.Errorf("while replacing arg variables with values for format %q: %w", base, err) @@ -716,6 +788,8 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image // add base to current stage's dependency tree // but also confirm if this is not in additional context. if _, ok := b.additionalBuildContexts[mountFrom]; !ok { + // Treat from as a rootfs we need to preserve + b.rootfsMap[mountFrom] = true if _, ok := dependencyMap[mountFrom]; ok { // update current stage's dependency info currentStageInfo := dependencyMap[stage.Name] @@ -741,6 +815,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image markDependencyStagesForTarget(dependencyMap, stage.Name) } } + b.warnOnUnsetBuildArgs(stages, dependencyMap, b.args) type Result struct { Index int @@ -905,7 +980,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image } logrus.Debugf("printing final image id %q", imageID) if b.iidfile != "" { - if err = ioutil.WriteFile(b.iidfile, []byte("sha256:"+imageID), 0644); err != nil { + if err = os.WriteFile(b.iidfile, []byte("sha256:"+imageID), 0644); err != nil { return imageID, ref, fmt.Errorf("failed to write image ID to file %q: %w", b.iidfile, err) } } else { diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go index 8c858ea9..03bbe6b9 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go @@ -70,6 +70,7 @@ type StageExecutor struct { output string containerIDs []string stage *imagebuilder.Stage + didExecute bool argsFromContainerfile []string } @@ -516,7 +517,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte // to `mountPoint` replaced from additional // build-context. Reason: Parser will use this // `from` to refer from stageMountPoints map later. - stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint} + stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint} break } else { // Most likely this points to path on filesystem @@ -548,7 +549,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte mountPoint = additionalBuildContext.DownloadedCache } } - stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: mountPoint} + stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: true, MountPoint: mountPoint} break } } @@ -559,14 +560,14 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte return nil, err } if otherStage, ok := s.executor.stages[from]; ok && otherStage.index < s.index { - stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: otherStage.mountPoint} + stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: otherStage.didExecute, MountPoint: otherStage.mountPoint} break } else { mountPoint, err := s.getImageRootfs(s.ctx, from) if err != nil { return nil, fmt.Errorf("%s from=%s: no stage or image found with that name", flag, from) } - stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint} + stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint} break } default: @@ -726,6 +727,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo builderOptions := buildah.BuilderOptions{ Args: ib.Args, FromImage: from, + GroupAdd: s.executor.groupAdd, PullPolicy: pullPolicy, ContainerSuffix: s.executor.containerSuffix, Registry: s.executor.registry, @@ -1147,6 +1149,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // If we're doing a single-layer build, just process the // instruction. if !s.executor.layers { + s.didExecute = true err := ib.Run(step, s, noRunsRemaining) if err != nil { logrus.Debugf("Error building at step %+v: %v", *step, err) @@ -1192,6 +1195,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, } // We're in a multi-layered build. + s.didExecute = false var ( commitName string cacheID string @@ -1203,7 +1207,36 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, canMatchCacheOnlyAfterRun bool ) - needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil) + // Only attempt to find cache if its needed, this part is needed + // so that if a step is using RUN --mount and mounts content from + // previous stages then it uses the freshly built stage instead + // of re-using the older stage from the store. + avoidLookingCache := false + var mounts []string + for _, a := range node.Flags { + arg, err := imagebuilder.ProcessWord(a, s.stage.Builder.Arguments()) + if err != nil { + return "", nil, err + } + switch { + case strings.HasPrefix(arg, "--mount="): + mount := strings.TrimPrefix(arg, "--mount=") + mounts = append(mounts, mount) + default: + continue + } + } + stageMountPoints, err := s.runStageMountPoints(mounts) + if err != nil { + return "", nil, err + } + for _, mountPoint := range stageMountPoints { + if mountPoint.DidExecute { + avoidLookingCache = true + } + } + + needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil) && !avoidLookingCache // If we have to commit for this instruction, only assign the // stage's configured output name to the last layer. @@ -1227,13 +1260,14 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // we need to call ib.Run() to correctly put the args together before // determining if a cached layer with the same build args already exists // and that is done in the if block below. - if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) { + if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) && !avoidLookingCache { // For `COPY` and `ADD`, history entries include digests computed from // the content that's copied in. We need to compute that information so that // it can be used to evaluate the cache, which means we need to go ahead // and copy the content. canMatchCacheOnlyAfterRun = (step.Command == command.Add || step.Command == command.Copy) if canMatchCacheOnlyAfterRun { + s.didExecute = true if err = ib.Run(step, s, noRunsRemaining); err != nil { logrus.Debugf("Error building at step %+v: %v", *step, err) return "", nil, fmt.Errorf("building at STEP \"%s\": %w", step.Message, err) @@ -1280,6 +1314,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // cases above, so we shouldn't do it again. if cacheID == "" && !canMatchCacheOnlyAfterRun { // Process the instruction directly. + s.didExecute = true if err = ib.Run(step, s, noRunsRemaining); err != nil { logrus.Debugf("Error building at step %+v: %v", *step, err) return "", nil, fmt.Errorf("building at STEP \"%s\": %w", step.Message, err) @@ -1297,11 +1332,29 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // Check if there's already an image based on our parent that // has the same change that we just made. - if checkForLayers { + if checkForLayers && !avoidLookingCache { cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) if err != nil { return "", nil, fmt.Errorf("checking if cached image exists from a previous build: %w", err) } + // All the best effort to find cache on localstorage have failed try pulling + // cache from remote repo if `--cache-from` was configured and cacheKey was + // generated again after adding content summary. + if cacheID == "" && s.executor.cacheFrom != nil { + // only attempt to use cache again if pulling was successful + // otherwise do nothing and attempt to run the step, err != nil + // is ignored and will be automatically logged for --log-level debug + if id, err := s.pullCache(ctx, cacheKey); id != "" && err == nil { + logCachePulled(cacheKey) + cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) + if err != nil { + return "", nil, fmt.Errorf("checking if cached image exists from a previous build: %w", err) + } + if cacheID != "" { + pulledAndUsedCacheImage = true + } + } + } } } else { // This log line is majorly here so we can verify in tests @@ -1315,6 +1368,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // still don't want to restart using the image's // configuration blob. if !s.stepRequiresLayer(step) { + s.didExecute = true err := ib.Run(step, s, noRunsRemaining) if err != nil { logrus.Debugf("Error building at step %+v: %v", *step, err) @@ -1605,17 +1659,17 @@ func (s *StageExecutor) getBuildArgsResolvedForRun() string { return strings.Join(envs, " ") } -// getBuildArgs key returns set args are key which were specified during the build process -// following function will be exclusively used by build history +// getBuildArgs key returns the set of args which were specified during the +// build process, formatted for inclusion in the build history func (s *StageExecutor) getBuildArgsKey() string { - var envs []string + var args []string for key := range s.stage.Builder.Args { if _, ok := s.stage.Builder.AllowedArgs[key]; ok { - envs = append(envs, key) + args = append(args, key) } } - sort.Strings(envs) - return strings.Join(envs, " ") + sort.Strings(args) + return strings.Join(args, " ") } // tagExistingImage adds names to an image already in the store @@ -1711,42 +1765,48 @@ func (s *StageExecutor) generateCacheKey(ctx context.Context, currNode *parser.N // cacheImageReference is internal function which generates ImageReference from Named repo sources // and a tag. -func cacheImageReference(repo reference.Named, cachekey string) (types.ImageReference, error) { - tagged, err := reference.WithTag(repo, cachekey) - if err != nil { - return nil, fmt.Errorf("failed generating tagged reference for %q: %w", repo, err) - } - dest, err := imagedocker.NewReference(tagged) - if err != nil { - return nil, fmt.Errorf("failed generating docker reference for %q: %w", tagged, err) +func cacheImageReferences(repos []reference.Named, cachekey string) ([]types.ImageReference, error) { + var result []types.ImageReference + for _, repo := range repos { + tagged, err := reference.WithTag(repo, cachekey) + if err != nil { + return nil, fmt.Errorf("failed generating tagged reference for %q: %w", repo, err) + } + dest, err := imagedocker.NewReference(tagged) + if err != nil { + return nil, fmt.Errorf("failed generating docker reference for %q: %w", tagged, err) + } + result = append(result, dest) } - return dest, nil + return result, nil } // pushCache takes the image id of intermediate image and attempts // to perform push at the remote repository with cacheKey as the tag. // Returns error if fails otherwise returns nil. func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) error { - dest, err := cacheImageReference(s.executor.cacheTo, cacheKey) + destList, err := cacheImageReferences(s.executor.cacheTo, cacheKey) if err != nil { return err } - logrus.Debugf("trying to push cache to dest: %+v from src:%+v", dest, src) - options := buildah.PushOptions{ - Compression: s.executor.compression, - SignaturePolicyPath: s.executor.signaturePolicyPath, - Store: s.executor.store, - SystemContext: s.executor.systemContext, - BlobDirectory: s.executor.blobDirectory, - SignBy: s.executor.signBy, - MaxRetries: s.executor.maxPullPushRetries, - RetryDelay: s.executor.retryPullPushDelay, - } - ref, digest, err := buildah.Push(ctx, src, dest, options) - if err != nil { - return fmt.Errorf("failed pushing cache to %q: %w", dest, err) + for _, dest := range destList { + logrus.Debugf("trying to push cache to dest: %+v from src:%+v", dest, src) + options := buildah.PushOptions{ + Compression: s.executor.compression, + SignaturePolicyPath: s.executor.signaturePolicyPath, + Store: s.executor.store, + SystemContext: s.executor.systemContext, + BlobDirectory: s.executor.blobDirectory, + SignBy: s.executor.signBy, + MaxRetries: s.executor.maxPullPushRetries, + RetryDelay: s.executor.retryPullPushDelay, + } + ref, digest, err := buildah.Push(ctx, src, dest, options) + if err != nil { + return fmt.Errorf("failed pushing cache to %q: %w", dest, err) + } + logrus.Debugf("successfully pushed cache to dest: %+v with ref:%+v and digest: %v", dest, ref, digest) } - logrus.Debugf("successfully pushed cache to dest: %+v with ref:%+v and digest: %v", dest, ref, digest) return nil } @@ -1757,29 +1817,33 @@ func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) err // image was pulled function returns image id otherwise returns empty // string "" or error if any error was encontered while pulling the cache. func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (string, error) { - src, err := cacheImageReference(s.executor.cacheFrom, cacheKey) + srcList, err := cacheImageReferences(s.executor.cacheFrom, cacheKey) if err != nil { return "", err } - logrus.Debugf("trying to pull cache from remote repo: %+v", src.DockerReference()) - options := buildah.PullOptions{ - SignaturePolicyPath: s.executor.signaturePolicyPath, - Store: s.executor.store, - SystemContext: s.executor.systemContext, - BlobDirectory: s.executor.blobDirectory, - MaxRetries: s.executor.maxPullPushRetries, - RetryDelay: s.executor.retryPullPushDelay, - AllTags: false, - ReportWriter: nil, - PullPolicy: define.PullIfNewer, - } - id, err := buildah.Pull(ctx, src.DockerReference().String(), options) - if err != nil { - logrus.Debugf("failed pulling cache from source %s: %v", src, err) - return "", fmt.Errorf("failed while pulling cache from %q: %w", src, err) + for _, src := range srcList { + logrus.Debugf("trying to pull cache from remote repo: %+v", src.DockerReference()) + options := buildah.PullOptions{ + SignaturePolicyPath: s.executor.signaturePolicyPath, + Store: s.executor.store, + SystemContext: s.executor.systemContext, + BlobDirectory: s.executor.blobDirectory, + MaxRetries: s.executor.maxPullPushRetries, + RetryDelay: s.executor.retryPullPushDelay, + AllTags: false, + ReportWriter: nil, + PullPolicy: define.PullIfNewer, + } + id, err := buildah.Pull(ctx, src.DockerReference().String(), options) + if err != nil { + logrus.Debugf("failed pulling cache from source %s: %v", src, err) + continue // failed pulling this one try next + //return "", fmt.Errorf("failed while pulling cache from %q: %w", src, err) + } + logrus.Debugf("successfully pulled cache from repo %s: %s", src, id) + return id, nil } - logrus.Debugf("successfully pulled cache from repo %s: %s", src, id) - return id, nil + return "", fmt.Errorf("failed pulling cache from all available sources %q", srcList) } // intermediateImageExists returns true if an intermediate image of currNode exists in the image store from a previous build. @@ -1906,25 +1970,6 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer spec := strings.SplitN(envSpec, "=", 2) s.builder.SetEnv(spec[0], spec[1]) } - for _, envSpec := range s.executor.envs { - env := strings.SplitN(envSpec, "=", 2) - if len(env) > 1 { - getenv := func(name string) string { - for _, envvar := range s.builder.Env() { - val := strings.SplitN(envvar, "=", 2) - if len(val) == 2 && val[0] == name { - return val[1] - } - } - logrus.Errorf("error expanding variable %q: no value set in image", name) - return name - } - env[1] = os.Expand(env[1], getenv) - s.builder.SetEnv(env[0], env[1]) - } else { - s.builder.SetEnv(env[0], os.Getenv(env[0])) - } - } for _, envSpec := range s.executor.unsetEnvs { s.builder.UnsetEnv(envSpec) } diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md index ce9b3803..b69b99a8 100644 --- a/vendor/github.com/containers/buildah/install.md +++ b/vendor/github.com/containers/buildah/install.md @@ -52,9 +52,9 @@ rpm-ostree install buildah Note: [`podman`](https://podman.io) build is available by default. ### [Gentoo](https://www.gentoo.org) -[app-containers/podman](https://packages.gentoo.org/packages/app-containers/podman) +[app-containers/buildah](https://packages.gentoo.org/packages/app-containers/buildah) ```bash -sudo emerge app-containers/podman +sudo emerge app-containers/buildah ``` ### [openSUSE](https://www.opensuse.org) diff --git a/vendor/github.com/containers/buildah/internal/parse/parse.go b/vendor/github.com/containers/buildah/internal/parse/parse.go index 23af9aeb..5050be38 100644 --- a/vendor/github.com/containers/buildah/internal/parse/parse.go +++ b/vendor/github.com/containers/buildah/internal/parse/parse.go @@ -4,12 +4,14 @@ import ( "context" "fmt" "os" + "path" "path/filepath" "strconv" "strings" "errors" + "github.com/containers/buildah/define" "github.com/containers/buildah/internal" internalUtil "github.com/containers/buildah/internal/util" "github.com/containers/common/pkg/parse" @@ -17,13 +19,12 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/lockfile" + "github.com/containers/storage/pkg/unshare" specs "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" ) const ( - // TypeBind is the type for mounting host dir - TypeBind = "bind" // TypeTmpfs is the type for mounting tmpfs TypeTmpfs = "tmpfs" // TypeCache is the type for mounting a common persistent cache from host @@ -33,6 +34,9 @@ const ( BuildahCacheDir = "buildah-cache" // mount=type=cache allows users to lock a cache store while its being used by another build BuildahCacheLockfile = "buildah-cache-lockfile" + // All the lockfiles are stored in a separate directory inside `BuildahCacheDir` + // Example `/var/tmp/buildah-cache//buildah-cache-lockfile` + BuildahCacheLockfileDir = "buildah-cache-lockfiles" ) var ( @@ -46,9 +50,9 @@ var ( // GetBindMount parses a single bind mount entry from the --mount flag. // Returns specifiedMount and a string which contains name of image that we mounted otherwise its empty. // Caller is expected to perform unmount of any mounted images -func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails) (specs.Mount, string, error) { +func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, string, error) { newMount := specs.Mount{ - Type: TypeBind, + Type: define.TypeBind, } mountReadability := false @@ -98,10 +102,14 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st if len(kv) == 1 { return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + targetPath := kv[1] + if !path.IsAbs(targetPath) { + targetPath = filepath.Join(workDir, targetPath) + } + if err := parse.ValidateVolumeCtrDir(targetPath); err != nil { return newMount, "", err } - newMount.Destination = kv[1] + newMount.Destination = targetPath setDest = true case "consistency": // Option for OS X only, has no meaning on other platforms @@ -183,11 +191,19 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st return newMount, fromImage, nil } +// CleanCacheMount gets the cache parent created by `--mount=type=cache` and removes it. +func CleanCacheMount() error { + cacheParent := filepath.Join(internalUtil.GetTempDir(), BuildahCacheDir+"-"+strconv.Itoa(unshare.GetRootlessUID())) + return os.RemoveAll(cacheParent) +} + // GetCacheMount parses a single cache mount entry from the --mount flag. -func GetCacheMount(args []string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails) (specs.Mount, []string, error) { +// +// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??). +func GetCacheMount(args []string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, *lockfile.LockFile, error) { var err error var mode uint64 - lockedTargets := make([]string, 0) + var buildahLockFilesDir string var ( setDest bool setShared bool @@ -196,7 +212,7 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a ) fromStage := "" newMount := specs.Mount{ - Type: TypeBind, + Type: define.TypeBind, } // if id is set a new subdirectory with `id` will be created under /host-temp/buildah-build-cache/id id := "" @@ -235,59 +251,63 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a sharing = kv[1] case "bind-propagation": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } newMount.Options = append(newMount.Options, kv[1]) case "id": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } id = kv[1] case "from": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } fromStage = kv[1] case "target", "dst", "destination": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, lockedTargets, err + targetPath := kv[1] + if !path.IsAbs(targetPath) { + targetPath = filepath.Join(workDir, targetPath) } - newMount.Destination = kv[1] + if err := parse.ValidateVolumeCtrDir(targetPath); err != nil { + return newMount, nil, err + } + newMount.Destination = targetPath setDest = true case "src", "source": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } newMount.Source = kv[1] case "mode": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } mode, err = strconv.ParseUint(kv[1], 8, 32) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to parse cache mode: %w", err) + return newMount, nil, fmt.Errorf("unable to parse cache mode: %w", err) } case "uid": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } uid, err = strconv.Atoi(kv[1]) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to parse cache uid: %w", err) + return newMount, nil, fmt.Errorf("unable to parse cache uid: %w", err) } case "gid": if len(kv) == 1 { - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg) } gid, err = strconv.Atoi(kv[1]) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to parse cache gid: %w", err) + return newMount, nil, fmt.Errorf("unable to parse cache gid: %w", err) } default: - return newMount, lockedTargets, fmt.Errorf("%v: %w", kv[0], errBadMntOption) + return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadMntOption) } } @@ -298,7 +318,7 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a } if !setDest { - return newMount, lockedTargets, errBadVolDest + return newMount, nil, errBadVolDest } if fromStage != "" { @@ -315,7 +335,7 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a // Cache does not supports using image so if not stage found // return with error if mountPoint == "" { - return newMount, lockedTargets, fmt.Errorf("no stage found with name %s", fromStage) + return newMount, nil, fmt.Errorf("no stage found with name %s", fromStage) } // path should be /contextDir/specified path newMount.Source = filepath.Join(mountPoint, filepath.Clean(string(filepath.Separator)+newMount.Source)) @@ -326,18 +346,20 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a // create a common cache directory, which persists on hosts within temp lifecycle // add subdirectory if specified - // cache parent directory - cacheParent := filepath.Join(internalUtil.GetTempDir(), BuildahCacheDir) + // cache parent directory: creates separate cache parent for each user. + cacheParent := filepath.Join(internalUtil.GetTempDir(), BuildahCacheDir+"-"+strconv.Itoa(unshare.GetRootlessUID())) // create cache on host if not present err = os.MkdirAll(cacheParent, os.FileMode(0755)) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to create build cache directory: %w", err) + return newMount, nil, fmt.Errorf("unable to create build cache directory: %w", err) } if id != "" { newMount.Source = filepath.Join(cacheParent, filepath.Clean(id)) + buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, filepath.Clean(id)) } else { newMount.Source = filepath.Join(cacheParent, filepath.Clean(newMount.Destination)) + buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, filepath.Clean(newMount.Destination)) } idPair := idtools.IDPair{ UID: uid, @@ -346,26 +368,40 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a //buildkit parity: change uid and gid if specified otheriwise keep `0` err = idtools.MkdirAllAndChownNew(newMount.Source, os.FileMode(mode), idPair) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to change uid,gid of cache directory: %w", err) + return newMount, nil, fmt.Errorf("unable to change uid,gid of cache directory: %w", err) + } + + // create a subdirectory inside `cacheParent` just to store lockfiles + buildahLockFilesDir = filepath.Join(cacheParent, buildahLockFilesDir) + err = os.MkdirAll(buildahLockFilesDir, os.FileMode(0700)) + if err != nil { + return newMount, nil, fmt.Errorf("unable to create build cache lockfiles directory: %w", err) } } + var targetLock *lockfile.LockFile // = nil + succeeded := false + defer func() { + if !succeeded && targetLock != nil { + targetLock.Unlock() + } + }() switch sharing { case "locked": // lock parent cache - lockfile, err := lockfile.GetLockfile(filepath.Join(newMount.Source, BuildahCacheLockfile)) + lockfile, err := lockfile.GetLockFile(filepath.Join(buildahLockFilesDir, BuildahCacheLockfile)) if err != nil { - return newMount, lockedTargets, fmt.Errorf("unable to acquire lock when sharing mode is locked: %w", err) + return newMount, nil, fmt.Errorf("unable to acquire lock when sharing mode is locked: %w", err) } // Will be unlocked after the RUN step is executed. lockfile.Lock() - lockedTargets = append(lockedTargets, filepath.Join(newMount.Source, BuildahCacheLockfile)) + targetLock = lockfile case "shared": // do nothing since default is `shared` break default: // error out for unknown values - return newMount, lockedTargets, fmt.Errorf("unrecognized value %q for field `sharing`: %w", sharing, err) + return newMount, nil, fmt.Errorf("unrecognized value %q for field `sharing`: %w", sharing, err) } // buildkit parity: default sharing should be shared @@ -383,11 +419,12 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a opts, err := parse.ValidateVolumeOpts(newMount.Options) if err != nil { - return newMount, lockedTargets, err + return newMount, nil, err } newMount.Options = opts - return newMount, lockedTargets, nil + succeeded = true + return newMount, targetLock, nil } // ValidateVolumeMountHostDir validates the host path of buildah --volume @@ -474,19 +511,34 @@ func Volume(volume string) (specs.Mount, error) { return mount, nil } +// UnlockLockArray is a helper for cleaning up after GetVolumes and the like. +func UnlockLockArray(locks []*lockfile.LockFile) { + for _, lock := range locks { + lock.Unlock() + } +} + // GetVolumes gets the volumes from --volume and --mount -func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, mounts []string, contextDir string) ([]specs.Mount, []string, []string, error) { - unifiedMounts, mountedImages, lockedTargets, err := getMounts(ctx, store, mounts, contextDir) +// +// If this function succeeds, the caller must unlock the returned *lockfile.LockFile s if any (when??). +func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, mounts []string, contextDir string, workDir string) ([]specs.Mount, []string, []*lockfile.LockFile, error) { + unifiedMounts, mountedImages, targetLocks, err := getMounts(ctx, store, mounts, contextDir, workDir) if err != nil { - return nil, mountedImages, lockedTargets, err + return nil, mountedImages, nil, err } + succeeded := false + defer func() { + if !succeeded { + UnlockLockArray(targetLocks) + } + }() volumeMounts, err := getVolumeMounts(volumes) if err != nil { - return nil, mountedImages, lockedTargets, err + return nil, mountedImages, nil, err } for dest, mount := range volumeMounts { if _, ok := unifiedMounts[dest]; ok { - return nil, mountedImages, lockedTargets, fmt.Errorf("%v: %w", dest, errDuplicateDest) + return nil, mountedImages, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest) } unifiedMounts[dest] = mount } @@ -495,19 +547,28 @@ func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, for _, mount := range unifiedMounts { finalMounts = append(finalMounts, mount) } - return finalMounts, mountedImages, lockedTargets, nil + succeeded = true + return finalMounts, mountedImages, targetLocks, nil } // getMounts takes user-provided input from the --mount flag and creates OCI // spec mounts. // buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... // buildah run --mount type=tmpfs,target=/dev/shm ... -func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, contextDir string) (map[string]specs.Mount, []string, []string, error) { +// +// If this function succeeds, the caller must unlock the returned *lockfile.LockFile s if any (when??). +func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, contextDir string, workDir string) (map[string]specs.Mount, []string, []*lockfile.LockFile, error) { // If `type` is not set default to "bind" - mountType := TypeBind + mountType := define.TypeBind finalMounts := make(map[string]specs.Mount) mountedImages := make([]string, 0) - lockedTargets := make([]string, 0) + targetLocks := make([]*lockfile.LockFile, 0) + succeeded := false + defer func() { + if !succeeded { + UnlockLockArray(targetLocks) + } + }() errInvalidSyntax := errors.New("incorrect mount format: should be --mount type=,[src=,]target=[,options]") @@ -517,53 +578,56 @@ func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, c for _, mount := range mounts { tokens := strings.Split(mount, ",") if len(tokens) < 2 { - return nil, mountedImages, lockedTargets, fmt.Errorf("%q: %w", mount, errInvalidSyntax) + return nil, mountedImages, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax) } for _, field := range tokens { if strings.HasPrefix(field, "type=") { kv := strings.Split(field, "=") if len(kv) != 2 { - return nil, mountedImages, lockedTargets, fmt.Errorf("%q: %w", mount, errInvalidSyntax) + return nil, mountedImages, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax) } mountType = kv[1] } } switch mountType { - case TypeBind: - mount, image, err := GetBindMount(ctx, tokens, contextDir, store, "", nil) + case define.TypeBind: + mount, image, err := GetBindMount(ctx, tokens, contextDir, store, "", nil, workDir) if err != nil { - return nil, mountedImages, lockedTargets, err + return nil, mountedImages, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, mountedImages, lockedTargets, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) + return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount mountedImages = append(mountedImages, image) case TypeCache: - mount, lockedPaths, err := GetCacheMount(tokens, store, "", nil) - lockedTargets = lockedPaths + mount, tl, err := GetCacheMount(tokens, store, "", nil, workDir) if err != nil { - return nil, mountedImages, lockedTargets, err + return nil, mountedImages, nil, err + } + if tl != nil { + targetLocks = append(targetLocks, tl) } if _, ok := finalMounts[mount.Destination]; ok { - return nil, mountedImages, lockedTargets, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) + return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount case TypeTmpfs: mount, err := GetTmpfsMount(tokens) if err != nil { - return nil, mountedImages, lockedTargets, err + return nil, mountedImages, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, mountedImages, lockedTargets, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) + return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest) } finalMounts[mount.Destination] = mount default: - return nil, mountedImages, lockedTargets, fmt.Errorf("invalid filesystem type %q", mountType) + return nil, mountedImages, nil, fmt.Errorf("invalid filesystem type %q", mountType) } } - return finalMounts, mountedImages, lockedTargets, nil + succeeded = true + return finalMounts, mountedImages, targetLocks, nil } // GetTmpfsMount parses a single tmpfs mount entry from the --mount flag diff --git a/vendor/github.com/containers/buildah/internal/types.go b/vendor/github.com/containers/buildah/internal/types.go index 3b1c1062..ee87eca2 100644 --- a/vendor/github.com/containers/buildah/internal/types.go +++ b/vendor/github.com/containers/buildah/internal/types.go @@ -12,6 +12,7 @@ const ( // StageExecutor has ability to mount stages/images in current context and // automatically clean them up. type StageMountDetails struct { + DidExecute bool // tells if the stage which is being mounted was freshly executed or was part of older cache IsStage bool // tells if mountpoint returned from stage executor is stage or image MountPoint string // mountpoint of stage/image } diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go index 11f0933b..ddd86d22 100644 --- a/vendor/github.com/containers/buildah/new.go +++ b/vendor/github.com/containers/buildah/new.go @@ -301,6 +301,7 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions FromImage: imageSpec, FromImageID: imageID, FromImageDigest: imageDigest, + GroupAdd: options.GroupAdd, Container: name, ContainerID: container.ID, ImageAnnotations: imageAnnotations, diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go index 07bb2195..81810d28 100644 --- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go +++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go @@ -2,7 +2,6 @@ package overlay import ( "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -60,7 +59,7 @@ func TempDir(containerDir string, rootUID, rootGID int) (string, error) { return "", fmt.Errorf("failed to create the overlay %s directory: %w", contentDir, err) } - contentDir, err := ioutil.TempDir(contentDir, "") + contentDir, err := os.MkdirTemp(contentDir, "") if err != nil { return "", fmt.Errorf("failed to create the overlay tmpdir in %s directory: %w", contentDir, err) } @@ -291,7 +290,7 @@ func CleanupMount(contentDir string) (Err error) { func CleanupContent(containerDir string) (Err error) { contentDir := filepath.Join(containerDir, "overlay") - files, err := ioutil.ReadDir(contentDir) + files, err := os.ReadDir(contentDir) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index b3f26035..3d604589 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -16,6 +16,7 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containers/buildah/define" + securejoin "github.com/cyphar/filepath-securejoin" internalParse "github.com/containers/buildah/internal/parse" "github.com/containers/buildah/pkg/sshagent" "github.com/containers/common/pkg/config" @@ -45,21 +46,25 @@ const ( TypeTmpfs = "tmpfs" // TypeCache is the type for mounting a common persistent cache from host TypeCache = "cache" - // mount=type=cache must create a persistent directory on host so its available for all consecutive builds. + // mount=type=cache must create a persistent directory on host so it's available for all consecutive builds. // Lifecycle of following directory will be inherited from how host machine treats temporary directory BuildahCacheDir = "buildah-cache" ) -// RepoNameToNamedReference parse the raw string to Named reference -func RepoNameToNamedReference(dest string) (reference.Named, error) { - named, err := reference.ParseNormalizedNamed(dest) - if err != nil { - return nil, fmt.Errorf("invalid repo %q: must contain registry and repository: %w", dest, err) - } - if !reference.IsNameOnly(named) { - return nil, fmt.Errorf("repository must contain neither a tag nor digest: %v", named) +// RepoNamesToNamedReferences parse the raw string to Named reference +func RepoNamesToNamedReferences(destList []string) ([]reference.Named, error) { + var result []reference.Named + for _, dest := range destList { + named, err := reference.ParseNormalizedNamed(dest) + if err != nil { + return nil, fmt.Errorf("invalid repo %q: must contain registry and repository: %w", dest, err) + } + if !reference.IsNameOnly(named) { + return nil, fmt.Errorf("repository must contain neither a tag nor digest: %v", named) + } + result = append(result, named) } - return named, nil + return result, nil } // CommonBuildOptions parses the build options from the bud cli @@ -222,13 +227,14 @@ func GetAdditionalBuildContext(value string) (define.AdditionalBuildContext, err func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOptions) error { for _, opt := range securityOpts { if opt == "no-new-privileges" { - return errors.New("no-new-privileges is not supported") + commonOpts.NoNewPrivileges = true + continue } + con := strings.SplitN(opt, "=", 2) if len(con) != 2 { return fmt.Errorf("invalid --security-opt name=value pair: %q", opt) } - switch con[0] { case "label": commonOpts.LabelOpts = append(commonOpts.LabelOpts, con[1]) @@ -928,10 +934,11 @@ func IsolationOption(isolation string) (define.Isolation, error) { // Device parses device mapping string to a src, dest & permissions string // Valid values for device look like: -// '/dev/sdc" -// '/dev/sdc:/dev/xvdc" -// '/dev/sdc:/dev/xvdc:rwm" -// '/dev/sdc:rm" +// +// '/dev/sdc" +// '/dev/sdc:/dev/xvdc" +// '/dev/sdc:/dev/xvdc:rwm" +// '/dev/sdc:rm" func Device(device string) (string, string, string, error) { src := "" dst := "" @@ -1101,10 +1108,16 @@ func ContainerIgnoreFile(contextDir, path string, containerFiles []string) ([]st return excludes, containerfileIgnore, err } } - path = filepath.Join(contextDir, ".containerignore") + path, symlinkErr := securejoin.SecureJoin(contextDir, ".containerignore") + if symlinkErr != nil { + return nil, "", symlinkErr + } excludes, err := imagebuilder.ParseIgnore(path) if errors.Is(err, os.ErrNotExist) { - path = filepath.Join(contextDir, ".dockerignore") + path, symlinkErr = securejoin.SecureJoin(contextDir, ".dockerignore") + if symlinkErr != nil { + return nil, "", symlinkErr + } excludes, err = imagebuilder.ParseIgnore(path) } if errors.Is(err, os.ErrNotExist) { diff --git a/vendor/github.com/containers/buildah/pkg/sshagent/sshagent.go b/vendor/github.com/containers/buildah/pkg/sshagent/sshagent.go index 7a656302..fd835be3 100644 --- a/vendor/github.com/containers/buildah/pkg/sshagent/sshagent.go +++ b/vendor/github.com/containers/buildah/pkg/sshagent/sshagent.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "os" "path/filepath" + "runtime" "sync" "time" @@ -68,11 +68,18 @@ func newAgentServerSocket(socketPath string) (*AgentServer, error) { // Serve starts the SSH agent on the host and returns the path of the socket where the agent is serving func (a *AgentServer) Serve(processLabel string) (string, error) { + // Calls to `selinux.SetSocketLabel` should be wrapped in + // runtime.LockOSThread()/runtime.UnlockOSThread() until + // the the socket is created to guarantee another goroutine + // does not migrate to the current thread before execution + // is complete. + // Ref: https://github.com/opencontainers/selinux/blob/main/go-selinux/selinux.go#L158 + runtime.LockOSThread() err := selinux.SetSocketLabel(processLabel) if err != nil { return "", err } - serveDir, err := ioutil.TempDir("", ".buildah-ssh-sock") + serveDir, err := os.MkdirTemp("", ".buildah-ssh-sock") if err != nil { return "", err } @@ -83,7 +90,12 @@ func (a *AgentServer) Serve(processLabel string) (string, error) { if err != nil { return "", err } + // Reset socket label. err = selinux.SetSocketLabel("") + // Unlock the thread only if the process label could be restored + // successfully. Otherwise leave the thread locked and the Go runtime + // will terminate it once it returns to the threads pool. + runtime.UnlockOSThread() if err != nil { return "", err } @@ -210,7 +222,7 @@ func NewSource(paths []string) (*Source, error) { if err != nil { return nil, err } - dt, err := ioutil.ReadAll(&io.LimitedReader{R: f, N: 100 * 1024}) + dt, err := io.ReadAll(&io.LimitedReader{R: f, N: 100 * 1024}) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go index 7c8b6ba7..a27a4801 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go @@ -3,12 +3,12 @@ package util import ( "bytes" "errors" - "io/ioutil" "time" + "os" ) func ReadUptime() (time.Duration, error) { - buf, err := ioutil.ReadFile("/proc/uptime") + buf, err := os.ReadFile("/proc/uptime") if err != nil { return 0, err } diff --git a/vendor/github.com/containers/buildah/pkg/util/util.go b/vendor/github.com/containers/buildah/pkg/util/util.go index 20e9ede4..6bb20219 100644 --- a/vendor/github.com/containers/buildah/pkg/util/util.go +++ b/vendor/github.com/containers/buildah/pkg/util/util.go @@ -2,7 +2,6 @@ package util import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -21,12 +20,12 @@ func MirrorToTempFileIfPathIsDescriptor(file string) (string, bool) { if !strings.HasPrefix(file, "/dev/fd") { return file, false } - b, err := ioutil.ReadFile(file) + b, err := os.ReadFile(file) if err != nil { // if anything goes wrong return original path return file, false } - tmpfile, err := ioutil.TempFile(os.TempDir(), "buildah-temp-file") + tmpfile, err := os.CreateTemp(os.TempDir(), "buildah-temp-file") if err != nil { return file, false } diff --git a/vendor/github.com/containers/buildah/run.go b/vendor/github.com/containers/buildah/run.go index d3171113..4473bef0 100644 --- a/vendor/github.com/containers/buildah/run.go +++ b/vendor/github.com/containers/buildah/run.go @@ -8,6 +8,7 @@ import ( "github.com/containers/buildah/internal" "github.com/containers/buildah/pkg/sshagent" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/lockfile" "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" @@ -176,12 +177,14 @@ type runMountArtifacts struct { Agents []*sshagent.AgentServer // SSHAuthSock is the path to the ssh auth sock inside the container SSHAuthSock string - // LockedTargets to be unlocked if there are any. - LockedTargets []string + // TargetLocks to be unlocked if there are any. + TargetLocks []*lockfile.LockFile } // RunMountInfo are the available run mounts for this run type runMountInfo struct { + // WorkDir is the current working directory inside the container. + WorkDir string // ContextDir is the root directory for the source location for bind mounts. ContextDir string // Secrets are the available secrets to use in a RUN diff --git a/vendor/github.com/containers/buildah/run_common.go b/vendor/github.com/containers/buildah/run_common.go index 6bd20d8d..bcfd70e1 100644 --- a/vendor/github.com/containers/buildah/run_common.go +++ b/vendor/github.com/containers/buildah/run_common.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "os" "os/exec" @@ -266,6 +265,20 @@ func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, opti for _, gid := range user.AdditionalGids { g.AddProcessAdditionalGid(gid) } + for _, group := range b.GroupAdd { + if group == "keep-groups" { + if len(b.GroupAdd) > 1 { + return "", errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options") + } + g.AddAnnotation("run.oci.keep_original_groups", "1") + continue + } + gid, err := strconv.ParseUint(group, 10, 32) + if err != nil { + return "", err + } + g.AddProcessAdditionalGid(uint32(gid)) + } // Remove capabilities if not running as root except Bounding set if user.UID != 0 && g.Config.Process.Capabilities != nil { @@ -556,7 +569,7 @@ func runUsingRuntime(options RunOptions, configureNetwork bool, moreCreateArgs [ }() // Make sure we read the container's exit status when it exits. - pidValue, err := ioutil.ReadFile(pidFile) + pidValue, err := os.ReadFile(pidFile) if err != nil { return 1, err } @@ -1185,7 +1198,7 @@ func (b *Builder) runUsingRuntimeSubproc(isolation define.Isolation, options Run logrus.Errorf("did not get container create message from subprocess: %v", err) } else { pidFile := filepath.Join(bundlePath, "pid") - pidValue, err := ioutil.ReadFile(pidFile) + pidValue, err := os.ReadFile(pidFile) if err != nil { return err } @@ -1199,7 +1212,7 @@ func (b *Builder) runUsingRuntimeSubproc(isolation define.Isolation, options Run defer teardown() } if err != nil { - return err + return fmt.Errorf("setup network: %w", err) } // only add hosts if we manage the hosts file @@ -1256,6 +1269,7 @@ func init() { reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain) } +// If this succeeds, the caller must call cleanupMounts(). func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, runFileMounts []string, runMountInfo runMountInfo) (*runMountArtifacts, error) { // Start building a new list of mounts. var mounts []specs.Mount @@ -1318,6 +1332,12 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st if err != nil { return nil, err } + succeeded := false + defer func() { + if !succeeded { + internalParse.UnlockLockArray(mountArtifacts.TargetLocks) + } + }() // Add temporary copies of the contents of volume locations at the // volume locations, unless we already have something there. builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, builtinVolumes, int(rootUID), int(rootGID)) @@ -1353,6 +1373,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st // Set the list in the spec. spec.Mounts = mounts + succeeded = true return mountArtifacts, nil } @@ -1444,9 +1465,11 @@ func cleanableDestinationListFromMounts(mounts []spec.Mount) []string { } // runSetupRunMounts sets up mounts that exist only in this RUN, not in subsequent runs +// +// If this function succeeds, the caller must unlock runMountArtifacts.TargetLocks (when??) func (b *Builder) runSetupRunMounts(mounts []string, sources runMountInfo, idMaps IDMaps) ([]spec.Mount, *runMountArtifacts, error) { - // If `type` is not set default to "bind" - mountType := internalParse.TypeBind + // If `type` is not set default to TypeBind + mountType := define.TypeBind mountTargets := make([]string, 0, 10) tmpFiles := make([]string, 0, len(mounts)) mountImages := make([]string, 0, 10) @@ -1454,7 +1477,13 @@ func (b *Builder) runSetupRunMounts(mounts []string, sources runMountInfo, idMap agents := make([]*sshagent.AgentServer, 0, len(mounts)) sshCount := 0 defaultSSHSock := "" - lockedTargets := []string{} + targetLocks := []*lockfile.LockFile{} + succeeded := false + defer func() { + if !succeeded { + internalParse.UnlockLockArray(targetLocks) + } + }() for _, mount := range mounts { tokens := strings.Split(mount, ",") for _, field := range tokens { @@ -1468,7 +1497,7 @@ func (b *Builder) runSetupRunMounts(mounts []string, sources runMountInfo, idMap } switch mountType { case "secret": - mount, envFile, err := b.getSecretMount(tokens, sources.Secrets, idMaps) + mount, envFile, err := b.getSecretMount(tokens, sources.Secrets, idMaps, sources.WorkDir) if err != nil { return nil, nil, err } @@ -1494,8 +1523,8 @@ func (b *Builder) runSetupRunMounts(mounts []string, sources runMountInfo, idMap // Count is needed as the default destination of the ssh sock inside the container is /run/buildkit/ssh_agent.{i} sshCount++ } - case "bind": - mount, image, err := b.getBindMount(tokens, sources.SystemContext, sources.ContextDir, sources.StageMountPoints, idMaps) + case define.TypeBind: + mount, image, err := b.getBindMount(tokens, sources.SystemContext, sources.ContextDir, sources.StageMountPoints, idMaps, sources.WorkDir) if err != nil { return nil, nil, err } @@ -1513,34 +1542,37 @@ func (b *Builder) runSetupRunMounts(mounts []string, sources runMountInfo, idMap finalMounts = append(finalMounts, *mount) mountTargets = append(mountTargets, mount.Destination) case "cache": - mount, lockedPaths, err := b.getCacheMount(tokens, sources.StageMountPoints, idMaps) + mount, tl, err := b.getCacheMount(tokens, sources.StageMountPoints, idMaps, sources.WorkDir) if err != nil { return nil, nil, err } finalMounts = append(finalMounts, *mount) mountTargets = append(mountTargets, mount.Destination) - lockedTargets = lockedPaths + if tl != nil { + targetLocks = append(targetLocks, tl) + } default: return nil, nil, fmt.Errorf("invalid mount type %q", mountType) } } + succeeded = true artifacts := &runMountArtifacts{ RunMountTargets: mountTargets, TmpFiles: tmpFiles, Agents: agents, MountedImages: mountImages, SSHAuthSock: defaultSSHSock, - LockedTargets: lockedTargets, + TargetLocks: targetLocks, } return finalMounts, artifacts, nil } -func (b *Builder) getBindMount(tokens []string, context *imageTypes.SystemContext, contextDir string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps) (*spec.Mount, string, error) { +func (b *Builder) getBindMount(tokens []string, context *imageTypes.SystemContext, contextDir string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*spec.Mount, string, error) { if contextDir == "" { return nil, "", errors.New("Context Directory for current run invocation is not configured") } var optionMounts []specs.Mount - mount, image, err := internalParse.GetBindMount(context, tokens, contextDir, b.store, b.MountLabel, stageMountPoints) + mount, image, err := internalParse.GetBindMount(context, tokens, contextDir, b.store, b.MountLabel, stageMountPoints, workDir) if err != nil { return nil, image, err } @@ -1566,7 +1598,7 @@ func (b *Builder) getTmpfsMount(tokens []string, idMaps IDMaps) (*spec.Mount, er return &volumes[0], nil } -func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secret, idMaps IDMaps) (*spec.Mount, string, error) { +func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secret, idMaps IDMaps, workdir string) (*spec.Mount, string, error) { errInvalidSyntax := errors.New("secret should have syntax id=id[,target=path,required=bool,mode=uint,uid=uint,gid=uint") if len(tokens) == 0 { return nil, "", errInvalidSyntax @@ -1586,6 +1618,9 @@ func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secr id = kv[1] case "target", "dst", "destination": target = kv[1] + if !filepath.IsAbs(target) { + target = filepath.Join(workdir, target) + } case "required": required, err = strconv.ParseBool(kv[1]) if err != nil { @@ -1636,7 +1671,7 @@ func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secr switch secr.SourceType { case "env": data = []byte(os.Getenv(secr.Source)) - tmpFile, err := ioutil.TempFile(define.TempDir, "buildah*") + tmpFile, err := os.CreateTemp(define.TempDir, "buildah*") if err != nil { return nil, "", err } @@ -1647,7 +1682,7 @@ func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secr if err != nil { return nil, "", err } - data, err = ioutil.ReadFile(secr.Source) + data, err = os.ReadFile(secr.Source) if err != nil { return nil, "", err } @@ -1661,7 +1696,7 @@ func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secr if err := os.MkdirAll(filepath.Dir(ctrFileOnHost), 0755); err != nil { return nil, "", err } - if err := ioutil.WriteFile(ctrFileOnHost, data, 0644); err != nil { + if err := os.WriteFile(ctrFileOnHost, data, 0644); err != nil { return nil, "", err } @@ -1862,30 +1897,6 @@ func (b *Builder) cleanupRunMounts(context *imageTypes.SystemContext, mountpoint } } // unlock if any locked files from this RUN statement - for _, path := range artifacts.LockedTargets { - _, err := os.Stat(path) - if err != nil { - // Lockfile not found this might be a problem, - // since LockedTargets must contain list of all locked files - // don't break here since we need to unlock other files but - // log so user can take a look - logrus.Warnf("Lockfile %q was expected here, stat failed with %v", path, err) - continue - } - lockfile, err := lockfile.GetLockfile(path) - if err != nil { - // unable to get lockfile - // lets log error and continue - // unlocking other files - logrus.Warn(err) - continue - } - if lockfile.Locked() { - lockfile.Unlock() - } else { - logrus.Warnf("Lockfile %q was expected to be locked, this is unexpected", path) - continue - } - } + internalParse.UnlockLockArray(artifacts.TargetLocks) return prevErr } diff --git a/vendor/github.com/containers/buildah/run_freebsd.go b/vendor/github.com/containers/buildah/run_freebsd.go index 27982560..ba58982c 100644 --- a/vendor/github.com/containers/buildah/run_freebsd.go +++ b/vendor/github.com/containers/buildah/run_freebsd.go @@ -6,7 +6,6 @@ package buildah import ( "errors" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -25,6 +24,7 @@ import ( nettypes "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/stringid" "github.com/docker/go-units" "github.com/opencontainers/runtime-spec/specs-go" @@ -71,7 +71,7 @@ func setChildProcess() error { } func (b *Builder) Run(command []string, options RunOptions) error { - p, err := ioutil.TempDir("", Package) + p, err := os.MkdirTemp("", Package) if err != nil { return err } @@ -252,10 +252,6 @@ func (b *Builder) Run(command []string, options RunOptions) error { jconf.Set("devfs_ruleset", 4) jconf.Set("allow.raw_sockets", true) jconf.Set("allow.chflags", true) - jconf.Set("allow.mount", true) - jconf.Set("allow.mount.devfs", true) - jconf.Set("allow.mount.nullfs", true) - jconf.Set("allow.mount.fdescfs", true) jconf.Set("securelevel", -1) netjail, err := jail.Create(jconf) if err != nil { @@ -308,7 +304,8 @@ func setupSpecialMountSpecChanges(spec *spec.Spec, shmSize string) ([]specs.Moun return spec.Mounts, nil } -func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps) (*spec.Mount, []string, error) { +// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??). +func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*spec.Mount, *lockfile.LockFile, error) { return nil, nil, errors.New("cache mounts not supported on freebsd") } @@ -359,8 +356,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, if len(spliti) > 2 { options = strings.Split(spliti[2], ",") } - options = append(options, "bind") - mount, err := parseMount("bind", spliti[0], spliti[1], options) + mount, err := parseMount("nullfs", spliti[0], spliti[1], options) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index 09a3cd06..4dc6ddb5 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -7,7 +7,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -34,6 +33,7 @@ import ( hooksExec "github.com/containers/common/pkg/hooks/exec" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/unshare" "github.com/docker/go-units" @@ -69,7 +69,7 @@ func setChildProcess() error { // Run runs the specified command in the container's root filesystem. func (b *Builder) Run(command []string, options RunOptions) error { - p, err := ioutil.TempDir("", define.Package) + p, err := os.MkdirTemp("", define.Package) if err != nil { return err } @@ -114,8 +114,10 @@ func (b *Builder) Run(command []string, options RunOptions) error { return err } + workDir := b.WorkDir() if options.WorkingDir != "" { g.SetProcessCwd(options.WorkingDir) + workDir = options.WorkingDir } else if b.WorkDir() != "" { g.SetProcessCwd(b.WorkDir()) } @@ -209,6 +211,8 @@ func (b *Builder) Run(command []string, options RunOptions) error { return err } + g.SetProcessNoNewPrivileges(b.CommonBuildOpts.NoNewPrivileges) + g.SetProcessApparmorProfile(b.CommonBuildOpts.ApparmorProfile) // Now grab the spec from the generator. Set the generator to nil so that future contributors @@ -318,6 +322,7 @@ rootless=%d } runMountInfo := runMountInfo{ + WorkDir: workDir, ContextDir: options.ContextDir, Secrets: options.Secrets, SSHSources: options.SSHSources, @@ -477,7 +482,7 @@ func setupRootlessNetwork(pid int) (teardown func(), err error) { defer rootlessSlirpSyncR.Close() // Be sure there are no fds inherited to slirp4netns except the sync pipe - files, err := ioutil.ReadDir("/proc/self/fd") + files, err := os.ReadDir("/proc/self/fd") if err != nil { return nil, fmt.Errorf("cannot list open fds: %w", err) } @@ -1196,18 +1201,26 @@ func checkIdsGreaterThan5(ids []spec.LinuxIDMapping) bool { return false } -func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps) (*spec.Mount, []string, error) { +// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??). +func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*spec.Mount, *lockfile.LockFile, error) { var optionMounts []specs.Mount - mount, lockedTargets, err := internalParse.GetCacheMount(tokens, b.store, b.MountLabel, stageMountPoints) + mount, targetLock, err := internalParse.GetCacheMount(tokens, b.store, b.MountLabel, stageMountPoints, workDir) if err != nil { - return nil, lockedTargets, err + return nil, nil, err } + succeeded := false + defer func() { + if !succeeded && targetLock != nil { + targetLock.Unlock() + } + }() optionMounts = append(optionMounts, mount) volumes, err := b.runSetupVolumeMounts(b.MountLabel, nil, optionMounts, idMaps) if err != nil { - return nil, lockedTargets, err + return nil, nil, err } - return &volumes[0], lockedTargets, nil + succeeded = true + return &volumes[0], targetLock, nil } // setPdeathsig sets a parent-death signal for the process diff --git a/vendor/github.com/containers/buildah/seccomp.go b/vendor/github.com/containers/buildah/seccomp.go index 0f9a2c48..3348a3e1 100644 --- a/vendor/github.com/containers/buildah/seccomp.go +++ b/vendor/github.com/containers/buildah/seccomp.go @@ -5,7 +5,7 @@ package buildah import ( "fmt" - "io/ioutil" + "os" "github.com/containers/common/pkg/seccomp" "github.com/opencontainers/runtime-spec/specs-go" @@ -22,7 +22,7 @@ func setupSeccomp(spec *specs.Spec, seccompProfilePath string) error { } spec.Linux.Seccomp = seccompConfig default: - seccompProfile, err := ioutil.ReadFile(seccompProfilePath) + seccompProfile, err := os.ReadFile(seccompProfilePath) if err != nil { return fmt.Errorf("opening seccomp profile failed: %w", err) } diff --git a/vendor/github.com/containers/buildah/util/util.go b/vendor/github.com/containers/buildah/util/util.go index 4c67af70..bec86148 100644 --- a/vendor/github.com/containers/buildah/util/util.go +++ b/vendor/github.com/containers/buildah/util/util.go @@ -441,7 +441,14 @@ func (m byDestination) Len() int { } func (m byDestination) Less(i, j int) bool { - return m.parts(i) < m.parts(j) + iparts, jparts := m.parts(i), m.parts(j) + switch { + case iparts < jparts: + return true + case iparts > jparts: + return false + } + return filepath.Clean(m[i].Destination) < filepath.Clean(m[j].Destination) } func (m byDestination) Swap(i, j int) { @@ -453,7 +460,7 @@ func (m byDestination) parts(i int) int { } func SortMounts(m []specs.Mount) []specs.Mount { - sort.Sort(byDestination(m)) + sort.Stable(byDestination(m)) return m } diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go index 13ed9c85..29c76113 100644 --- a/vendor/github.com/containers/common/libimage/copier.go +++ b/vendor/github.com/containers/common/libimage/copier.go @@ -16,6 +16,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/signature/signer" storageTransport "github.com/containers/image/v5/storage" "github.com/containers/image/v5/types" encconfig "github.com/containers/ocicrypt/config" @@ -99,6 +100,9 @@ type CopyOptions struct { PolicyAllowStorage bool // SignaturePolicyPath to overwrite the default one. SignaturePolicyPath string + // If non-empty, asks for signatures to be added during the copy + // using the provided signers. + Signers []*signer.Signer // If non-empty, asks for a signature to be added during the copy, and // specifies a key ID. SignBy string @@ -299,6 +303,7 @@ func (r *Runtime) newCopier(options *CopyOptions) (*copier, error) { c.imageCopyOptions.OciEncryptLayers = options.OciEncryptLayers c.imageCopyOptions.OciDecryptConfig = options.OciDecryptConfig c.imageCopyOptions.RemoveSignatures = options.RemoveSignatures + c.imageCopyOptions.Signers = options.Signers c.imageCopyOptions.SignBy = options.SignBy c.imageCopyOptions.SignPassphrase = options.SignPassphrase c.imageCopyOptions.SignBySigstorePrivateKeyFile = options.SignBySigstorePrivateKeyFile diff --git a/vendor/github.com/containers/common/libimage/disk_usage.go b/vendor/github.com/containers/common/libimage/disk_usage.go index 2cde0984..431642f5 100644 --- a/vendor/github.com/containers/common/libimage/disk_usage.go +++ b/vendor/github.com/containers/common/libimage/disk_usage.go @@ -28,26 +28,51 @@ type ImageDiskUsage struct { // DiskUsage calculates the disk usage for each image in the local containers // storage. Note that a single image may yield multiple usage reports, one for // each repository tag. -func (r *Runtime) DiskUsage(ctx context.Context) ([]ImageDiskUsage, error) { +func (r *Runtime) DiskUsage(ctx context.Context) ([]ImageDiskUsage, int64, error) { layerTree, err := r.layerTree() if err != nil { - return nil, err + return nil, -1, err } images, err := r.ListImages(ctx, nil, nil) if err != nil { - return nil, err + return nil, -1, err } + var totalSize int64 + visitedImages := make(map[string]bool) + visistedLayers := make(map[string]bool) + var allUsages []ImageDiskUsage for _, image := range images { usages, err := diskUsageForImage(ctx, image, layerTree) if err != nil { - return nil, err + return nil, -1, err } allUsages = append(allUsages, usages...) + + if _, ok := visitedImages[image.ID()]; ok { + // Do not count an image twice + continue + } + visitedImages[image.ID()] = true + + size, err := image.Size() + if err != nil { + return nil, -1, err + } + for _, layer := range layerTree.layersOf(image) { + if _, ok := visistedLayers[layer.ID]; ok { + // Do not count a layer twice, so remove its + // size from the image size. + size -= layer.UncompressedSize + continue + } + visistedLayers[layer.ID] = true + } + totalSize += size } - return allUsages, err + return allUsages, totalSize, err } // diskUsageForImage returns the disk-usage baseistics for the specified image. diff --git a/vendor/github.com/containers/common/libimage/filters.go b/vendor/github.com/containers/common/libimage/filters.go index f387edf5..8f85640e 100644 --- a/vendor/github.com/containers/common/libimage/filters.go +++ b/vendor/github.com/containers/common/libimage/filters.go @@ -73,7 +73,8 @@ func (r *Runtime) filterImages(ctx context.Context, images []*Image, options *Li // compileImageFilters creates `filterFunc`s for the specified filters. The // required format is `key=value` with the following supported keys: -// after, since, before, containers, dangling, id, label, readonly, reference, intermediate +// +// after, since, before, containers, dangling, id, label, readonly, reference, intermediate func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) (map[string][]filterFunc, error) { logrus.Tracef("Parsing image filters %s", options.Filters) @@ -145,6 +146,9 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp case "id": filter = filterID(value) + case "digest": + filter = filterDigest(value) + case "intermediate": intermediate, err := r.bool(duplicate, key, value) if err != nil { @@ -382,6 +386,13 @@ func filterID(value string) filterFunc { } } +// filterDigest creates an digest filter for matching the specified value. +func filterDigest(value string) filterFunc { + return func(img *Image) (bool, error) { + return string(img.Digest()) == value, nil + } +} + // filterIntermediate creates an intermediate filter for images. An image is // considered to be an intermediate image if it is dangling (i.e., no tags) and // has no children (i.e., no other image depends on it). diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go index 0130532c..032dd139 100644 --- a/vendor/github.com/containers/common/libimage/image.go +++ b/vendor/github.com/containers/common/libimage/image.go @@ -46,6 +46,8 @@ type Image struct { ociv1Image *ociv1.Image // Names() parsed into references. namesReferences []reference.Reference + // Calculating the Size() is expensive, so cache it. + size *int64 } } @@ -62,6 +64,7 @@ func (i *Image) reload() error { i.cached.completeInspectData = nil i.cached.ociv1Image = nil i.cached.namesReferences = nil + i.cached.size = nil return nil } @@ -775,7 +778,13 @@ func (i *Image) Unmount(force bool) error { // Size computes the size of the image layers and associated data. func (i *Image) Size() (int64, error) { - return i.runtime.store.ImageSize(i.ID()) + if i.cached.size != nil { + return *i.cached.size, nil + } + + size, err := i.runtime.store.ImageSize(i.ID()) + i.cached.size = &size + return size, err } // HasDifferentDigestOptions allows for customizing the check if another diff --git a/vendor/github.com/containers/common/libimage/layer_tree.go b/vendor/github.com/containers/common/libimage/layer_tree.go index 05f21531..8c84dc41 100644 --- a/vendor/github.com/containers/common/libimage/layer_tree.go +++ b/vendor/github.com/containers/common/libimage/layer_tree.go @@ -126,6 +126,17 @@ func (r *Runtime) layerTree() (*layerTree, error) { return &tree, nil } +// layersOf returns all storage layers of the specified image. +func (t *layerTree) layersOf(image *Image) []*storage.Layer { + var layers []*storage.Layer + node := t.node(image.TopLayer()) + for node != nil { + layers = append(layers, node.layer) + node = node.parent + } + return layers +} + // children returns the child images of parent. Child images are images with // either the same top layer as parent or parent being the true parent layer. // Furthermore, the history of the parent and child images must match with the diff --git a/vendor/github.com/containers/common/libimage/manifest_list.go b/vendor/github.com/containers/common/libimage/manifest_list.go index cec44f1a..bf1738a3 100644 --- a/vendor/github.com/containers/common/libimage/manifest_list.go +++ b/vendor/github.com/containers/common/libimage/manifest_list.go @@ -452,6 +452,7 @@ func (m *ManifestList) Push(ctx context.Context, destination string, options *Ma ImageListSelection: options.ImageListSelection, Instances: options.Instances, ReportWriter: options.Writer, + Signers: options.Signers, SignBy: options.SignBy, SignPassphrase: options.SignPassphrase, SignBySigstorePrivateKeyFile: options.SignBySigstorePrivateKeyFile, diff --git a/vendor/github.com/containers/common/libimage/manifests/manifests.go b/vendor/github.com/containers/common/libimage/manifests/manifests.go index 163e37f0..0f3c1d71 100644 --- a/vendor/github.com/containers/common/libimage/manifests/manifests.go +++ b/vendor/github.com/containers/common/libimage/manifests/manifests.go @@ -15,6 +15,7 @@ import ( "github.com/containers/image/v5/image" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/signature/signer" is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" @@ -61,6 +62,7 @@ type PushOptions struct { ImageListSelection cp.ImageListSelection // set to either CopySystemImage, CopyAllImages, or CopySpecificImages Instances []digest.Digest // instances to copy if ImageListSelection == CopySpecificImages ReportWriter io.Writer // will be used to log the writing of the list and any blobs + Signers []*signer.Signer // if non-empty, asks for signatures to be added during the copy using the provided signers. SignBy string // fingerprint of GPG key to use to sign images SignPassphrase string // passphrase to use when signing with the key ID from SignBy. SignBySigstorePrivateKeyFile string // if non-empty, asks for a signature to be added during the copy, using a sigstore private key file at the provided path. @@ -244,6 +246,7 @@ func (l *list) Push(ctx context.Context, dest types.ImageReference, options Push DestinationCtx: options.SystemContext, ReportWriter: options.ReportWriter, RemoveSignatures: options.RemoveSignatures, + Signers: options.Signers, SignBy: options.SignBy, SignPassphrase: options.SignPassphrase, SignBySigstorePrivateKeyFile: options.SignBySigstorePrivateKeyFile, diff --git a/vendor/github.com/containers/common/libimage/platform.go b/vendor/github.com/containers/common/libimage/platform.go index 4d5dde31..06c15ee6 100644 --- a/vendor/github.com/containers/common/libimage/platform.go +++ b/vendor/github.com/containers/common/libimage/platform.go @@ -69,9 +69,9 @@ func toPlatformString(os, arch, variant string) string { // Checks whether the image matches the specified platform. // Returns -// * 1) a matching error that can be used for logging (or returning) what does not match -// * 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error) -// * 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.) +// - 1) a matching error that can be used for logging (or returning) what does not match +// - 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error) +// - 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.) func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) (error, bool, error) { if err := i.isCorrupted(""); err != nil { return err, false, nil @@ -96,5 +96,5 @@ func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) ( return nil, customPlatform, nil } - return fmt.Errorf("image platform (%s) does not match the expected platform (%s)", fromImage, expected), customPlatform, nil + return fmt.Errorf("image platform (%s) does not match the expected platform (%s)", platforms.Format(fromImage), platforms.Format(expected)), customPlatform, nil } diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 51712bb3..95513286 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -232,7 +232,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, imageName = storageName case ociArchiveTransport.Transport.Name(): - manifestDescriptor, err := ociArchiveTransport.LoadManifestDescriptor(ref) + manifestDescriptor, err := ociArchiveTransport.LoadManifestDescriptorWithContext(r.SystemContext(), ref) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go index de6adbdc..6e4514b9 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go +++ b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go @@ -7,7 +7,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -328,8 +327,11 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ } cniPathName := "" if writeToDisk { + if err := os.MkdirAll(n.cniConfigDir, 0o755); err != nil { + return nil, "", err + } cniPathName = filepath.Join(n.cniConfigDir, network.Name+".conflist") - err = ioutil.WriteFile(cniPathName, b, 0o644) + err = os.WriteFile(cniPathName, b, 0o644) if err != nil { return nil, "", err } diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_types.go b/vendor/github.com/containers/common/libnetwork/cni/cni_types.go index cfc5b33b..1d48d080 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/cni_types.go +++ b/vendor/github.com/containers/common/libnetwork/cni/cni_types.go @@ -287,7 +287,7 @@ func newVLANPlugin(pluginType, device, mode string, mtu int, ipam *ipamConfig) V caps := make(map[string]bool) caps["ips"] = true // if we use host-local set the ips cap to ensure we can set static ips via runtime config - if ipam.PluginType == types.HostLocalIPAMDriver { + if m.IPAM.PluginType == types.HostLocalIPAMDriver { m.Capabilities = caps } return m diff --git a/vendor/github.com/containers/common/libnetwork/cni/config.go b/vendor/github.com/containers/common/libnetwork/cni/config.go index aa94e73d..b7021ec5 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/config.go +++ b/vendor/github.com/containers/common/libnetwork/cni/config.go @@ -15,9 +15,13 @@ import ( "github.com/sirupsen/logrus" ) +func (n *cniNetwork) NetworkUpdate(name string, options types.NetworkUpdateOptions) error { + return fmt.Errorf("NetworkUpdate is not supported for backend CNI: %w", types.ErrInvalidArg) +} + // NetworkCreate will take a partial filled Network and fill the // missing fields. It creates the Network and returns the full Network. -func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { +func (n *cniNetwork) NetworkCreate(net types.Network, options *types.NetworkCreateOptions) (types.Network, error) { n.lock.Lock() defer n.lock.Unlock() err := n.loadNetworks() @@ -26,6 +30,11 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { } network, err := n.networkCreate(&net, false) if err != nil { + if options != nil && options.IgnoreIfExists && errors.Is(err, types.ErrNetworkExists) { + if network, ok := n.networks[net.Name]; ok { + return *network.libpodNet, nil + } + } return types.Network{}, err } // add the new network to the map @@ -36,6 +45,9 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { // networkCreate will fill out the given network struct and return the new network entry. // If defaultNet is true it will not validate against used subnets and it will not write the cni config to disk. func (n *cniNetwork) networkCreate(newNetwork *types.Network, defaultNet bool) (*network, error) { + if len(newNetwork.NetworkDNSServers) > 0 { + return nil, fmt.Errorf("NetworkDNSServers cannot be configured for backend CNI: %w", types.ErrInvalidArg) + } // if no driver is set use the default one if newNetwork.Driver == "" { newNetwork.Driver = types.DefaultNetworkDriver diff --git a/vendor/github.com/containers/common/libnetwork/cni/network.go b/vendor/github.com/containers/common/libnetwork/cni/network.go index 11f1bbe1..80b04a3c 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/network.go +++ b/vendor/github.com/containers/common/libnetwork/cni/network.go @@ -18,10 +18,12 @@ import ( "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" "github.com/containers/storage/pkg/lockfile" + "github.com/containers/storage/pkg/unshare" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) +const defaultRootLockPath = "/run/lock/podman-cni.lock" + type cniNetwork struct { // cniConfigDir is directory where the cni config files are stored. cniConfigDir string @@ -81,19 +83,15 @@ type InitConfig struct { // NewCNINetworkInterface creates the ContainerNetwork interface for the CNI backend. // Note: The networks are not loaded from disk until a method is called. func NewCNINetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { - // TODO: consider using a shared memory lock - lock, err := lockfile.GetLockfile(filepath.Join(conf.CNIConfigDir, "cni.lock")) + // root needs to use a globally unique lock because there is only one host netns + lockPath := defaultRootLockPath + if unshare.IsRootless() { + lockPath = filepath.Join(conf.CNIConfigDir, "cni.lock") + } + + lock, err := lockfile.GetLockFile(lockPath) if err != nil { - // If we're on a read-only filesystem, there is no risk of - // contention. Fall back to a local lockfile. - if errors.Is(err, unix.EROFS) { - lock, err = lockfile.GetLockfile(filepath.Join(conf.RunDir, "cni.lock")) - if err != nil { - return nil, err - } - } else { - return nil, err - } + return nil, err } defaultNetworkName := conf.DefaultNetwork @@ -143,11 +141,15 @@ func (n *cniNetwork) DefaultNetworkName() string { func (n *cniNetwork) loadNetworks() error { // check the mod time of the config dir + var modTime time.Time f, err := os.Stat(n.cniConfigDir) - if err != nil { + // ignore error if the file does not exists + if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - modTime := f.ModTime() + if err == nil { + modTime = f.ModTime() + } // skip loading networks if they are already loaded and // if the config dir was not modified since the last call diff --git a/vendor/github.com/containers/common/libnetwork/netavark/config.go b/vendor/github.com/containers/common/libnetwork/netavark/config.go index 64714365..4b1ab7d5 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/config.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/config.go @@ -10,6 +10,7 @@ import ( "net" "os" "path/filepath" + "reflect" "strconv" "time" @@ -19,9 +20,68 @@ import ( "github.com/containers/storage/pkg/stringid" ) +func sliceRemoveDuplicates(strList []string) []string { + list := make([]string, 0, len(strList)) + for _, item := range strList { + if !util.StringInSlice(item, list) { + list = append(list, item) + } + } + return list +} + +func (n *netavarkNetwork) commitNetwork(network *types.Network) error { + confPath := filepath.Join(n.networkConfigDir, network.Name+".json") + f, err := os.Create(confPath) + if err != nil { + return err + } + defer f.Close() + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + err = enc.Encode(network) + if err != nil { + return err + } + return nil +} + +func (n *netavarkNetwork) NetworkUpdate(name string, options types.NetworkUpdateOptions) error { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return err + } + network, err := n.getNetwork(name) + if err != nil { + return err + } + networkDNSServersBefore := network.NetworkDNSServers + networkDNSServersAfter := []string{} + for _, server := range networkDNSServersBefore { + if util.StringInSlice(server, options.RemoveDNSServers) { + continue + } + networkDNSServersAfter = append(networkDNSServersAfter, server) + } + networkDNSServersAfter = append(networkDNSServersAfter, options.AddDNSServers...) + networkDNSServersAfter = sliceRemoveDuplicates(networkDNSServersAfter) + network.NetworkDNSServers = networkDNSServersAfter + if reflect.DeepEqual(networkDNSServersBefore, networkDNSServersAfter) { + return nil + } + err = n.commitNetwork(network) + if err != nil { + return err + } + + return n.execUpdate(network.Name, network.NetworkDNSServers) +} + // NetworkCreate will take a partial filled Network and fill the // missing fields. It creates the Network and returns the full Network. -func (n *netavarkNetwork) NetworkCreate(net types.Network) (types.Network, error) { +func (n *netavarkNetwork) NetworkCreate(net types.Network, options *types.NetworkCreateOptions) (types.Network, error) { n.lock.Lock() defer n.lock.Unlock() err := n.loadNetworks() @@ -30,6 +90,11 @@ func (n *netavarkNetwork) NetworkCreate(net types.Network) (types.Network, error } network, err := n.networkCreate(&net, false) if err != nil { + if options != nil && options.IgnoreIfExists && errors.Is(err, types.ErrNetworkExists) { + if network, ok := n.networks[net.Name]; ok { + return *network, nil + } + } return types.Network{}, err } // add the new network to the map @@ -116,6 +181,11 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo } // rust only support "true" or "false" while go can parse 1 and 0 as well so we need to change it newNetwork.Options[types.IsolateOption] = strconv.FormatBool(val) + case types.MetricOption: + _, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unsupported bridge network option %s", key) } @@ -132,6 +202,17 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo // when we do not have ipam we must disable dns internalutil.IpamNoneDisableDNS(newNetwork) + // process NetworkDNSServers + if len(newNetwork.NetworkDNSServers) > 0 && !newNetwork.DNSEnabled { + return nil, fmt.Errorf("Cannot set NetworkDNSServers if DNS is not enabled for the network: %w", types.ErrInvalidArg) + } + // validate ip address + for _, dnsServer := range newNetwork.NetworkDNSServers { + if net.ParseIP(dnsServer) == nil { + return nil, fmt.Errorf("Unable to parse ip %s specified in NetworkDNSServers: %w", dnsServer, types.ErrInvalidArg) + } + } + // add gateway when not internal or dns enabled addGateway := !newNetwork.Internal || newNetwork.DNSEnabled err = internalutil.ValidateSubnets(newNetwork, addGateway, usedNetworks) @@ -142,15 +223,7 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo newNetwork.Created = time.Now() if !defaultNet { - confPath := filepath.Join(n.networkConfigDir, newNetwork.Name+".json") - f, err := os.Create(confPath) - if err != nil { - return nil, err - } - defer f.Close() - enc := json.NewEncoder(f) - enc.SetIndent("", " ") - err = enc.Encode(newNetwork) + err = n.commitNetwork(newNetwork) if err != nil { return nil, err } @@ -170,6 +243,11 @@ func createMacvlan(network *types.Network) error { } } + // always turn dns off with macvlan, it is not implemented in netavark + // and makes little sense to support with macvlan + // see https://github.com/containers/netavark/pull/467 + network.DNSEnabled = false + // we already validated the drivers before so we just have to set the default here switch network.IPAMOptions[types.Driver] { case "": diff --git a/vendor/github.com/containers/common/libnetwork/netavark/const.go b/vendor/github.com/containers/common/libnetwork/netavark/const.go index b375acd1..70c50b1a 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/const.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/const.go @@ -4,3 +4,5 @@ package netavark const defaultBridgeName = "podman" + +const defaultRootLockPath = "/run/lock/netavark.lock" diff --git a/vendor/github.com/containers/common/libnetwork/netavark/network.go b/vendor/github.com/containers/common/libnetwork/netavark/network.go index e3e2f7e5..ca57df30 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/network.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/network.go @@ -7,7 +7,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -95,8 +94,13 @@ type InitConfig struct { // NewNetworkInterface creates the ContainerNetwork interface for the netavark backend. // Note: The networks are not loaded from disk until a method is called. func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { - // TODO: consider using a shared memory lock - lock, err := lockfile.GetLockfile(filepath.Join(conf.NetworkConfigDir, "netavark.lock")) + // root needs to use a globally unique lock because there is only one host netns + lockPath := defaultRootLockPath + if unshare.IsRootless() { + lockPath = filepath.Join(conf.NetworkConfigDir, "netavark.lock") + } + + lock, err := lockfile.GetLockFile(lockPath) if err != nil { return nil, err } @@ -174,7 +178,7 @@ func (n *netavarkNetwork) loadNetworks() error { n.networks = nil n.modTime = modTime - files, err := ioutil.ReadDir(n.networkConfigDir) + files, err := os.ReadDir(n.networkConfigDir) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } diff --git a/vendor/github.com/containers/common/libnetwork/netavark/run.go b/vendor/github.com/containers/common/libnetwork/netavark/run.go index b364f42d..7732947c 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/run.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/run.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "strconv" + "strings" "github.com/containers/common/libnetwork/internal/util" "github.com/containers/common/libnetwork/types" @@ -18,6 +19,11 @@ type netavarkOptions struct { Networks map[string]*types.Network `json:"network_info"` } +func (n *netavarkNetwork) execUpdate(networkName string, networkDNSServers []string) error { + retErr := n.execNetavark([]string{"update", networkName, "--network-dns-servers", strings.Join(networkDNSServers, ",")}, nil, nil) + return retErr +} + // Setup will setup the container network namespace. It returns // a map of StatusBlocks, the key is the network name. func (n *netavarkNetwork) Setup(namespacePath string, options types.SetupOptions) (map[string]types.StatusBlock, error) { diff --git a/vendor/github.com/containers/common/libnetwork/network/interface.go b/vendor/github.com/containers/common/libnetwork/network/interface.go index 2093e104..752004de 100644 --- a/vendor/github.com/containers/common/libnetwork/network/interface.go +++ b/vendor/github.com/containers/common/libnetwork/network/interface.go @@ -6,7 +6,6 @@ package network import ( "errors" "fmt" - "io/ioutil" "os" "path/filepath" @@ -37,11 +36,12 @@ const ( // NetworkBackend returns the network backend name and interface // It returns either the CNI or netavark backend depending on what is set in the config. // If the the backend is set to "" we will automatically assign the backend on the following conditions: -// 1. read ${graphroot}/defaultNetworkBackend -// 2. find netavark binary (if not installed use CNI) -// 3. check containers, images and CNI networks and if there are some we have an existing install and should continue to use CNI +// 1. read ${graphroot}/defaultNetworkBackend +// 2. find netavark binary (if not installed use CNI) +// 3. check containers, images and CNI networks and if there are some we have an existing install and should continue to use CNI // // revive does not like the name because the package is already called network +// //nolint:revive func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (types.NetworkBackend, types.ContainerNetwork, error) { backend := types.NetworkBackend(conf.Network.NetworkBackend) @@ -100,7 +100,7 @@ func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (type func defaultNetworkBackend(store storage.Store, conf *config.Config) (backend types.NetworkBackend, err error) { // read defaultNetworkBackend file file := filepath.Join(store.GraphRoot(), defaultNetworkBackendFileName) - b, err := ioutil.ReadFile(file) + b, err := os.ReadFile(file) if err == nil { val := string(b) if val == string(types.Netavark) { diff --git a/vendor/github.com/containers/common/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/containers/common/libnetwork/resolvconf/resolvconf.go index 54b8c322..6b844373 100644 --- a/vendor/github.com/containers/common/libnetwork/resolvconf/resolvconf.go +++ b/vendor/github.com/containers/common/libnetwork/resolvconf/resolvconf.go @@ -5,9 +5,9 @@ package resolvconf import ( "bytes" "os" - "regexp" "strings" + "github.com/containers/storage/pkg/regexp" "github.com/sirupsen/logrus" ) @@ -32,20 +32,19 @@ var ( // ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range. ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` - localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`) - nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) - nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) - searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) - optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) + localhostNSRegexp = regexp.Delayed(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`) + nsIPv6Regexp = regexp.Delayed(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) + nsRegexp = regexp.Delayed(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) + searchRegexp = regexp.Delayed(`^\s*search\s*(([^\s]+\s*)*)$`) + optionsRegexp = regexp.Delayed(`^\s*options\s*(([^\s]+\s*)*)$`) ) // filterResolvDNS cleans up the config in resolvConf. It has two main jobs: -// 1. If a netns is enabled, it looks for localhost (127.*|::1) entries in the provided -// resolv.conf, removing local nameserver entries, and, if the resulting -// cleaned config has no defined nameservers left, adds default DNS entries -// 2. Given the caller provides the enable/disable state of IPv6, the filter -// code will remove all IPv6 nameservers if it is not enabled for containers -// +// 1. If a netns is enabled, it looks for localhost (127.*|::1) entries in the provided +// resolv.conf, removing local nameserver entries, and, if the resulting +// cleaned config has no defined nameservers left, adds default DNS entries +// 2. Given the caller provides the enable/disable state of IPv6, the filter +// code will remove all IPv6 nameservers if it is not enabled for containers func filterResolvDNS(resolvConf []byte, ipv6Enabled bool, netnsEnabled bool) []byte { // If we're using the host netns, we have nothing to do besides hash the file. if !netnsEnabled { diff --git a/vendor/github.com/containers/common/libnetwork/types/const.go b/vendor/github.com/containers/common/libnetwork/types/const.go index da8fa31c..e367f9ad 100644 --- a/vendor/github.com/containers/common/libnetwork/types/const.go +++ b/vendor/github.com/containers/common/libnetwork/types/const.go @@ -40,6 +40,7 @@ const ( MTUOption = "mtu" ModeOption = "mode" IsolateOption = "isolate" + MetricOption = "metric" ) type NetworkBackend string diff --git a/vendor/github.com/containers/common/libnetwork/types/network.go b/vendor/github.com/containers/common/libnetwork/types/network.go index 2e894899..b8804bf6 100644 --- a/vendor/github.com/containers/common/libnetwork/types/network.go +++ b/vendor/github.com/containers/common/libnetwork/types/network.go @@ -9,7 +9,9 @@ import ( type ContainerNetwork interface { // NetworkCreate will take a partial filled Network and fill the // missing fields. It creates the Network and returns the full Network. - NetworkCreate(Network) (Network, error) + NetworkCreate(Network, *NetworkCreateOptions) (Network, error) + // NetworkUpdate will take network name and ID and updates network DNS Servers. + NetworkUpdate(nameOrID string, options NetworkUpdateOptions) error // NetworkRemove will remove the Network with the given name or ID. NetworkRemove(nameOrID string) error // NetworkList will return all known Networks. Optionally you can @@ -54,8 +56,12 @@ type Network struct { // to public or other Networks. Internal bool `json:"internal"` // DNSEnabled is whether name resolution is active for container on - // this Network. + // this Network. Only supported with the bridge driver. DNSEnabled bool `json:"dns_enabled"` + // List of custom DNS server for podman's DNS resolver at network level, + // all the containers attached to this network will consider resolvers + // configured at network level. + NetworkDNSServers []string `json:"network_dns_servers,omitempty"` // Labels is a set of key-value labels that have been applied to the // Network. Labels map[string]string `json:"labels,omitempty"` @@ -66,6 +72,14 @@ type Network struct { IPAMOptions map[string]string `json:"ipam_options,omitempty"` } +// NetworkOptions for a given container. +type NetworkUpdateOptions struct { + // List of custom DNS server for podman's DNS resolver. + // Priority order will be kept as defined by user in the configuration. + AddDNSServers []string `json:"add_dns_servers,omitempty"` + RemoveDNSServers []string `json:"remove_dns_servers,omitempty"` +} + // IPNet is used as custom net.IPNet type to add Marshal/Unmarshal methods. type IPNet struct { net.IPNet @@ -226,6 +240,9 @@ type NetworkOptions struct { // Networks contains all networks with the PerNetworkOptions. // The map should contain at least one element. Networks map[string]PerNetworkOptions `json:"networks"` + // List of custom DNS server for podman's DNS resolver. + // Priority order will be kept as defined by user in the configuration. + DNSServers []string `json:"dns_servers,omitempty"` } // PortMapping is one or more ports that will be mapped into the container. @@ -282,3 +299,8 @@ type TeardownOptions struct { // FilterFunc can be passed to NetworkList to filter the networks. type FilterFunc func(Network) bool + +type NetworkCreateOptions struct { + // IgnoreIfExists if true, do not fail if the network already exists + IgnoreIfExists bool +} diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups.go index 4ec6b84d..7c9c4504 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups.go @@ -8,7 +8,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "math" "os" "path/filepath" @@ -143,7 +142,7 @@ func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) basePath := cgroupRoot + userSlice controllersFile = fmt.Sprintf("%s/cgroup.controllers", basePath) } - controllersFileBytes, err := ioutil.ReadFile(controllersFile) + controllersFileBytes, err := os.ReadFile(controllersFile) if err != nil { return nil, fmt.Errorf("failed while reading controllers for cgroup v2: %w", err) } @@ -294,7 +293,7 @@ func (c *CgroupControl) initialize() (err error) { } func readFileAsUint64(path string) (uint64, error) { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { return 0, err } @@ -310,7 +309,7 @@ func readFileAsUint64(path string) (uint64, error) { } func readFileByKeyAsUint64(path, key string) (uint64, error) { - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return 0, err } @@ -432,7 +431,7 @@ func (c *CgroupControl) CreateSystemdUnit(path string) error { // GetUserConnection returns an user connection to D-BUS func GetUserConnection(uid int) (*systemdDbus.Conn, error) { return systemdDbus.NewConnection(func() (*dbus.Conn, error) { - return dbusAuthConnection(uid, dbus.SessionBusPrivate) + return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup) }) } @@ -533,7 +532,7 @@ func (c *CgroupControl) AddPid(pid int) error { if c.cgroup2 { p := filepath.Join(cgroupRoot, c.path, "cgroup.procs") - if err := ioutil.WriteFile(p, pidString, 0o644); err != nil { + if err := os.WriteFile(p, pidString, 0o644); err != nil { return fmt.Errorf("write %s: %w", p, err) } return nil @@ -556,7 +555,7 @@ func (c *CgroupControl) AddPid(pid int) error { continue } p := filepath.Join(c.getCgroupv1Path(n), "tasks") - if err := ioutil.WriteFile(p, pidString, 0o644); err != nil { + if err := os.WriteFile(p, pidString, 0o644); err != nil { return fmt.Errorf("write %s: %w", p, err) } } diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go index 76397c29..03d85750 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go @@ -8,7 +8,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "math" "os" "path/filepath" @@ -96,7 +95,7 @@ func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) basePath := cgroupRoot + userSlice controllersFile = fmt.Sprintf("%s/cgroup.controllers", basePath) } - controllersFileBytes, err := ioutil.ReadFile(controllersFile) + controllersFileBytes, err := os.ReadFile(controllersFile) if err != nil { return nil, fmt.Errorf("failed while reading controllers for cgroup v2: %w", err) } @@ -247,7 +246,7 @@ func (c *CgroupControl) initialize() (err error) { } func readFileAsUint64(path string) (uint64, error) { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { return 0, err } @@ -263,7 +262,7 @@ func readFileAsUint64(path string) (uint64, error) { } func readFileByKeyAsUint64(path, key string) (uint64, error) { - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return 0, err } @@ -516,7 +515,7 @@ func (c *CgroupControl) AddPid(pid int) error { continue } p := filepath.Join(c.getCgroupv1Path(n), "tasks") - if err := ioutil.WriteFile(p, pidString, 0o644); err != nil { + if err := os.WriteFile(p, pidString, 0o644); err != nil { return fmt.Errorf("write %s: %w", p, err) } } diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go index 0d70e397..3a861223 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go @@ -7,7 +7,6 @@ import ( "bufio" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -99,12 +98,12 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) { func rmDirRecursively(path string) error { killProcesses := func(signal syscall.Signal) { if signal == unix.SIGKILL { - if err := ioutil.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil { + if err := os.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil { return } } // kill all the processes that are still part of the cgroup - if procs, err := ioutil.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil { + if procs, err := os.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil { for _, pidS := range strings.Split(string(procs), "\n") { if pid, err := strconv.Atoi(pidS); err == nil { _ = unix.Kill(pid, signal) @@ -116,7 +115,7 @@ func rmDirRecursively(path string) error { if err := os.Remove(path); err == nil || errors.Is(err, os.ErrNotExist) { return nil } - entries, err := ioutil.ReadDir(path) + entries, err := os.ReadDir(path) if err != nil { return err } diff --git a/vendor/github.com/containers/common/pkg/cgroups/pids.go b/vendor/github.com/containers/common/pkg/cgroups/pids.go index 1cb7ced8..76e983ea 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/pids.go +++ b/vendor/github.com/containers/common/pkg/cgroups/pids.go @@ -5,7 +5,7 @@ package cgroups import ( "fmt" - "io/ioutil" + "os" "path/filepath" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -31,7 +31,7 @@ func (c *pidHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error { } p := filepath.Join(PIDRoot, "pids.max") - return ioutil.WriteFile(p, []byte(fmt.Sprintf("%d\n", res.Pids.Limit)), 0o644) + return os.WriteFile(p, []byte(fmt.Sprintf("%d\n", res.Pids.Limit)), 0o644) } // Create the cgroup diff --git a/vendor/github.com/containers/common/pkg/cgroups/systemd.go b/vendor/github.com/containers/common/pkg/cgroups/systemd.go index 118fa97a..80a7bde2 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/systemd.go +++ b/vendor/github.com/containers/common/pkg/cgroups/systemd.go @@ -53,18 +53,20 @@ func systemdCreate(path string, c *systemdDbus.Conn) error { } /* - systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that - has the following license: - Copyright The containerd Authors. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that +has the following license: +Copyright The containerd Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ func systemdDestroyConn(path string, c *systemdDbus.Conn) error { name := filepath.Base(path) diff --git a/vendor/github.com/containers/common/pkg/cgroups/systemd_linux.go b/vendor/github.com/containers/common/pkg/cgroups/systemd_linux.go index 92b87fda..e8107604 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/systemd_linux.go +++ b/vendor/github.com/containers/common/pkg/cgroups/systemd_linux.go @@ -108,22 +108,22 @@ func systemdCreate(resources *configs.Resources, path string, c *systemdDbus.Con } /* - systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that - has the following license: +systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that +has the following license: - Copyright The containerd Authors. +Copyright The containerd Authors. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ func systemdDestroyConn(path string, c *systemdDbus.Conn) error { name := filepath.Base(path) diff --git a/vendor/github.com/containers/common/pkg/cgroups/utils.go b/vendor/github.com/containers/common/pkg/cgroups/utils.go index d4dda590..8ade6787 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/utils.go +++ b/vendor/github.com/containers/common/pkg/cgroups/utils.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -24,7 +23,7 @@ func readAcct(ctr *CgroupControl, name string) (uint64, error) { func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) { p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name) - data, err := ioutil.ReadFile(p) + data, err := os.ReadFile(p) if err != nil { return nil, err } @@ -54,7 +53,7 @@ func GetSystemCPUUsage() (uint64, error) { return readFileAsUint64(p) } - files, err := ioutil.ReadDir(cgroupRoot) + files, err := os.ReadDir(cgroupRoot) if err != nil { return 0, err } @@ -90,7 +89,7 @@ func cpusetCopyFileFromParent(dir, file string, cgroupv2 bool) ([]byte, error) { if cgroupv2 { parentPath = fmt.Sprintf("%s.effective", parentPath) } - data, err := ioutil.ReadFile(parentPath) + data, err := os.ReadFile(parentPath) if err != nil { // if the file doesn't exist, it is likely that the cpuset controller // is not enabled in the kernel. @@ -106,7 +105,7 @@ func cpusetCopyFileFromParent(dir, file string, cgroupv2 bool) ([]byte, error) { if err != nil { return nil, err } - if err := ioutil.WriteFile(path, data, 0o644); err != nil { + if err := os.WriteFile(path, data, 0o644); err != nil { return nil, fmt.Errorf("write %s: %w", path, err) } return data, nil @@ -126,7 +125,7 @@ func createCgroupv2Path(path string) (deferredError error) { if !strings.HasPrefix(path, cgroupRoot+"/") { return fmt.Errorf("invalid cgroup path %s", path) } - content, err := ioutil.ReadFile(cgroupRoot + "/cgroup.controllers") + content, err := os.ReadFile(cgroupRoot + "/cgroup.controllers") if err != nil { return err } @@ -154,7 +153,7 @@ func createCgroupv2Path(path string) (deferredError error) { // We enable the controllers for all the path components except the last one. It is not allowed to add // PIDs if there are already enabled controllers. if i < len(elements[3:])-1 { - if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), res, 0o755); err != nil { + if err := os.WriteFile(filepath.Join(current, "cgroup.subtree_control"), res, 0o755); err != nil { return err } } diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index cde7cec5..5e965402 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -194,6 +194,9 @@ type ContainersConfig struct { // performance implications. PrepareVolumeOnCreate bool `toml:"prepare_volume_on_create,omitempty"` + // ReadOnly causes engine to run all containers with root file system mounted read-only + ReadOnly bool `toml:"read_only,omitempty"` + // SeccompProfile is the seccomp.json profile path which is used as the // default for the runtime. SeccompProfile string `toml:"seccomp_profile,omitempty"` @@ -214,6 +217,7 @@ type ContainersConfig struct { UserNS string `toml:"userns,omitempty"` // UserNSSize how many UIDs to allocate for automatically created UserNS + // Deprecated: no user of this field is known. UserNSSize int `toml:"userns_size,omitempty,omitzero"` } @@ -272,6 +276,11 @@ type EngineConfig struct { // EventsLogger determines where events should be logged. EventsLogger string `toml:"events_logger,omitempty"` + // EventsContainerCreateInspectData creates a more verbose + // container-create event which includes a JSON payload with detailed + // information about the container. + EventsContainerCreateInspectData bool `toml:"events_container_create_inspect_data,omitempty"` + // graphRoot internal stores the location of the graphroot graphRoot string @@ -358,6 +367,9 @@ type EngineConfig struct { // OCIRuntimes are the set of configured OCI runtimes (default is runc). OCIRuntimes map[string][]string `toml:"runtimes,omitempty"` + // PlatformToOCIRuntime requests specific OCI runtime for a specified platform of image. + PlatformToOCIRuntime map[string]string `toml:"platform_to_oci_runtime,omitempty"` + // PodExitPolicy determines the behaviour when the last container of a pod exits. PodExitPolicy PodExitPolicy `toml:"pod_exit_policy,omitempty"` @@ -579,6 +591,7 @@ type SecretConfig struct { // ConfigMapConfig represents the "configmap" TOML config table // // revive does not like the name because the package is already called config +// //nolint:revive type ConfigMapConfig struct { // Driver specifies the configmap driver to use. @@ -618,6 +631,16 @@ type Destination struct { IsMachine bool `toml:"is_machine,omitempty"` } +// Consumes container image's os and arch and returns if any dedicated runtime was +// configured otherwise returns default runtime. +func (c *EngineConfig) ImagePlatformToRuntime(os string, arch string) string { + platformString := os + "/" + arch + if val, ok := c.PlatformToOCIRuntime[platformString]; ok { + return val + } + return c.OCIRuntime +} + // NewConfig creates a new Config. It starts with an empty config and, if // specified, merges the config at `userConfigPath` path. Depending if we're // running as root or rootless, we then merge the system configuration followed @@ -842,7 +865,7 @@ func (m *MachineConfig) URI() string { func (c *EngineConfig) findRuntime() string { // Search for crun first followed by runc, kata, runsc - for _, name := range []string{"crun", "runc", "runj", "kata", "runsc"} { + for _, name := range []string{"crun", "runc", "runj", "kata", "runsc", "ocijail"} { for _, v := range c.OCIRuntimes[name] { if _, err := os.Stat(v); err == nil { return name @@ -944,11 +967,10 @@ func (c *NetworkConfig) Validate() error { // to first (version) matching conmon binary. If non is found, we try // to do a path lookup of "conmon". func (c *Config) FindConmon() (string, error) { - return findConmonPath(c.Engine.ConmonPath, "conmon", _conmonMinMajorVersion, _conmonMinMinorVersion, _conmonMinPatchVersion) + return findConmonPath(c.Engine.ConmonPath, "conmon") } -func findConmonPath(paths []string, binaryName string, major int, minor int, patch int) (string, error) { - foundOutdatedConmon := false +func findConmonPath(paths []string, binaryName string) (string, error) { for _, path := range paths { stat, err := os.Stat(path) if err != nil { @@ -957,29 +979,14 @@ func findConmonPath(paths []string, binaryName string, major int, minor int, pat if stat.IsDir() { continue } - if err := probeConmon(path); err != nil { - logrus.Warnf("Conmon at %s invalid: %v", path, err) - foundOutdatedConmon = true - continue - } logrus.Debugf("Using conmon: %q", path) return path, nil } // Search the $PATH as last fallback if path, err := exec.LookPath(binaryName); err == nil { - if err := probeConmon(path); err != nil { - logrus.Warnf("Conmon at %s is invalid: %v", path, err) - foundOutdatedConmon = true - } else { - logrus.Debugf("Using conmon from $PATH: %q", path) - return path, nil - } - } - - if foundOutdatedConmon { - return "", fmt.Errorf("please update to v%d.%d.%d or later: %w", - major, minor, patch, ErrConmonOutdated) + logrus.Debugf("Using conmon from $PATH: %q", path) + return path, nil } return "", fmt.Errorf("could not find a working conmon binary (configured options: %v: %w)", @@ -990,7 +997,7 @@ func findConmonPath(paths []string, binaryName string, major int, minor int, pat // to first (version) matching conmonrs binary. If non is found, we try // to do a path lookup of "conmonrs". func (c *Config) FindConmonRs() (string, error) { - return findConmonPath(c.Engine.ConmonRsPath, "conmonrs", _conmonrsMinMajorVersion, _conmonrsMinMinorVersion, _conmonrsMinPatchVersion) + return findConmonPath(c.Engine.ConmonRsPath, "conmonrs") } // GetDefaultEnv returns the environment variables for the container. @@ -1037,10 +1044,11 @@ func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []s // Device parses device mapping string to a src, dest & permissions string // Valid values for device looklike: -// '/dev/sdc" -// '/dev/sdc:/dev/xvdc" -// '/dev/sdc:/dev/xvdc:rwm" -// '/dev/sdc:rm" +// +// '/dev/sdc" +// '/dev/sdc:/dev/xvdc" +// '/dev/sdc:/dev/xvdc:rwm" +// '/dev/sdc:rm" func Device(device string) (src, dst, permissions string, err error) { permissions = "rwm" split := strings.Split(device, ":") diff --git a/vendor/github.com/containers/common/pkg/config/config_darwin.go b/vendor/github.com/containers/common/pkg/config/config_darwin.go index 5283665e..13bd3a37 100644 --- a/vendor/github.com/containers/common/pkg/config/config_darwin.go +++ b/vendor/github.com/containers/common/pkg/config/config_darwin.go @@ -10,6 +10,10 @@ const ( // DefaultContainersConfig holds the default containers config path DefaultContainersConfig = "/usr/share/" + _configPath + + // DefaultSignaturePolicyPath is the default value for the + // policy.json file. + DefaultSignaturePolicyPath = "/etc/containers/policy.json" ) // podman remote clients on darwin cannot use unshare.isRootless() to determine the configuration file locations. diff --git a/vendor/github.com/containers/common/pkg/config/config_freebsd.go b/vendor/github.com/containers/common/pkg/config/config_freebsd.go index d6981235..903f0b47 100644 --- a/vendor/github.com/containers/common/pkg/config/config_freebsd.go +++ b/vendor/github.com/containers/common/pkg/config/config_freebsd.go @@ -10,6 +10,10 @@ const ( // DefaultContainersConfig holds the default containers config path DefaultContainersConfig = "/usr/local/share/" + _configPath + + // DefaultSignaturePolicyPath is the default value for the + // policy.json file. + DefaultSignaturePolicyPath = "/usr/local/etc/containers/policy.json" ) // podman remote clients on freebsd cannot use unshare.isRootless() to determine the configuration file locations. diff --git a/vendor/github.com/containers/common/pkg/config/config_linux.go b/vendor/github.com/containers/common/pkg/config/config_linux.go index 0f622bb7..4ce5d032 100644 --- a/vendor/github.com/containers/common/pkg/config/config_linux.go +++ b/vendor/github.com/containers/common/pkg/config/config_linux.go @@ -13,6 +13,10 @@ const ( // DefaultContainersConfig holds the default containers config path DefaultContainersConfig = "/usr/share/" + _configPath + + // DefaultSignaturePolicyPath is the default value for the + // policy.json file. + DefaultSignaturePolicyPath = "/etc/containers/policy.json" ) func selinuxEnabled() bool { diff --git a/vendor/github.com/containers/common/pkg/config/config_local.go b/vendor/github.com/containers/common/pkg/config/config_local.go index bc8ddc65..e101b062 100644 --- a/vendor/github.com/containers/common/pkg/config/config_local.go +++ b/vendor/github.com/containers/common/pkg/config/config_local.go @@ -11,6 +11,7 @@ import ( "strings" "syscall" + "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" units "github.com/docker/go-units" ) @@ -57,6 +58,9 @@ func (c *EngineConfig) validatePaths() error { func (c *ContainersConfig) validateDevices() error { for _, d := range c.Devices { + if cdi.IsQualifiedName(d) { + continue + } _, _, _, err := Device(d) if err != nil { return err diff --git a/vendor/github.com/containers/common/pkg/config/config_windows.go b/vendor/github.com/containers/common/pkg/config/config_windows.go index 6c9d5848..b5838072 100644 --- a/vendor/github.com/containers/common/pkg/config/config_windows.go +++ b/vendor/github.com/containers/common/pkg/config/config_windows.go @@ -8,6 +8,10 @@ const ( // DefaultContainersConfig holds the default containers config path DefaultContainersConfig = "/usr/share/" + _configPath + + // DefaultSignaturePolicyPath is the default value for the + // policy.json file. + DefaultSignaturePolicyPath = "/etc/containers/policy.json" ) // podman remote clients on windows cannot use unshare.isRootless() to determine the configuration file locations. diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index 5b5aaa00..83396173 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -52,19 +52,18 @@ # List of default capabilities for containers. If it is empty or commented out, # the default capabilities defined in the container engine will be added. # -default_capabilities = [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "FSETID", - "KILL", - "NET_BIND_SERVICE", - "SETFCAP", - "SETGID", - "SETPCAP", - "SETUID", - "SYS_CHROOT" -] +#default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FOWNER", +# "FSETID", +# "KILL", +# "NET_BIND_SERVICE", +# "SETFCAP", +# "SETGID", +# "SETPCAP", +# "SETUID", +#] # A list of sysctls to be set in containers by default, # specified as "name=value", @@ -216,6 +215,10 @@ default_sysctls = [ # #prepare_volume_on_create = false +# Run all containers with root file system mounted read-only +# +# read_only = false + # Path to the seccomp.json profile which is used as the default seccomp profile # for the runtime. # @@ -244,12 +247,6 @@ default_sysctls = [ # #userns = "host" -# Number of UIDs to allocate for the automatic container creation. -# UIDs are allocated from the "container" UIDs listed in -# /etc/subuid & /etc/subgid -# -#userns_size = 65536 - # Default way to to create a UTS namespace for the container # Options are: # `private` Create private UTS Namespace for the container. @@ -263,6 +260,11 @@ default_sysctls = [ # If it is empty or commented out, no volumes will be added # #volumes = [] +# +#[engine.platform_to_oci_runtime] +#"wasi/wasm" = ["crun-wasm"] +#"wasi/wasm32" = ["crun-wasm"] +#"wasi/wasm64" = ["crun-wasm"] [secrets] #driver = "file" @@ -411,6 +413,10 @@ default_sysctls = [ # #events_logger = "journald" +# Creates a more verbose container-create event which includes a JSON payload +# with detailed information about the container. +#events_container_create_inspect_data = false + # A is a list of directories which are used to search for helper binaries. # #helper_binaries_dir = [ @@ -543,7 +549,7 @@ default_sysctls = [ # List of the OCI runtimes that support --format=json. When json is supported # engine will use it for reporting nicer errors. # -#runtime_supports_json = ["crun", "runc", "kata", "runsc", "krun"] +#runtime_supports_json = ["crun", "runc", "kata", "runsc", "youki", "krun"] # List of the OCI runtimes that supports running containers with KVM Separation. # @@ -581,7 +587,7 @@ default_sysctls = [ # map of service destinations # -#[service_destinations] +# [service_destinations] # [service_destinations.production] # URI to access the Podman service # Examples: @@ -654,6 +660,13 @@ default_sysctls = [ # "/run/current-system/sw/bin/runsc", #] +#youki = [ +# "/usr/local/bin/youki", +# "/usr/bin/youki", +# "/bin/youki", +# "/run/current-system/sw/bin/youki", +#] + #krun = [ # "/usr/bin/krun", # "/usr/local/bin/krun", diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf-freebsd b/vendor/github.com/containers/common/pkg/config/containers.conf-freebsd index 50480fe7..0ac0f325 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf-freebsd +++ b/vendor/github.com/containers/common/pkg/config/containers.conf-freebsd @@ -212,12 +212,6 @@ default_sysctls = [ # #userns = "host" -# Number of UIDs to allocate for the automatic container creation. -# UIDs are allocated from the "container" UIDs listed in -# /etc/subuid & /etc/subgid -# -#userns_size = 65536 - # Default way to to create a UTS namespace for the container # Options are: # `private` Create private UTS Namespace for the container. @@ -485,7 +479,7 @@ default_sysctls = [ # List of the OCI runtimes that support --format=json. When json is supported # engine will use it for reporting nicer errors. # -#runtime_supports_json = ["crun", "runc", "kata", "runsc", "krun"] +#runtime_supports_json = ["crun", "runc", "kata", "runsc", "youki", "krun"] # List of the OCI runtimes that supports running containers with KVM Separation. # @@ -590,6 +584,13 @@ default_sysctls = [ # "/run/current-system/sw/bin/runsc", #] +#youki = [ +# "/usr/local/bin/youki", +# "/usr/bin/youki", +# "/bin/youki", +# "/run/current-system/sw/bin/youki", +#] + #krun = [ # "/usr/bin/krun", # "/usr/local/bin/krun", diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index c5fca7f0..e27f6306 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -1,15 +1,11 @@ package config import ( - "bytes" "errors" "fmt" "net" "os" - "os/exec" "path/filepath" - "regexp" - "strconv" "strings" nettypes "github.com/containers/common/libnetwork/types" @@ -24,28 +20,6 @@ import ( ) const ( - // _conmonMinMajorVersion is the major version required for conmon. - _conmonMinMajorVersion = 2 - - // _conmonMinMinorVersion is the minor version required for conmon. - _conmonMinMinorVersion = 0 - - // _conmonMinPatchVersion is the sub-minor version required for conmon. - _conmonMinPatchVersion = 1 - - // _conmonrsMinMajorVersion is the major version required for conmonrs. - _conmonrsMinMajorVersion = 0 - - // _conmonrsMinMinorVersion is the minor version required for conmonrs. - _conmonrsMinMinorVersion = 1 - - // _conmonrsMinPatchVersion is the sub-minor version required for conmonrs. - _conmonrsMinPatchVersion = 0 - - // _conmonVersionFormatErr is used when the expected versio-format of conmon - // has changed. - _conmonVersionFormatErr = "conmon version changed format: %w" - // _defaultGraphRoot points to the default path of the graph root. _defaultGraphRoot = "/var/lib/containers/storage" @@ -76,20 +50,16 @@ var ( DefaultHooksDirs = []string{"/usr/share/containers/oci/hooks.d"} // DefaultCapabilities is the default for the default_capabilities option in the containers.conf file. DefaultCapabilities = []string{ - "CAP_AUDIT_WRITE", "CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", - "CAP_MKNOD", "CAP_NET_BIND_SERVICE", - "CAP_NET_RAW", "CAP_SETFCAP", "CAP_SETGID", "CAP_SETPCAP", "CAP_SETUID", - "CAP_SYS_CHROOT", } // Search these locations in which CNIPlugins can be installed. @@ -149,9 +119,6 @@ const ( DefaultPidsLimit = 2048 // DefaultPullPolicy pulls the image if it does not exist locally. DefaultPullPolicy = "missing" - // DefaultSignaturePolicyPath is the default value for the - // policy.json file. - DefaultSignaturePolicyPath = "/etc/containers/policy.json" // DefaultSubnet is the subnet that will be used for the default // network. DefaultSubnet = "10.88.0.0/16" @@ -161,6 +128,7 @@ const ( // DefaultShmSize is the default upper limit on the size of tmpfs mounts. DefaultShmSize = "65536k" // DefaultUserNSSize indicates the default number of UIDs allocated for user namespace within a container. + // Deprecated: no user of this field is known. DefaultUserNSSize = 65536 // OCIBufSize limits maximum LogSizeMax. OCIBufSize = 8192 @@ -235,7 +203,7 @@ func DefaultConfig() (*Config, error) { TZ: "", Umask: "0022", UTSNS: "private", - UserNSSize: DefaultUserNSSize, + UserNSSize: DefaultUserNSSize, // Deprecated }, Network: NetworkConfig{ DefaultNetwork: "podman", @@ -332,6 +300,15 @@ func defaultConfigFromMemory() (*EngineConfig, error) { "/bin/crun", "/run/current-system/sw/bin/crun", }, + "crun-wasm": { + "/usr/bin/crun-wasm", + "/usr/sbin/crun-wasm", + "/usr/local/bin/crun-wasm", + "/usr/local/sbin/crun-wasm", + "/sbin/crun-wasm", + "/bin/crun-wasm", + "/run/current-system/sw/bin/crun-wasm", + }, "runc": { "/usr/bin/runc", "/usr/sbin/runc", @@ -364,10 +341,24 @@ func defaultConfigFromMemory() (*EngineConfig, error) { "/sbin/runsc", "/run/current-system/sw/bin/runsc", }, + "youki": { + "/usr/local/bin/youki", + "/usr/bin/youki", + "/bin/youki", + "/run/current-system/sw/bin/youki", + }, "krun": { "/usr/bin/krun", "/usr/local/bin/krun", }, + "ocijail": { + "/usr/local/bin/ocijail", + }, + } + c.PlatformToOCIRuntime = map[string]string{ + "wasi/wasm": "crun-wasm", + "wasi/wasm32": "crun-wasm", + "wasi/wasm64": "crun-wasm", } // Needs to be called after populating c.OCIRuntimes. c.OCIRuntime = c.findRuntime() @@ -401,6 +392,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) { "runc", "kata", "runsc", + "youki", "krun", } c.RuntimeSupportsNoCgroups = []string{"crun", "krun"} @@ -448,70 +440,6 @@ func defaultTmpDir() (string, error) { return filepath.Join(libpodRuntimeDir, "tmp"), nil } -// probeConmon calls conmon --version and verifies it is a new enough version for -// the runtime expectations the container engine currently has. -func probeConmon(conmonBinary string) error { - cmd := exec.Command(conmonBinary, "--version") - var out bytes.Buffer - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return err - } - r := regexp.MustCompile(`^(version:|conmon version)? (?P\d+).(?P\d+).(?P\d+)`) - - matches := r.FindStringSubmatch(out.String()) - if len(matches) != 5 { - return fmt.Errorf(_conmonVersionFormatErr, errors.New("invalid version format")) - } - major, err := strconv.Atoi(matches[2]) - - var minMajor, minMinor, minPatch int - // conmon-rs returns "^version:" - if matches[1] == "version:" { - minMajor = _conmonrsMinMajorVersion - minMinor = _conmonrsMinMinorVersion - minPatch = _conmonrsMinPatchVersion - } else { - minMajor = _conmonMinMajorVersion - minMinor = _conmonMinMinorVersion - minPatch = _conmonMinPatchVersion - } - - if err != nil { - return fmt.Errorf(_conmonVersionFormatErr, err) - } - if major < minMajor { - return ErrConmonOutdated - } - if major > minMajor { - return nil - } - - minor, err := strconv.Atoi(matches[3]) - if err != nil { - return fmt.Errorf(_conmonVersionFormatErr, err) - } - if minor < minMinor { - return ErrConmonOutdated - } - if minor > minMinor { - return nil - } - - patch, err := strconv.Atoi(matches[4]) - if err != nil { - return fmt.Errorf(_conmonVersionFormatErr, err) - } - if patch < minPatch { - return ErrConmonOutdated - } - if patch > minPatch { - return nil - } - - return nil -} - // NetNS returns the default network namespace. func (c *Config) NetNS() string { return c.Containers.NetNS @@ -685,4 +613,3 @@ func useUserConfigLocations() bool { // GetRootlessUID == -1 on Windows, so exclude negative range return unshare.GetRootlessUID() > 0 } - diff --git a/vendor/github.com/containers/common/pkg/config/default_darwin.go b/vendor/github.com/containers/common/pkg/config/default_darwin.go index 5d857df4..75576662 100644 --- a/vendor/github.com/containers/common/pkg/config/default_darwin.go +++ b/vendor/github.com/containers/common/pkg/config/default_darwin.go @@ -14,5 +14,9 @@ func getLibpodTmpDir() string { // getDefaultMachineVolumes returns default mounted volumes (possibly with env vars, which will be expanded) func getDefaultMachineVolumes() []string { - return []string{"$HOME:$HOME"} + return []string{ + "/Users:/Users", + "/private:/private", + "/var/folders:/var/folders", + } } diff --git a/vendor/github.com/containers/common/pkg/config/default_freebsd.go b/vendor/github.com/containers/common/pkg/config/default_freebsd.go index 781783b4..f3c999be 100644 --- a/vendor/github.com/containers/common/pkg/config/default_freebsd.go +++ b/vendor/github.com/containers/common/pkg/config/default_freebsd.go @@ -7,7 +7,7 @@ func getDefaultCgroupsMode() string { // In theory, FreeBSD should be able to use shm locks but in practice, // this causes cryptic error messages from the kernel that look like: // -// comm podman pid 90813: handling rb error 22 +// comm podman pid 90813: handling rb error 22 // // These seem to be related to fork/exec code paths. Fall back to // file-based locks. diff --git a/vendor/github.com/containers/common/pkg/config/default_linux.go b/vendor/github.com/containers/common/pkg/config/default_linux.go index 15052c10..d4d04764 100644 --- a/vendor/github.com/containers/common/pkg/config/default_linux.go +++ b/vendor/github.com/containers/common/pkg/config/default_linux.go @@ -2,7 +2,6 @@ package config import ( "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -37,7 +36,7 @@ func getDefaultProcessLimits() []string { rlim := unix.Rlimit{Cur: oldMaxSize, Max: oldMaxSize} oldrlim := rlim // Attempt to set file limit and process limit to pid_max in OS - dat, err := ioutil.ReadFile("/proc/sys/kernel/pid_max") + dat, err := os.ReadFile("/proc/sys/kernel/pid_max") if err == nil { val := strings.TrimSuffix(string(dat), "\n") max, err := strconv.ParseUint(val, 10, 64) diff --git a/vendor/github.com/containers/common/pkg/config/systemd.go b/vendor/github.com/containers/common/pkg/config/systemd.go index 03d19a12..3cd6ff84 100644 --- a/vendor/github.com/containers/common/pkg/config/systemd.go +++ b/vendor/github.com/containers/common/pkg/config/systemd.go @@ -4,7 +4,7 @@ package config import ( - "io/ioutil" + "os" "path/filepath" "strings" "sync" @@ -53,7 +53,7 @@ func defaultLogDriver() string { func useSystemd() bool { systemdOnce.Do(func() { - dat, err := ioutil.ReadFile("/proc/1/comm") + dat, err := os.ReadFile("/proc/1/comm") if err == nil { val := strings.TrimSuffix(string(dat), "\n") usesSystemd = (val == "systemd") @@ -68,13 +68,13 @@ func useJournald() bool { return } for _, root := range []string{"/run/log/journal", "/var/log/journal"} { - dirs, err := ioutil.ReadDir(root) + dirs, err := os.ReadDir(root) if err != nil { continue } for _, d := range dirs { if d.IsDir() { - if _, err := ioutil.ReadDir(filepath.Join(root, d.Name())); err == nil { + if _, err := os.ReadDir(filepath.Join(root, d.Name())); err == nil { usesJournald = true return } diff --git a/vendor/github.com/containers/common/pkg/download/download.go b/vendor/github.com/containers/common/pkg/download/download.go index abf4c877..777a1152 100644 --- a/vendor/github.com/containers/common/pkg/download/download.go +++ b/vendor/github.com/containers/common/pkg/download/download.go @@ -3,14 +3,14 @@ package download import ( "fmt" "io" - "io/ioutil" "net/http" + "os" ) // FromURL downloads the specified source to a file in tmpdir (OS defaults if // empty). func FromURL(tmpdir, source string) (string, error) { - tmp, err := ioutil.TempFile(tmpdir, "") + tmp, err := os.CreateTemp(tmpdir, "") if err != nil { return "", fmt.Errorf("creating temporary download file: %w", err) } diff --git a/vendor/github.com/containers/common/pkg/filters/filters.go b/vendor/github.com/containers/common/pkg/filters/filters.go index f8b0066e..729061db 100644 --- a/vendor/github.com/containers/common/pkg/filters/filters.go +++ b/vendor/github.com/containers/common/pkg/filters/filters.go @@ -38,6 +38,7 @@ func ComputeUntilTimestamp(filterValues []string) (time.Time, error) { // background. // // revive does not like the name because the package is already called filters +// //nolint:revive func FiltersFromRequest(r *http.Request) ([]string, error) { var ( diff --git a/vendor/github.com/containers/common/pkg/hooks/hooks.go b/vendor/github.com/containers/common/pkg/hooks/hooks.go index 6d3747e5..2c47f440 100644 --- a/vendor/github.com/containers/common/pkg/hooks/hooks.go +++ b/vendor/github.com/containers/common/pkg/hooks/hooks.go @@ -90,8 +90,8 @@ func (m *Manager) namedHooks() (hooks []*namedHook) { // extensionStageHooks. This takes precedence over their inclusion in // the OCI configuration. For example: // -// manager, err := New(ctx, []string{DefaultDir}, []string{"poststop"}) -// extensionStageHooks, err := manager.Hooks(config, annotations, hasBindMounts) +// manager, err := New(ctx, []string{DefaultDir}, []string{"poststop"}) +// extensionStageHooks, err := manager.Hooks(config, annotations, hasBindMounts) // // will have any matching post-stop hooks in extensionStageHooks and // will not insert them into config.Hooks.Poststop. diff --git a/vendor/github.com/containers/common/pkg/hooks/monitor.go b/vendor/github.com/containers/common/pkg/hooks/monitor.go index e9facf0d..11434c6e 100644 --- a/vendor/github.com/containers/common/pkg/hooks/monitor.go +++ b/vendor/github.com/containers/common/pkg/hooks/monitor.go @@ -15,16 +15,16 @@ import ( // first is written after the watchers are established and the second // when this function exits. The expected usage is: // -// ctx, cancel := context.WithCancel(context.Background()) -// sync := make(chan error, 2) -// go m.Monitor(ctx, sync) -// err := <-sync // block until writers are established -// if err != nil { -// return err // failed to establish watchers -// } -// // do stuff -// cancel() -// err = <-sync // block until monitor finishes +// ctx, cancel := context.WithCancel(context.Background()) +// sync := make(chan error, 2) +// go m.Monitor(ctx, sync) +// err := <-sync // block until writers are established +// if err != nil { +// return err // failed to establish watchers +// } +// // do stuff +// cancel() +// err = <-sync // block until monitor finishes func (m *Manager) Monitor(ctx context.Context, sync chan<- error) { watcher, err := fsnotify.NewWatcher() if err != nil { diff --git a/vendor/github.com/containers/common/pkg/hooks/read.go b/vendor/github.com/containers/common/pkg/hooks/read.go index 25cf7be9..e7d349f3 100644 --- a/vendor/github.com/containers/common/pkg/hooks/read.go +++ b/vendor/github.com/containers/common/pkg/hooks/read.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -31,7 +30,7 @@ func Read(path string, extensionStages []string) (*current.Hook, error) { if !strings.HasSuffix(path, ".json") { return nil, ErrNoJSONSuffix } - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return nil, err } @@ -64,7 +63,7 @@ func read(content []byte) (hook *current.Hook, err error) { // clobbering any previous entries with the same filenames. func ReadDir(path string, extensionStages []string, hooks map[string]*current.Hook) error { logrus.Debugf("reading hooks from %s", path) - files, err := ioutil.ReadDir(path) + files, err := os.ReadDir(path) if err != nil { return err } diff --git a/vendor/github.com/containers/common/pkg/machine/machine.go b/vendor/github.com/containers/common/pkg/machine/machine.go index 37e89a08..36428e58 100644 --- a/vendor/github.com/containers/common/pkg/machine/machine.go +++ b/vendor/github.com/containers/common/pkg/machine/machine.go @@ -10,6 +10,7 @@ import ( ) // TODO: change name to MachineMarker since package is already called machine +// //nolint:revive type MachineMarker struct { Enabled bool @@ -57,6 +58,7 @@ func IsPodmanMachine() bool { } // TODO: change name to HostType since package is already called machine +// //nolint:revive func MachineHostType() string { return GetMachineMarker().Type diff --git a/vendor/github.com/containers/common/pkg/netns/netns_linux.go b/vendor/github.com/containers/common/pkg/netns/netns_linux.go index b3459b21..883fdd95 100644 --- a/vendor/github.com/containers/common/pkg/netns/netns_linux.go +++ b/vendor/github.com/containers/common/pkg/netns/netns_linux.go @@ -179,14 +179,13 @@ func NewNSWithName(name string) (ns.NetNS, error) { return ns.GetNS(nsPath) } -// UnmountNS unmounts the NS held by the netns object -func UnmountNS(netns ns.NetNS) error { +// UnmountNS unmounts the given netns path +func UnmountNS(nsPath string) error { nsRunDir, err := GetNSRunDir() if err != nil { return err } - nsPath := netns.Path() // Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) if strings.HasPrefix(nsPath, nsRunDir) { if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil { diff --git a/vendor/github.com/containers/common/pkg/parse/parse.go b/vendor/github.com/containers/common/pkg/parse/parse.go index 15e93212..7629f584 100644 --- a/vendor/github.com/containers/common/pkg/parse/parse.go +++ b/vendor/github.com/containers/common/pkg/parse/parse.go @@ -103,10 +103,11 @@ func ValidateVolumeOpts(options []string) ([]string, error) { // Device parses device mapping string to a src, dest & permissions string // Valid values for device looklike: -// '/dev/sdc" -// '/dev/sdc:/dev/xvdc" -// '/dev/sdc:/dev/xvdc:rwm" -// '/dev/sdc:rm" +// +// '/dev/sdc" +// '/dev/sdc:/dev/xvdc" +// '/dev/sdc:/dev/xvdc:rwm" +// '/dev/sdc:rm" func Device(device string) (src, dest, permissions string, err error) { permissions = "rwm" arr := strings.Split(device, ":") diff --git a/vendor/github.com/containers/common/pkg/secrets/filedriver/filedriver.go b/vendor/github.com/containers/common/pkg/secrets/filedriver/filedriver.go index f1b7ef3f..b63516f6 100644 --- a/vendor/github.com/containers/common/pkg/secrets/filedriver/filedriver.go +++ b/vendor/github.com/containers/common/pkg/secrets/filedriver/filedriver.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "os" "path/filepath" "sort" @@ -39,7 +39,7 @@ func NewDriver(rootPath string) (*Driver, error) { return nil, err } - lock, err := lockfile.GetLockfile(filepath.Join(rootPath, "secretsdata.lock")) + lock, err := lockfile.GetLockFile(filepath.Join(rootPath, "secretsdata.lock")) if err != nil { return nil, err } @@ -96,7 +96,7 @@ func (d *Driver) Store(id string, data []byte) error { if err != nil { return err } - err = ioutil.WriteFile(d.secretsDataFilePath, marshalled, 0o600) + err = os.WriteFile(d.secretsDataFilePath, marshalled, 0o600) if err != nil { return err } @@ -120,7 +120,7 @@ func (d *Driver) Delete(id string) error { if err != nil { return err } - err = ioutil.WriteFile(d.secretsDataFilePath, marshalled, 0o600) + err = os.WriteFile(d.secretsDataFilePath, marshalled, 0o600) if err != nil { return err } @@ -145,7 +145,7 @@ func (d *Driver) getAllData() (map[string][]byte, error) { } defer file.Close() - byteValue, err := ioutil.ReadAll(file) + byteValue, err := io.ReadAll(file) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/common/pkg/secrets/passdriver/passdriver.go b/vendor/github.com/containers/common/pkg/secrets/passdriver/passdriver.go index 7a658c02..e7022b42 100644 --- a/vendor/github.com/containers/common/pkg/secrets/passdriver/passdriver.go +++ b/vendor/github.com/containers/common/pkg/secrets/passdriver/passdriver.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -60,7 +59,7 @@ func defaultDriverConfig() *driverConfig { continue } cfg.Root = path - bs, err := ioutil.ReadFile(filepath.Join(path, ".gpg-id")) + bs, err := os.ReadFile(filepath.Join(path, ".gpg-id")) if err != nil { continue } @@ -76,7 +75,7 @@ func (cfg *driverConfig) findGpgID() { path := cfg.Root for len(path) > 1 { if _, err := os.Stat(filepath.Join(path, ".gpg-id")); err == nil { - bs, err := ioutil.ReadFile(filepath.Join(path, ".gpg-id")) + bs, err := os.ReadFile(filepath.Join(path, ".gpg-id")) if err != nil { continue } @@ -106,7 +105,7 @@ func NewDriver(opts map[string]string) (*Driver, error) { // List returns all secret IDs func (d *Driver) List() (secrets []string, err error) { - files, err := ioutil.ReadDir(d.Root) + files, err := os.ReadDir(d.Root) if err != nil { return nil, fmt.Errorf("failed to read secret directory: %w", err) } @@ -168,7 +167,7 @@ func (d *Driver) gpg(ctx context.Context, in io.Reader, out io.Writer, args ...s cmd.Env = os.Environ() cmd.Stdin = in cmd.Stdout = out - cmd.Stderr = ioutil.Discard + cmd.Stderr = io.Discard return cmd.Run() } diff --git a/vendor/github.com/containers/common/pkg/secrets/secrets.go b/vendor/github.com/containers/common/pkg/secrets/secrets.go index 705da3dd..d90775fa 100644 --- a/vendor/github.com/containers/common/pkg/secrets/secrets.go +++ b/vendor/github.com/containers/common/pkg/secrets/secrets.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "strings" "time" @@ -13,6 +12,7 @@ import ( "github.com/containers/common/pkg/secrets/passdriver" "github.com/containers/common/pkg/secrets/shelldriver" "github.com/containers/storage/pkg/lockfile" + "github.com/containers/storage/pkg/regexp" "github.com/containers/storage/pkg/stringid" ) @@ -51,11 +51,12 @@ var secretsFile = "secrets.json" // secretNameRegexp matches valid secret names // Allowed: 64 [a-zA-Z0-9-_.] characters, and the start and end character must be [a-zA-Z0-9] -var secretNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]*$`) +var secretNameRegexp = regexp.Delayed(`^[a-zA-Z0-9][a-zA-Z0-9_.-]*$`) // SecretsManager holds information on handling secrets // // revive does not like the name because the package is already called secrets +// //nolint:revive type SecretsManager struct { // secretsPath is the path to the db file where secrets are stored @@ -90,6 +91,7 @@ type Secret struct { // Currently only the unencrypted filedriver is implemented. // // revive does not like the name because the package is already called secrets +// //nolint:revive type SecretsDriver interface { // List lists all secret ids in the secrets data store @@ -125,7 +127,7 @@ func NewManager(rootPath string) (*SecretsManager, error) { return nil, err } - lock, err := lockfile.GetLockfile(filepath.Join(rootPath, "secrets.lock")) + lock, err := lockfile.GetLockFile(filepath.Join(rootPath, "secrets.lock")) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/common/pkg/secrets/secretsdb.go b/vendor/github.com/containers/common/pkg/secrets/secretsdb.go index 91b0b7eb..cc86c450 100644 --- a/vendor/github.com/containers/common/pkg/secrets/secretsdb.go +++ b/vendor/github.com/containers/common/pkg/secrets/secretsdb.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "os" "strings" "time" @@ -51,7 +51,7 @@ func (s *SecretsManager) loadDB() error { return err } - byteValue, err := ioutil.ReadAll(file) + byteValue, err := io.ReadAll(file) if err != nil { return err } @@ -176,7 +176,7 @@ func (s *SecretsManager) store(entry *Secret) error { if err != nil { return err } - err = ioutil.WriteFile(s.secretsDBPath, marshalled, 0o600) + err = os.WriteFile(s.secretsDBPath, marshalled, 0o600) if err != nil { return err } @@ -202,7 +202,7 @@ func (s *SecretsManager) delete(nameOrID string) error { if err != nil { return err } - err = ioutil.WriteFile(s.secretsDBPath, marshalled, 0o600) + err = os.WriteFile(s.secretsDBPath, marshalled, 0o600) if err != nil { return err } diff --git a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go index 8ec3c45e..b9c46447 100644 --- a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go +++ b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "os/user" + "path" "path/filepath" "regexp" "strings" @@ -289,11 +290,24 @@ func ValidateAndConfigure(uri *url.URL, iden string, insecureIsMachineConnection keyFilePath := filepath.Join(homedir.Get(), ".ssh", "known_hosts") known, err := knownhosts.New(keyFilePath) if err != nil { - if errors.Is(err, os.ErrNotExist) { - logrus.Warn("please create a known_hosts file. The next time this host is connected to, podman will add it to known_hosts") - return nil + if !errors.Is(err, os.ErrNotExist) { + return err + } + keyDir := path.Dir(keyFilePath) + if _, err := os.Stat(keyDir); errors.Is(err, os.ErrNotExist) { + if err := os.Mkdir(keyDir, 0o700); err != nil { + return err + } + } + k, err := os.OpenFile(keyFilePath, os.O_RDWR|os.O_CREATE, 0o600) + if err != nil { + return err + } + k.Close() + known, err = knownhosts.New(keyFilePath) + if err != nil { + return err } - return err } // we need to check if there is an error from reading known hosts for this public key and if there is an error, what is it, and why is it happening? // if it is a key mismatch we want to error since we know the host using another key diff --git a/vendor/github.com/containers/common/pkg/ssh/utils.go b/vendor/github.com/containers/common/pkg/ssh/utils.go index b05105d9..d2b7d4a0 100644 --- a/vendor/github.com/containers/common/pkg/ssh/utils.go +++ b/vendor/github.com/containers/common/pkg/ssh/utils.go @@ -3,7 +3,6 @@ package ssh import ( "fmt" "io" - "io/ioutil" "net" "net/url" "os" @@ -106,7 +105,7 @@ func ReadPassword(prompt string) (pw []byte, err error) { } func PublicKey(path string, passphrase []byte) (ssh.Signer, error) { - key, err := ioutil.ReadFile(path) + key, err := os.ReadFile(path) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go index 6454f16b..cdece0a1 100644 --- a/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go +++ b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go @@ -4,7 +4,6 @@ import ( "bufio" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -42,7 +41,7 @@ func (s subscriptionData) saveTo(dir string) error { if err := os.MkdirAll(filepath.Dir(path), s.dirMode); err != nil { return err } - return ioutil.WriteFile(path, s.data, s.mode) + return os.WriteFile(path, s.data, s.mode) } func readAll(root, prefix string, parentMode os.FileMode) ([]subscriptionData, error) { @@ -50,7 +49,7 @@ func readAll(root, prefix string, parentMode os.FileMode) ([]subscriptionData, e data := []subscriptionData{} - files, err := ioutil.ReadDir(path) + files, err := os.ReadDir(path) if err != nil { if errors.Is(err, os.ErrNotExist) { return data, nil @@ -90,7 +89,7 @@ func readFileOrDir(root, name string, parentMode os.FileMode) ([]subscriptionDat } return dirData, nil } - bytes, err := ioutil.ReadFile(path) + bytes, err := os.ReadFile(path) if err != nil { return nil, err } @@ -153,7 +152,9 @@ func getMountsMap(path string) (string, string, error) { //nolint // containerRunDir: Private data for storing subscriptions on the host mounted in container. // mountFile: Additional mount points required for the container. // mountPoint: Container image mountpoint, or the directory from the hosts perspective that -// corresponds to `/` in the container. +// +// corresponds to `/` in the container. +// // uid: to assign to content created for subscriptions // gid: to assign to content created for subscriptions // rootless: indicates whether container is running in rootless mode @@ -268,7 +269,7 @@ func addSubscriptionsFromMountsFile(filePath, mountLabel, containerRunDir string if err := os.MkdirAll(filepath.Dir(ctrDirOrFileOnHost), s.dirMode); err != nil { return nil, err } - if err := ioutil.WriteFile(ctrDirOrFileOnHost, s.data, s.mode); err != nil { + if err := os.WriteFile(ctrDirOrFileOnHost, s.data, s.mode); err != nil { return nil, fmt.Errorf("saving data to container filesystem: %w", err) } } @@ -305,10 +306,10 @@ func addSubscriptionsFromMountsFile(filePath, mountLabel, containerRunDir string // (i.e: be FIPs compliant). // It should only be called if /etc/system-fips exists on host. // It primarily does two things: -// - creates /run/secrets/system-fips in the container root filesystem, and adds it to the `mounts` slice. -// - If `/etc/crypto-policies/back-ends` already exists inside of the container, it creates -// `/usr/share/crypto-policies/back-ends/FIPS` inside the container as well. -// It is done from within the container to ensure to avoid policy incompatibility between the container and host. +// - creates /run/secrets/system-fips in the container root filesystem, and adds it to the `mounts` slice. +// - If `/etc/crypto-policies/back-ends` already exists inside of the container, it creates +// `/usr/share/crypto-policies/back-ends/FIPS` inside the container as well. +// It is done from within the container to ensure to avoid policy incompatibility between the container and host. func addFIPSModeSubscription(mounts *[]rspec.Mount, containerRunDir, mountPoint, mountLabel string, uid, gid int) error { subscriptionsDir := "/run/secrets" ctrDirOnHost := filepath.Join(containerRunDir, subscriptionsDir) diff --git a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go index 39cc5beb..81986e94 100644 --- a/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go +++ b/vendor/github.com/containers/common/pkg/sysinfo/sysinfo_linux.go @@ -3,7 +3,6 @@ package sysinfo import ( "errors" "fmt" - "io/ioutil" "os" "path" "strings" @@ -210,12 +209,12 @@ func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetI return cgroupCpusetInfo{} } - cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus")) + cpus, err := os.ReadFile(path.Join(mountPoint, "cpuset.cpus")) if err != nil { return cgroupCpusetInfo{} } - mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems")) + mems, err := os.ReadFile(path.Join(mountPoint, "cpuset.mems")) if err != nil { return cgroupCpusetInfo{} } @@ -255,7 +254,7 @@ func cgroupEnabled(mountPoint, name string) bool { } func readProcBool(file string) bool { - val, err := ioutil.ReadFile(file) + val, err := os.ReadFile(file) if err != nil { return false } diff --git a/vendor/github.com/containers/common/pkg/timetype/timestamp.go b/vendor/github.com/containers/common/pkg/timetype/timestamp.go index 3cbfe409..519884c5 100644 --- a/vendor/github.com/containers/common/pkg/timetype/timestamp.go +++ b/vendor/github.com/containers/common/pkg/timetype/timestamp.go @@ -103,8 +103,10 @@ func GetTimestamp(value string, reference time.Time) (string, error) { // if the incoming nanosecond portion is longer or shorter than 9 digits it is // converted to nanoseconds. The expectation is that the seconds and // seconds will be used to create a time variable. For example: -// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) -// if err == nil since := time.Unix(seconds, nanoseconds) +// +// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) +// if err == nil since := time.Unix(seconds, nanoseconds) +// // returns seconds as def(aultSeconds) if value == "" func ParseTimestamps(value string, def int64) (secs, nanoSecs int64, err error) { if value == "" { diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 297966b7..44362e6a 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.50.1" +const Version = "0.51.0" diff --git a/vendor/github.com/containers/image/v5/copy/compression.go b/vendor/github.com/containers/image/v5/copy/compression.go index ff0e7945..5286975b 100644 --- a/vendor/github.com/containers/image/v5/copy/compression.go +++ b/vendor/github.com/containers/image/v5/copy/compression.go @@ -238,7 +238,7 @@ func (d *bpCompressionStepData) updateCompressionEdits(operation *types.LayerCom } // recordValidatedBlobData updates b.blobInfoCache with data about the created uploadedInfo adnd the original srcInfo. -// This must ONLY be called if all data has been validated by OUR code, and is not comming from third parties. +// This must ONLY be called if all data has been validated by OUR code, and is not coming from third parties. func (d *bpCompressionStepData) recordValidatedDigestData(c *copier, uploadedInfo types.BlobInfo, srcInfo types.BlobInfo, encryptionStep *bpEncryptionStepData, decryptionStep *bpDecryptionStepData) error { // Don’t record any associations that involve encrypted data. This is a bit crude, diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 6758d4de..26521fe0 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -24,6 +24,7 @@ import ( "github.com/containers/image/v5/pkg/compression" compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/signature/signer" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" encconfig "github.com/containers/ocicrypt/config" @@ -61,6 +62,7 @@ var expectedCompressionFormats = map[string]*compressiontypes.Algorithm{ // copier allows us to keep track of diffID values for blobs, and other // data shared across one or more images in a possible manifest list. +// The owner must call close() when done. type copier struct { dest private.ImageDestination rawSource private.ImageSource @@ -75,6 +77,8 @@ type copier struct { ociEncryptConfig *encconfig.EncryptConfig concurrentBlobCopiesSemaphore *semaphore.Weighted // Limits the amount of concurrently copied blobs downloadForeignLayers bool + signers []*signer.Signer // Signers to use to create new signatures for the image + signersToClose []*signer.Signer // Signers that should be closed when this copier is destroyed. } // imageCopier tracks state specific to a single image (possibly an item of a manifest list) @@ -121,17 +125,21 @@ type ImageListSelection int // Options allows supplying non-default configuration modifying the behavior of CopyImage. type Options struct { - RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature. + RemoveSignatures bool // Remove any pre-existing signatures. Signers and SignBy… will still add a new signature. + // Signers to use to add signatures during the copy. + // Callers are still responsible for closing these Signer objects; they can be reused for multiple copy.Image operations in a row. + Signers []*signer.Signer SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(), - SignPassphrase string // Passphare to use when signing with the key ID from `SignBy`. + SignPassphrase string // Passphrase to use when signing with the key ID from `SignBy`. SignBySigstorePrivateKeyFile string // If non-empty, asks for a signature to be added during the copy, using a sigstore private key file at the provided path. - SignSigstorePrivateKeyPassphrase []byte // Passphare to use when signing with `SignBySigstorePrivateKeyFile`. + SignSigstorePrivateKeyPassphrase []byte // Passphrase to use when signing with `SignBySigstorePrivateKeyFile`. SignIdentity reference.Named // Identify to use when signing, defaults to the docker reference of the destination - ReportWriter io.Writer - SourceCtx *types.SystemContext - DestinationCtx *types.SystemContext - ProgressInterval time.Duration // time to wait between reports to signal the progress channel - Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset. + + ReportWriter io.Writer + SourceCtx *types.SystemContext + DestinationCtx *types.SystemContext + ProgressInterval time.Duration // time to wait between reports to signal the progress channel + Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset. // Preserve digests, and fail if we cannot. PreserveDigests bool @@ -257,6 +265,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, ociEncryptConfig: options.OciEncryptConfig, downloadForeignLayers: options.DownloadForeignLayers, } + defer c.close() // Set the concurrentBlobCopiesSemaphore if we can copy layers in parallel. if dest.HasThreadSafePutBlob() && rawSource.HasThreadSafeGetBlob() { @@ -284,6 +293,10 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, c.compressionLevel = options.DestinationCtx.CompressionLevel } + if err := c.setupSigners(options); err != nil { + return nil, err + } + unparsedToplevel := image.UnparsedInstance(rawSource, nil) multiImage, err := isMultiImage(ctx, unparsedToplevel) if err != nil { @@ -340,6 +353,15 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, return copiedManifest, nil } +// close tears down state owned by copier. +func (c *copier) close() { + for i, s := range c.signersToClose { + if err := s.Close(); err != nil { + logrus.Warnf("Error closing per-copy signer %d: %v", i+1, err) + } + } +} + // Checks if the destination supports accepting multiple images by checking if it can support // manifest types that are lists of other manifests. func supportsMultipleImages(dest types.ImageDestination) bool { @@ -564,20 +586,11 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur } // Sign the manifest list. - if options.SignBy != "" { - newSig, err := c.createSignature(manifestList, options.SignBy, options.SignPassphrase, options.SignIdentity) - if err != nil { - return nil, err - } - sigs = append(sigs, newSig) - } - if options.SignBySigstorePrivateKeyFile != "" { - newSig, err := c.createSigstoreSignature(manifestList, options.SignBySigstorePrivateKeyFile, options.SignSigstorePrivateKeyPassphrase, options.SignIdentity) - if err != nil { - return nil, err - } - sigs = append(sigs, newSig) + newSigs, err := c.createSignatures(ctx, manifestList, options.SignIdentity) + if err != nil { + return nil, err } + sigs = append(sigs, newSigs...) c.Printf("Storing list signatures\n") if err := c.dest.PutSignaturesWithFormat(ctx, sigs, nil); err != nil { @@ -675,12 +688,12 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli // Decide whether we can substitute blobs with semantic equivalents: // - Don’t do that if we can’t modify the manifest at all // - Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it. - // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path: + // This may be too conservative, but for now, better safe than sorry, _especially_ on the len(c.signers) != 0 path: // The signature makes the content non-repudiable, so it very much matters that the signature is made over exactly what the user intended. // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk // that the compressed version coming from a third party may be designed to attack some other decompressor implementation, // and we would reuse and sign it. - ic.canSubstituteBlobs = ic.cannotModifyManifestReason == "" && options.SignBy == "" && options.SignBySigstorePrivateKeyFile == "" + ic.canSubstituteBlobs = ic.cannotModifyManifestReason == "" && len(c.signers) == 0 if err := ic.updateEmbeddedDockerReference(); err != nil { return nil, "", "", err @@ -711,7 +724,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli // If enabled, fetch and compare the destination's manifest. And as an optimization skip updating the destination iff equal if options.OptimizeDestinationImageAlreadyExists { - shouldUpdateSigs := len(sigs) > 0 || options.SignBy != "" || options.SignBySigstorePrivateKeyFile != "" // TODO: Consider allowing signatures updates only and skipping the image's layers/manifest copy if possible + shouldUpdateSigs := len(sigs) > 0 || len(c.signers) != 0 // TODO: Consider allowing signatures updates only and skipping the image's layers/manifest copy if possible noPendingManifestUpdates := ic.noPendingManifestUpdates() logrus.Debugf("Checking if we can skip copying: has signatures=%t, OCI encryption=%t, no manifest updates=%t", shouldUpdateSigs, destRequiresOciEncryption, noPendingManifestUpdates) @@ -791,20 +804,11 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli targetInstance = &retManifestDigest } - if options.SignBy != "" { - newSig, err := c.createSignature(manifestBytes, options.SignBy, options.SignPassphrase, options.SignIdentity) - if err != nil { - return nil, "", "", err - } - sigs = append(sigs, newSig) - } - if options.SignBySigstorePrivateKeyFile != "" { - newSig, err := c.createSigstoreSignature(manifestBytes, options.SignBySigstorePrivateKeyFile, options.SignSigstorePrivateKeyPassphrase, options.SignIdentity) - if err != nil { - return nil, "", "", err - } - sigs = append(sigs, newSig) + newSigs, err := c.createSignatures(ctx, manifestBytes, options.SignIdentity) + if err != nil { + return nil, "", "", err } + sigs = append(sigs, newSigs...) c.Printf("Storing signatures\n") if err := c.dest.PutSignaturesWithFormat(ctx, sigs, targetInstance); err != nil { diff --git a/vendor/github.com/containers/image/v5/copy/sign.go b/vendor/github.com/containers/image/v5/copy/sign.go index 6c3d9d62..fd19e18c 100644 --- a/vendor/github.com/containers/image/v5/copy/sign.go +++ b/vendor/github.com/containers/image/v5/copy/sign.go @@ -7,11 +7,49 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/private" internalsig "github.com/containers/image/v5/internal/signature" - "github.com/containers/image/v5/signature" + internalSigner "github.com/containers/image/v5/internal/signer" "github.com/containers/image/v5/signature/sigstore" + "github.com/containers/image/v5/signature/simplesigning" "github.com/containers/image/v5/transports" ) +// setupSigners initializes c.signers based on options. +func (c *copier) setupSigners(options *Options) error { + c.signers = append(c.signers, options.Signers...) + // c.signersToClose is intentionally not updated with options.Signers. + + // We immediately append created signers to c.signers, and we rely on c.close() to clean them up; so we don’t need + // to clean up any created signers on failure. + + if options.SignBy != "" { + opts := []simplesigning.Option{ + simplesigning.WithKeyFingerprint(options.SignBy), + } + if options.SignPassphrase != "" { + opts = append(opts, simplesigning.WithPassphrase(options.SignPassphrase)) + } + signer, err := simplesigning.NewSigner(opts...) + if err != nil { + return err + } + c.signers = append(c.signers, signer) + c.signersToClose = append(c.signersToClose, signer) + } + + if options.SignBySigstorePrivateKeyFile != "" { + signer, err := sigstore.NewSigner( + sigstore.WithPrivateKeyFile(options.SignBySigstorePrivateKeyFile, options.SignSigstorePrivateKeyPassphrase), + ) + if err != nil { + return err + } + c.signers = append(c.signers, signer) + c.signersToClose = append(c.signersToClose, signer) + } + + return nil +} + // sourceSignatures returns signatures from unparsedSource based on options, // and verifies that they can be used (to avoid copying a large image when we // can tell in advance that it would ultimately fail) @@ -37,20 +75,16 @@ func (c *copier) sourceSignatures(ctx context.Context, unparsed private.Unparsed return sigs, nil } -// createSignature creates a new signature of manifest using keyIdentity. -func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string, identity reference.Named) (internalsig.Signature, error) { - mech, err := signature.NewGPGSigningMechanism() - if err != nil { - return nil, fmt.Errorf("initializing GPG: %w", err) - } - defer mech.Close() - if err := mech.SupportsSigning(); err != nil { - return nil, fmt.Errorf("Signing not supported: %w", err) +// createSignatures creates signatures for manifest and an optional identity. +func (c *copier) createSignatures(ctx context.Context, manifest []byte, identity reference.Named) ([]internalsig.Signature, error) { + if len(c.signers) == 0 { + // We must exit early here, otherwise copies with no Docker reference wouldn’t be possible. + return nil, nil } if identity != nil { if reference.IsNameOnly(identity) { - return nil, fmt.Errorf("Sign identity must be a fully specified reference %s", identity) + return nil, fmt.Errorf("Sign identity must be a fully specified reference %s", identity.String()) } } else { identity = c.dest.Reference().DockerReference() @@ -59,31 +93,23 @@ func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase } } - c.Printf("Signing manifest using simple signing\n") - newSig, err := signature.SignDockerManifestWithOptions(manifest, identity.String(), mech, keyIdentity, &signature.SignOptions{Passphrase: passphrase}) - if err != nil { - return nil, fmt.Errorf("creating signature: %w", err) - } - return internalsig.SimpleSigningFromBlob(newSig), nil -} - -// createSigstoreSignature creates a new sigstore signature of manifest using privateKeyFile and identity. -func (c *copier) createSigstoreSignature(manifest []byte, privateKeyFile string, passphrase []byte, identity reference.Named) (internalsig.Signature, error) { - if identity != nil { - if reference.IsNameOnly(identity) { - return nil, fmt.Errorf("Sign identity must be a fully specified reference %s", identity.String()) + res := make([]internalsig.Signature, 0, len(c.signers)) + for signerIndex, signer := range c.signers { + msg := internalSigner.ProgressMessage(signer) + if len(c.signers) == 1 { + c.Printf("Creating signature: %s\n", msg) + } else { + c.Printf("Creating signature %d: %s\n", signerIndex+1, msg) } - } else { - identity = c.dest.Reference().DockerReference() - if identity == nil { - return nil, fmt.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(c.dest.Reference())) + newSig, err := internalSigner.SignImageManifest(ctx, signer, manifest, identity) + if err != nil { + if len(c.signers) == 1 { + return nil, fmt.Errorf("creating signature: %w", err) + } else { + return nil, fmt.Errorf("creating signature %d: %w", signerIndex, err) + } } + res = append(res, newSig) } - - c.Printf("Signing manifest using a sigstore signature\n") - newSig, err := sigstore.SignDockerManifestWithPrivateKeyFileUnstable(manifest, identity, privateKeyFile, passphrase) - if err != nil { - return nil, fmt.Errorf("creating signature: %w", err) - } - return newSig, nil + return res, nil } diff --git a/vendor/github.com/containers/image/v5/directory/directory_dest.go b/vendor/github.com/containers/image/v5/directory/directory_dest.go index 47a5c17c..55b29fe1 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_dest.go +++ b/vendor/github.com/containers/image/v5/directory/directory_dest.go @@ -105,7 +105,7 @@ func newImageDestination(sys *types.SystemContext, ref dirReference) (private.Im AcceptsForeignLayerURLs: false, MustMatchRuntimeOS: false, IgnoresEmbeddedDockerReference: false, // N/A, DockerReference() returns nil. - HasThreadSafePutBlob: false, + HasThreadSafePutBlob: true, }), NoPutBlobPartialInitialize: stubs.NoPutBlobPartial(ref), diff --git a/vendor/github.com/containers/image/v5/docker/archive/writer.go b/vendor/github.com/containers/image/v5/docker/archive/writer.go index aa8cdd60..315c282c 100644 --- a/vendor/github.com/containers/image/v5/docker/archive/writer.go +++ b/vendor/github.com/containers/image/v5/docker/archive/writer.go @@ -64,7 +64,7 @@ func NewWriter(sys *types.SystemContext, path string) (*Writer, error) { }, nil } -// imageCommitted notifies the Writer that at least one image was successfully commited to the stream. +// imageCommitted notifies the Writer that at least one image was successfully committed to the stream. func (w *Writer) imageCommitted() { w.mutex.Lock() defer w.mutex.Unlock() diff --git a/vendor/github.com/containers/image/v5/docker/body_reader.go b/vendor/github.com/containers/image/v5/docker/body_reader.go new file mode 100644 index 00000000..f958e80e --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/body_reader.go @@ -0,0 +1,234 @@ +package docker + +import ( + "context" + "errors" + "fmt" + "io" + "math" + "math/rand" + "net/http" + "net/url" + "strconv" + "strings" + "syscall" + "time" + + "github.com/sirupsen/logrus" +) + +// bodyReaderMinimumProgress is the minimum progress we want to see before we retry +const bodyReaderMinimumProgress = 1 * 1024 * 1024 + +// bodyReader is an io.ReadCloser returned by dockerImageSource.GetBlob, +// which can transparently resume some (very limited) kinds of aborted connections. +type bodyReader struct { + ctx context.Context + c *dockerClient + + path string // path to pass to makeRequest to retry + logURL *url.URL // a string to use in error messages + body io.ReadCloser // The currently open connection we use to read data, or nil if there is nothing to read from / close. + lastRetryOffset int64 + offset int64 // Current offset within the blob + firstConnectionTime time.Time + lastSuccessTime time.Time // time.Time{} if N/A +} + +// newBodyReader creates a bodyReader for request path in c. +// firstBody is an already correctly opened body for the blob, returing the full blob from the start. +// If reading from firstBody fails, bodyReader may heuristically decide to resume. +func newBodyReader(ctx context.Context, c *dockerClient, path string, firstBody io.ReadCloser) (io.ReadCloser, error) { + logURL, err := c.resolveRequestURL(path) + if err != nil { + return nil, err + } + res := &bodyReader{ + ctx: ctx, + c: c, + + path: path, + logURL: logURL, + body: firstBody, + lastRetryOffset: 0, + offset: 0, + firstConnectionTime: time.Now(), + } + return res, nil +} + +// parseDecimalInString ensures that s[start:] starts with a non-negative decimal number, and returns that number and the offset after the number. +func parseDecimalInString(s string, start int) (int64, int, error) { + i := start + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + if i == start { + return -1, -1, errors.New("missing decimal number") + } + v, err := strconv.ParseInt(s[start:i], 10, 64) + if err != nil { + return -1, -1, fmt.Errorf("parsing number: %w", err) + } + return v, i, nil +} + +// parseExpectedChar ensures that s[pos] is the expected byte, and returns the offset after it. +func parseExpectedChar(s string, pos int, expected byte) (int, error) { + if pos == len(s) || s[pos] != expected { + return -1, fmt.Errorf("missing expected %q", expected) + } + return pos + 1, nil +} + +// parseContentRange ensures that res contains a Content-Range header with a byte range, and returns (first, last, completeLength) on success. Size can be -1. +func parseContentRange(res *http.Response) (int64, int64, int64, error) { + hdrs := res.Header.Values("Content-Range") + switch len(hdrs) { + case 0: + return -1, -1, -1, errors.New("missing Content-Range: header") + case 1: + break + default: + return -1, -1, -1, fmt.Errorf("ambiguous Content-Range:, %d header values", len(hdrs)) + } + hdr := hdrs[0] + expectedPrefix := "bytes " + if !strings.HasPrefix(hdr, expectedPrefix) { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q, missing prefix %q", hdr, expectedPrefix) + } + first, pos, err := parseDecimalInString(hdr, len(expectedPrefix)) + if err != nil { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q, parsing first-pos: %w", hdr, err) + } + pos, err = parseExpectedChar(hdr, pos, '-') + if err != nil { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q: %w", hdr, err) + } + last, pos, err := parseDecimalInString(hdr, pos) + if err != nil { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q, parsing last-pos: %w", hdr, err) + } + pos, err = parseExpectedChar(hdr, pos, '/') + if err != nil { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q: %w", hdr, err) + } + completeLength := int64(-1) + if pos < len(hdr) && hdr[pos] == '*' { + pos++ + } else { + completeLength, pos, err = parseDecimalInString(hdr, pos) + if err != nil { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q, parsing complete-length: %w", hdr, err) + } + } + if pos < len(hdr) { + return -1, -1, -1, fmt.Errorf("invalid Content-Range: %q, unexpected trailing content", hdr) + } + return first, last, completeLength, nil +} + +// Read implements io.ReadCloser +func (br *bodyReader) Read(p []byte) (int, error) { + if br.body == nil { + return 0, fmt.Errorf("internal error: bodyReader.Read called on a closed object for %s", br.logURL.Redacted()) + } + n, err := br.body.Read(p) + br.offset += int64(n) + switch { + case err == nil || err == io.EOF: + br.lastSuccessTime = time.Now() + return n, err // Unlike the default: case, don’t log anything. + + case errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET): + originalErr := err + redactedURL := br.logURL.Redacted() + if err := br.errorIfNotReconnecting(originalErr, redactedURL); err != nil { + return n, err + } + + if err := br.body.Close(); err != nil { + logrus.Debugf("Error closing blob body: %v", err) // … and ignore err otherwise + } + br.body = nil + time.Sleep(1*time.Second + time.Duration(rand.Intn(100_000))*time.Microsecond) // Some jitter so that a failure blip doesn’t cause a deterministic stampede + + headers := map[string][]string{ + "Range": {fmt.Sprintf("bytes=%d-", br.offset)}, + } + res, err := br.c.makeRequest(br.ctx, http.MethodGet, br.path, headers, nil, v2Auth, nil) + if err != nil { + return n, fmt.Errorf("%w (while reconnecting: %v)", originalErr, err) + } + consumedBody := false + defer func() { + if !consumedBody { + res.Body.Close() + } + }() + switch res.StatusCode { + case http.StatusPartialContent: // OK + // A client MUST inspect a 206 response's Content-Type and Content-Range field(s) to determine what parts are enclosed and whether additional requests are needed. + // The recipient of an invalid Content-Range MUST NOT attempt to recombine the received content with a stored representation. + first, last, completeLength, err := parseContentRange(res) + if err != nil { + return n, fmt.Errorf("%w (after reconnecting, invalid Content-Range header: %v)", originalErr, err) + } + // We don’t handle responses that start at an unrequested offset, nor responses that terminate before the end of the full blob. + if first != br.offset || (completeLength != -1 && last+1 != completeLength) { + return n, fmt.Errorf("%w (after reconnecting at offset %d, got unexpected Content-Range %d-%d/%d)", originalErr, br.offset, first, last, completeLength) + } + // Continue below + case http.StatusOK: + return n, fmt.Errorf("%w (after reconnecting, server did not process a Range: header, status %d)", originalErr, http.StatusOK) + default: + err := registryHTTPResponseToError(res) + return n, fmt.Errorf("%w (after reconnecting, fetching blob: %v)", originalErr, err) + } + + logrus.Debugf("Succesfully reconnected to %s", redactedURL) + consumedBody = true + br.body = res.Body + br.lastRetryOffset = br.offset + return n, nil + + default: + logrus.Debugf("Error reading blob body from %s: %#v", br.logURL.Redacted(), err) + return n, err + } +} + +// millisecondsSince is like time.Since(tm).Milliseconds, but it returns a floating-point value +func millisecondsSince(tm time.Time) float64 { + return float64(time.Since(tm).Nanoseconds()) / 1_000_000.0 +} + +// errorIfNotReconnecting makes a heuristic decision whether we should reconnect after err at redactedURL; if so, it returns nil, +// otherwise it returns an appropriate error to return to the caller (possibly augmented with data about the heuristic) +func (br *bodyReader) errorIfNotReconnecting(originalErr error, redactedURL string) error { + totalTime := millisecondsSince(br.firstConnectionTime) + failureTime := math.NaN() + if (br.lastSuccessTime != time.Time{}) { + failureTime = millisecondsSince(br.lastSuccessTime) + } + logrus.Debugf("Reading blob body from %s failed (%#v), decision inputs: lastRetryOffset %d, offset %d, %.3f ms since first connection, %.3f ms since last progress", + redactedURL, originalErr, br.lastRetryOffset, br.offset, totalTime, failureTime) + progress := br.offset - br.lastRetryOffset + if progress < bodyReaderMinimumProgress { + logrus.Debugf("Not reconnecting to %s because only %d bytes progress made", redactedURL, progress) + return fmt.Errorf("(heuristic tuning data: last retry %d, current offset %d; %.3f ms total, %.3f ms since progress): %w", + br.lastRetryOffset, br.offset, totalTime, failureTime, originalErr) + } + logrus.Infof("Reading blob body from %s failed (%v), reconnecting…", redactedURL, originalErr) + return nil +} + +// Close implements io.ReadCloser +func (br *bodyReader) Close() error { + if br.body == nil { + return nil + } + err := br.body.Close() + br.body = nil + return err +} diff --git a/vendor/github.com/containers/image/v5/docker/daemon/client.go b/vendor/github.com/containers/image/v5/docker/daemon/client.go index 323a02fc..7d2a98d6 100644 --- a/vendor/github.com/containers/image/v5/docker/daemon/client.go +++ b/vendor/github.com/containers/image/v5/docker/daemon/client.go @@ -30,13 +30,13 @@ func newDockerClient(sys *types.SystemContext) (*dockerclient.Client, error) { // // Similarly, if we want to communicate over plain HTTP on a TCP socket, we also need to set // TLSClientConfig to nil. This can be achieved by using the form `http://` - url, err := dockerclient.ParseHostURL(host) + serverURL, err := dockerclient.ParseHostURL(host) if err != nil { return nil, err } var httpClient *http.Client - if url.Scheme != "unix" { - if url.Scheme == "http" { + if serverURL.Scheme != "unix" { + if serverURL.Scheme == "http" { httpClient = httpConfig() } else { hc, err := tlsConfig(sys) diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index 73e26a1a..9e8fece0 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -19,12 +19,12 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/iolimits" + "github.com/containers/image/v5/internal/useragent" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/pkg/tlsclientconfig" "github.com/containers/image/v5/types" - "github.com/containers/image/v5/version" "github.com/containers/storage/pkg/homedir" "github.com/docker/distribution/registry/api/errcode" v2 "github.com/docker/distribution/registry/api/v2" @@ -68,8 +68,6 @@ var ( {path: etcDir + "/containers/certs.d", absolute: true}, {path: etcDir + "/docker/certs.d", absolute: true}, } - - defaultUserAgent = "containers/" + version.Version + " (github.com/containers/image)" ) // extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go: @@ -284,7 +282,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc } tlsClientConfig.InsecureSkipVerify = skipVerify - userAgent := defaultUserAgent + userAgent := useragent.DefaultUserAgent if sys != nil && sys.DockerRegistryUserAgent != "" { userAgent = sys.DockerRegistryUserAgent } @@ -314,8 +312,14 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password return err } defer resp.Body.Close() - - return httpResponseToError(resp, "") + if resp.StatusCode != http.StatusOK { + err := registryHTTPResponseToError(resp) + if resp.StatusCode == http.StatusUnauthorized { + err = ErrUnauthorizedForCredentials{Err: err} + } + return err + } + return nil } // SearchResult holds the information of each matching image @@ -412,7 +416,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err := httpResponseToError(resp, "") + err := registryHTTPResponseToError(resp) 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) } @@ -468,12 +472,22 @@ func (c *dockerClient) makeRequest(ctx context.Context, method, path string, hea return nil, err } + requestURL, err := c.resolveRequestURL(path) + if err != nil { + return nil, err + } + return c.makeRequestToResolvedURL(ctx, method, requestURL, headers, stream, -1, auth, extraScope) +} + +// resolveRequestURL turns a path for c.makeRequest into a full URL. +// Most users should call makeRequest directly, this exists basically to make the URL available for debug logs. +func (c *dockerClient) resolveRequestURL(path string) (*url.URL, error) { urlString := fmt.Sprintf("%s://%s%s", c.scheme, c.registry, path) - url, err := url.Parse(urlString) + res, err := url.Parse(urlString) if err != nil { return nil, err } - return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope) + return res, nil } // Checks if the auth headers in the response contain an indication of a failed @@ -516,9 +530,8 @@ func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Durat return time.Duration(num) * time.Second } // Second, check if we have an HTTP date. - // If the delta between the date and now is positive, use it. - // Otherwise, fall back to using the default exponential back off. if t, err := http.ParseTime(after); err == nil { + // If the delta between the date and now is positive, use it. delta := time.Until(t) if delta > 0 { return delta @@ -526,7 +539,6 @@ func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Durat logrus.Debugf("Retry-After date in the past, ignoring it") return fallbackDelay } - // If the header contents are bogus, fall back to using the default exponential back off. logrus.Debugf("Invalid Retry-After format, ignoring it") return fallbackDelay } @@ -536,11 +548,11 @@ func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Durat // makeRequest should generally be preferred. // In case of an HTTP 429 status code in the response, it may automatically retry a few times. // TODO(runcom): too many arguments here, use a struct -func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method string, url *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { +func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method string, requestURL *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { delay := backoffInitialDelay attempts := 0 for { - res, err := c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope) + res, err := c.makeRequestToResolvedURLOnce(ctx, method, requestURL, headers, stream, streamLen, auth, extraScope) attempts++ // By default we use pre-defined scopes per operation. In @@ -561,7 +573,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method stri // Note: This retry ignores extraScope. That’s, strictly speaking, incorrect, but we don’t currently // expect the insufficient_scope errors to happen for those callers. If that changes, we can add support // for more than one extra scope. - res, err = c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, newScope) + res, err = c.makeRequestToResolvedURLOnce(ctx, method, requestURL, headers, stream, streamLen, auth, newScope) extraScope = newScope } } @@ -577,14 +589,14 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method stri if delay > backoffMaxDelay { delay = backoffMaxDelay } - logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", url.Redacted(), delay.Seconds()) + logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", requestURL.Redacted(), delay.Seconds()) select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(delay): // Nothing } - delay = delay * 2 // exponential back off + delay = delay * 2 // If the registry does not specify a delay, back off exponentially. } } @@ -592,8 +604,8 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method stri // streamLen, if not -1, specifies the length of the data expected on stream. // makeRequest should generally be preferred. // Note that no exponential back off is performed when receiving an http 429 status code. -func (c *dockerClient) makeRequestToResolvedURLOnce(ctx context.Context, method string, url *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { - req, err := http.NewRequestWithContext(ctx, method, url.String(), stream) +func (c *dockerClient) makeRequestToResolvedURLOnce(ctx context.Context, method string, resolvedURL *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, method, resolvedURL.String(), stream) if err != nil { return nil, err } @@ -612,7 +624,7 @@ func (c *dockerClient) makeRequestToResolvedURLOnce(ctx context.Context, method return nil, err } } - logrus.Debugf("%s %s", method, url.Redacted()) + logrus.Debugf("%s %s", method, resolvedURL.Redacted()) res, err := c.client.Do(req) if err != nil { return nil, err @@ -805,19 +817,19 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { c.client = &http.Client{Transport: tr} ping := func(scheme string) error { - url, err := url.Parse(fmt.Sprintf(resolvedPingV2URL, scheme, c.registry)) + pingURL, err := url.Parse(fmt.Sprintf(resolvedPingV2URL, scheme, c.registry)) if err != nil { return err } - resp, err := c.makeRequestToResolvedURL(ctx, http.MethodGet, url, nil, nil, -1, noAuth, nil) + resp, err := c.makeRequestToResolvedURL(ctx, http.MethodGet, pingURL, nil, nil, -1, noAuth, nil) if err != nil { - logrus.Debugf("Ping %s err %s (%#v)", url.Redacted(), err.Error(), err) + logrus.Debugf("Ping %s err %s (%#v)", pingURL.Redacted(), err.Error(), err) return err } defer resp.Body.Close() - logrus.Debugf("Ping %s status %d", url.Redacted(), resp.StatusCode) + logrus.Debugf("Ping %s status %d", pingURL.Redacted(), resp.StatusCode) if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized { - return httpResponseToError(resp, "") + return registryHTTPResponseToError(resp) } c.challenges = parseAuthHeader(resp.Header) c.scheme = scheme @@ -835,17 +847,17 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { } // best effort to understand if we're talking to a V1 registry pingV1 := func(scheme string) bool { - url, err := url.Parse(fmt.Sprintf(resolvedPingV1URL, scheme, c.registry)) + pingURL, err := url.Parse(fmt.Sprintf(resolvedPingV1URL, scheme, c.registry)) if err != nil { return false } - resp, err := c.makeRequestToResolvedURL(ctx, http.MethodGet, url, nil, nil, -1, noAuth, nil) + resp, err := c.makeRequestToResolvedURL(ctx, http.MethodGet, pingURL, nil, nil, -1, noAuth, nil) if err != nil { - logrus.Debugf("Ping %s err %s (%#v)", url.Redacted(), err.Error(), err) + logrus.Debugf("Ping %s err %s (%#v)", pingURL.Redacted(), err.Error(), err) return false } defer resp.Body.Close() - logrus.Debugf("Ping %s status %d", url.Redacted(), resp.StatusCode) + logrus.Debugf("Ping %s status %d", pingURL.Redacted(), resp.StatusCode) if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized { return false } @@ -903,14 +915,14 @@ func (c *dockerClient) getExternalBlob(ctx context.Context, urls []string) (io.R return nil, 0, errors.New("internal error: getExternalBlob called with no URLs") } for _, u := range urls { - url, err := url.Parse(u) - if err != nil || (url.Scheme != "http" && url.Scheme != "https") { + blobURL, err := url.Parse(u) + if err != nil || (blobURL.Scheme != "http" && blobURL.Scheme != "https") { continue // unsupported url. skip this url. } // NOTE: we must not authenticate on additional URLs as those // can be abused to leak credentials or tokens. Please // refer to CVE-2020-15157 for more information. - resp, err = c.makeRequestToResolvedURL(ctx, http.MethodGet, url, nil, nil, -1, noAuth, nil) + resp, err = c.makeRequestToResolvedURL(ctx, http.MethodGet, blobURL, nil, nil, -1, noAuth, nil) if err == nil { if resp.StatusCode != http.StatusOK { err = fmt.Errorf("error fetching external blob from %q: %d (%s)", u, resp.StatusCode, http.StatusText(resp.StatusCode)) @@ -957,12 +969,20 @@ func (c *dockerClient) getBlob(ctx context.Context, ref dockerReference, info ty if err != nil { return nil, 0, err } - if err := httpResponseToError(res, "Error fetching blob"); err != nil { + if res.StatusCode != http.StatusOK { + err := registryHTTPResponseToError(res) 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)) - return res.Body, getBlobSize(res), nil + blobSize := getBlobSize(res) + + reconnectingReader, err := newBodyReader(ctx, c, path, res.Body) + if err != nil { + res.Body.Close() + return nil, 0, err + } + return reconnectingReader, blobSize, nil } // getOCIDescriptorContents returns the contents a blob spcified by descriptor in ref, which must fit within limit. @@ -983,19 +1003,15 @@ func (c *dockerClient) getOCIDescriptorContents(ctx context.Context, ref dockerR // isManifestUnknownError returns true iff err from fetchManifest is a “manifest unknown” error. func isManifestUnknownError(err error) bool { - var errs errcode.Errors - if errors.As(err, &errs) && len(errs) != 0 { - firstErr := errs[0] - // docker/distribution, and as defined in the spec - var ec errcode.ErrorCoder - if errors.As(firstErr, &ec) && ec.ErrorCode() == v2.ErrorCodeManifestUnknown { - return true - } - // registry.redhat.io as of October 2022 - var e errcode.Error - if errors.As(firstErr, &e) && e.ErrorCode() == errcode.ErrorCodeUnknown && e.Message == "Not Found" { - return true - } + // docker/distribution, and as defined in the spec + var ec errcode.ErrorCoder + if errors.As(err, &ec) && ec.ErrorCode() == v2.ErrorCodeManifestUnknown { + return true + } + // registry.redhat.io as of October 2022 + var e errcode.Error + if errors.As(err, &e) && e.ErrorCode() == errcode.ErrorCodeUnknown && e.Message == "Not Found" { + return true } // ALSO registry.redhat.io as of October 2022 var unexpected *unexpectedHTTPResponseError @@ -1048,9 +1064,8 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe return nil, err } defer res.Body.Close() - 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) diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go index 3e8dbbee..6a4331e3 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image.go @@ -77,8 +77,8 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. return nil, err } defer res.Body.Close() - if err := httpResponseToError(res, "fetching tags list"); err != nil { - return nil, err + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("fetching tags list: %w", registryHTTPResponseToError(res)) } var tagsHolder struct { diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index bcbae7db..7a7f72d9 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -244,7 +244,7 @@ func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference. logrus.Debugf("... not present") return false, -1, nil 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)) } } @@ -407,7 +407,7 @@ func (d *dockerImageDestination) TryReusingBlobWithOptions(ctx context.Context, // If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema), // but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError. func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte, instanceDigest *digest.Digest) error { - refTail := "" + var refTail string if instanceDigest != nil { // If the instanceDigest is provided, then use it as the refTail, because the reference, // whether it includes a tag or a digest, refers to the list as a whole, and not this @@ -487,15 +487,10 @@ func successStatus(status int) bool { 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 { - errors, ok := err.(errcode.Errors) - if !ok || len(errors) == 0 { - return false - } - err = errors[0] - ec, ok := err.(errcode.ErrorCoder) - if !ok { + var ec errcode.ErrorCoder + if ok := errors.As(err, &ec); !ok { return false } @@ -585,8 +580,8 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures []signature // NOTE: Keep this in sync with docs/signature-protocols.md! for i, signature := range signatures { - url := lookasideStorageURL(d.c.signatureBase, manifestDigest, i) - err := d.putOneSignature(url, signature) + sigURL := lookasideStorageURL(d.c.signatureBase, manifestDigest, i) + err := d.putOneSignature(sigURL, signature) if err != nil { return err } @@ -597,8 +592,8 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures []signature // is enough for dockerImageSource to stop looking for other signatures, so that // is sufficient. for i := len(signatures); ; i++ { - url := lookasideStorageURL(d.c.signatureBase, manifestDigest, i) - missing, err := d.c.deleteOneSignature(url) + sigURL := lookasideStorageURL(d.c.signatureBase, manifestDigest, i) + missing, err := d.c.deleteOneSignature(sigURL) if err != nil { return err } @@ -610,13 +605,13 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures []signature return nil } -// putOneSignature stores sig to url. +// putOneSignature stores sig to sigURL. // NOTE: Keep this in sync with docs/signature-protocols.md! -func (d *dockerImageDestination) putOneSignature(url *url.URL, sig signature.Signature) error { - switch url.Scheme { +func (d *dockerImageDestination) putOneSignature(sigURL *url.URL, sig signature.Signature) error { + switch sigURL.Scheme { case "file": - logrus.Debugf("Writing to %s", url.Path) - err := os.MkdirAll(filepath.Dir(url.Path), 0755) + logrus.Debugf("Writing to %s", sigURL.Path) + err := os.MkdirAll(filepath.Dir(sigURL.Path), 0755) if err != nil { return err } @@ -624,16 +619,16 @@ func (d *dockerImageDestination) putOneSignature(url *url.URL, sig signature.Sig if err != nil { return err } - err = os.WriteFile(url.Path, blob, 0644) + err = os.WriteFile(sigURL.Path, blob, 0644) if err != nil { return err } return nil case "http", "https": - return fmt.Errorf("Writing directly to a %s lookaside %s is not supported. Configure a lookaside-staging: location", url.Scheme, url.Redacted()) + return fmt.Errorf("Writing directly to a %s lookaside %s is not supported. Configure a lookaside-staging: location", sigURL.Scheme, sigURL.Redacted()) default: - return fmt.Errorf("Unsupported scheme when writing signature to %s", url.Redacted()) + return fmt.Errorf("Unsupported scheme when writing signature to %s", sigURL.Redacted()) } } @@ -644,7 +639,7 @@ func (d *dockerImageDestination) putSignaturesToSigstoreAttachments(ctx context. ociManifest, err := d.c.getSigstoreAttachmentManifest(ctx, d.ref, manifestDigest) if err != nil { - return nil + return err } var ociConfig imgspecv1.Image // Most fields empty by default if ociManifest == nil { @@ -716,13 +711,13 @@ func (d *dockerImageDestination) putSignaturesToSigstoreAttachments(ctx context. LayerIndex: nil, }) if err != nil { - return nil + return err } ociManifest.Config = configDesc manifestBlob, err := ociManifest.Serialize() if err != nil { - return nil + return err } logrus.Debugf("Uploading sigstore attachment manifest") return d.uploadManifest(ctx, manifestBlob, sigstoreAttachmentTag(manifestDigest)) @@ -772,23 +767,23 @@ func (d *dockerImageDestination) putBlobBytesAsOCI(ctx context.Context, contents }, nil } -// deleteOneSignature deletes a signature from url, if it exists. +// deleteOneSignature deletes a signature from sigURL, if it exists. // If it successfully determines that the signature does not exist, returns (true, nil) // NOTE: Keep this in sync with docs/signature-protocols.md! -func (c *dockerClient) deleteOneSignature(url *url.URL) (missing bool, err error) { - switch url.Scheme { +func (c *dockerClient) deleteOneSignature(sigURL *url.URL) (missing bool, err error) { + switch sigURL.Scheme { case "file": - logrus.Debugf("Deleting %s", url.Path) - err := os.Remove(url.Path) + logrus.Debugf("Deleting %s", sigURL.Path) + err := os.Remove(sigURL.Path) if err != nil && os.IsNotExist(err) { return true, nil } return false, err case "http", "https": - return false, fmt.Errorf("Writing directly to a %s lookaside %s is not supported. Configure a lookaside-staging: location", url.Scheme, url.Redacted()) + return false, fmt.Errorf("Writing directly to a %s lookaside %s is not supported. Configure a lookaside-staging: location", sigURL.Scheme, sigURL.Redacted()) default: - return false, fmt.Errorf("Unsupported scheme when deleting signature from %s", url.Redacted()) + return false, fmt.Errorf("Unsupported scheme when deleting signature from %s", sigURL.Redacted()) } } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index b0e87797..373fb259 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -10,7 +10,6 @@ import ( "net/http" "net/url" "os" - "regexp" "strings" "sync" @@ -24,10 +23,15 @@ import ( "github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/regexp" digest "github.com/opencontainers/go-digest" "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 { impl.Compat impl.PropertyMethodsInitialize @@ -299,7 +303,7 @@ func handle206Response(streams chan io.ReadCloser, errs chan error, body io.Read } } -var multipartByteRangesRe = regexp.MustCompile("multipart/byteranges; boundary=([A-Za-z-0-9:]+)") +var multipartByteRangesRe = regexp.Delayed("multipart/byteranges; boundary=([A-Za-z-0-9:]+)") func parseMediaType(contentType string) (string, map[string]string, error) { mediaType, params, err := mime.ParseMediaType(contentType) @@ -372,12 +376,9 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, res.Body.Close() return nil, nil, private.BadPartialRequestError{Status: res.Status} default: - err := httpResponseToError(res, "Error fetching partial blob") - if err == nil { - err = fmt.Errorf("invalid status code returned when fetching blob %d (%s)", res.StatusCode, http.StatusText(res.StatusCode)) - } + err := registryHTTPResponseToError(res) res.Body.Close() - return nil, nil, err + return nil, nil, fmt.Errorf("fetching partial blob: %w", err) } } @@ -451,8 +452,12 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst // NOTE: Keep this in sync with docs/signature-protocols.md! signatures := []signature.Signature{} for i := 0; ; i++ { - url := lookasideStorageURL(s.c.signatureBase, manifestDigest, i) - signature, missing, err := s.getOneSignature(ctx, url) + if i >= maxLookasideSignatures { + return nil, fmt.Errorf("server provided %d signatures, assuming that's unreasonable and a server error", maxLookasideSignatures) + } + + sigURL := lookasideStorageURL(s.c.signatureBase, manifestDigest, i) + signature, missing, err := s.getOneSignature(ctx, sigURL) if err != nil { return nil, err } @@ -464,14 +469,14 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst return signatures, nil } -// getOneSignature downloads one signature from url, and returns (signature, false, nil) +// getOneSignature downloads one signature from sigURL, and returns (signature, false, nil) // If it successfully determines that the signature does not exist, returns (nil, true, nil). // NOTE: Keep this in sync with docs/signature-protocols.md! -func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (signature.Signature, bool, error) { - switch url.Scheme { +func (s *dockerImageSource) getOneSignature(ctx context.Context, sigURL *url.URL) (signature.Signature, bool, error) { + switch sigURL.Scheme { case "file": - logrus.Debugf("Reading %s", url.Path) - sigBlob, err := os.ReadFile(url.Path) + logrus.Debugf("Reading %s", sigURL.Path) + sigBlob, err := os.ReadFile(sigURL.Path) if err != nil { if os.IsNotExist(err) { return nil, true, nil @@ -480,13 +485,13 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) ( } sig, err := signature.FromBlob(sigBlob) if err != nil { - return nil, false, fmt.Errorf("parsing signature %q: %w", url.Path, err) + return nil, false, fmt.Errorf("parsing signature %q: %w", sigURL.Path, err) } return sig, false, nil case "http", "https": - logrus.Debugf("GET %s", url.Redacted()) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil) + logrus.Debugf("GET %s", sigURL.Redacted()) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, sigURL.String(), nil) if err != nil { return nil, false, err } @@ -496,22 +501,31 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) ( } defer res.Body.Close() if res.StatusCode == http.StatusNotFound { + logrus.Debugf("... got status 404, as expected = end of signatures") return nil, true, nil } 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)", sigURL.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", sigURL.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) if err != nil { return nil, false, err } sig, err := signature.FromBlob(sigBlob) if err != nil { - return nil, false, fmt.Errorf("parsing signature %s: %w", url.Redacted(), err) + return nil, false, fmt.Errorf("parsing signature %s: %w", sigURL.Redacted(), err) } return sig, false, nil default: - return nil, false, fmt.Errorf("Unsupported scheme when reading signature from %s", url.Redacted()) + return nil, false, fmt.Errorf("Unsupported scheme when reading signature from %s", sigURL.Redacted()) } } @@ -605,16 +619,16 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } defer get.Body.Close() - manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize) - if err != nil { - return err - } switch get.StatusCode { case http.StatusOK: 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) 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) @@ -630,18 +644,13 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } defer delete.Body.Close() - - body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize) - if err != nil { - return err - } 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++ { - url := lookasideStorageURL(c.signatureBase, manifestDigest, i) - missing, err := c.deleteOneSignature(url) + sigURL := lookasideStorageURL(c.signatureBase, manifestDigest, i) + missing, err := c.deleteOneSignature(sigURL) if err != nil { return err } diff --git a/vendor/github.com/containers/image/v5/docker/errors.go b/vendor/github.com/containers/image/v5/docker/errors.go index e1986554..74fe1764 100644 --- a/vendor/github.com/containers/image/v5/docker/errors.go +++ b/vendor/github.com/containers/image/v5/docker/errors.go @@ -4,6 +4,9 @@ import ( "errors" "fmt" "net/http" + + "github.com/docker/distribution/registry/api/errcode" + "github.com/sirupsen/logrus" ) var ( @@ -33,7 +36,7 @@ func httpResponseToError(res *http.Response, context string) error { case http.StatusTooManyRequests: return ErrTooManyRequests case http.StatusUnauthorized: - err := handleErrorResponse(res) + err := registryHTTPResponseToError(res) return ErrUnauthorizedForCredentials{Err: err} default: if context != "" { @@ -47,13 +50,47 @@ func httpResponseToError(res *http.Response, context string) error { // registry func registryHTTPResponseToError(res *http.Response) error { 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) if len(response) > 50 { response = response[:50] + "..." } // %.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 } diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/reader.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/reader.go index eec7b84e..3b986f50 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/reader.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/reader.go @@ -34,15 +34,19 @@ func NewReaderFromFile(sys *types.SystemContext, path string) (*Reader, error) { } defer file.Close() - // If the file is already not compressed we can just return the file itself + // If the file is seekable and already not compressed we can just return the file itself // as a source. Otherwise we pass the stream to NewReaderFromStream. - stream, isCompressed, err := compression.AutoDecompress(file) - if err != nil { - return nil, fmt.Errorf("detecting compression for file %q: %w", path, err) - } - defer stream.Close() - if !isCompressed { - return newReader(path, false) + var stream io.Reader = file + if _, err := file.Seek(0, io.SeekCurrent); err == nil { // seeking is possible + decompressed, isCompressed, err := compression.AutoDecompress(file) + if err != nil { + return nil, fmt.Errorf("detecting compression for file %q: %w", path, err) + } + defer decompressed.Close() + stream = decompressed + if !isCompressed { + return newReader(path, false) + } } return NewReaderFromStream(sys, stream) } diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go index f6ee041c..44e1004a 100644 --- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go +++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go @@ -346,7 +346,7 @@ func (t *tarFI) Sys() interface{} { func (w *Writer) sendSymlinkLocked(path string, target string) error { hdr, err := tar.FileInfoHeader(&tarFI{path: path, size: 0, isSymlink: true}, target) if err != nil { - return nil + return err } logrus.Debugf("Sending as tar link %s -> %s", path, target) return w.tar.WriteHeader(hdr) @@ -363,7 +363,7 @@ func (w *Writer) sendBytesLocked(path string, b []byte) error { func (w *Writer) sendFileLocked(path string, expectedSize int64, stream io.Reader) error { hdr, err := tar.FileInfoHeader(&tarFI{path: path, size: expectedSize}, "") if err != nil { - return nil + return err } logrus.Debugf("Sending as tar file %s", path) if err := w.tar.WriteHeader(hdr); err != nil { diff --git a/vendor/github.com/containers/image/v5/docker/reference/regexp.go b/vendor/github.com/containers/image/v5/docker/reference/regexp.go index 78603493..bb8c5547 100644 --- a/vendor/github.com/containers/image/v5/docker/reference/regexp.go +++ b/vendor/github.com/containers/image/v5/docker/reference/regexp.go @@ -1,143 +1,155 @@ package reference -import "regexp" +import ( + storageRegexp "github.com/containers/storage/pkg/regexp" + "regexp" + "strings" +) -var ( - // alphaNumericRegexp defines the alpha numeric atom, typically a +const ( + // alphaNumeric defines the alpha numeric atom, typically a // component of names. This only allows lower case characters and digits. - alphaNumericRegexp = match(`[a-z0-9]+`) + alphaNumeric = `[a-z0-9]+` - // separatorRegexp defines the separators allowed to be embedded in name + // separator defines the separators allowed to be embedded in name // components. This allow one period, one or two underscore and multiple - // dashes. - separatorRegexp = match(`(?:[._]|__|[-]*)`) - - // nameComponentRegexp restricts registry path component names to start - // with at least one letter or number, with following parts able to be - // separated by one period, one or two underscore and multiple dashes. - nameComponentRegexp = expression( - alphaNumericRegexp, - optional(repeated(separatorRegexp, alphaNumericRegexp))) + // dashes. Repeated dashes and underscores are intentionally treated + // differently. In order to support valid hostnames as name components, + // supporting repeated dash was added. Additionally double underscore is + // now allowed as a separator to loosen the restriction for previously + // supported names. + separator = `(?:[._]|__|[-]*)` - // domainComponentRegexp restricts the registry domain component of a // repository name to start with a component as defined by DomainRegexp // and followed by an optional port. - domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + domainComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])` + // The string counterpart for TagRegexp. + tag = `[\w][\w.-]{0,127}` + + // The string counterpart for DigestRegexp. + digestPat = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}` + + // The string counterpart for IdentifierRegexp. + identifier = `([a-f0-9]{64})` + + // The string counterpart for ShortIdentifierRegexp. + shortIdentifier = `([a-f0-9]{6,64})` +) + +var ( + // nameComponent restricts registry path component names to start + // with at least one letter or number, with following parts able to be + // separated by one period, one or two underscore and multiple dashes. + nameComponent = expression( + alphaNumeric, + optional(repeated(separator, alphaNumeric))) + + domain = expression( + domainComponent, + optional(repeated(literal(`.`), domainComponent)), + optional(literal(`:`), `[0-9]+`)) // DomainRegexp defines the structure of potential domain components // that may be part of image names. This is purposely a subset of what is // allowed by DNS to ensure backwards compatibility with Docker image // names. - DomainRegexp = expression( - domainComponentRegexp, - optional(repeated(literal(`.`), domainComponentRegexp)), - optional(literal(`:`), match(`[0-9]+`))) + DomainRegexp = re(domain) // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. - TagRegexp = match(`[\w][\w.-]{0,127}`) + TagRegexp = re(tag) + anchoredTag = anchored(tag) // anchoredTagRegexp matches valid tag names, anchored at the start and // end of the matched string. - anchoredTagRegexp = anchored(TagRegexp) + anchoredTagRegexp = storageRegexp.Delayed(anchoredTag) // DigestRegexp matches valid digests. - DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) + DigestRegexp = re(digestPat) + anchoredDigest = anchored(digestPat) // anchoredDigestRegexp matches valid digests, anchored at the start and // end of the matched string. - anchoredDigestRegexp = anchored(DigestRegexp) + anchoredDigestRegexp = storageRegexp.Delayed(anchoredDigest) + namePat = expression( + optional(domain, literal(`/`)), + nameComponent, + optional(repeated(literal(`/`), nameComponent))) // NameRegexp is the format for the name component of references. The // regexp has capturing groups for the domain and name part omitting // the separating forward slash from either. - NameRegexp = expression( - optional(DomainRegexp, literal(`/`)), - nameComponentRegexp, - optional(repeated(literal(`/`), nameComponentRegexp))) + NameRegexp = re(namePat) + anchoredName = anchored( + optional(capture(domain), literal(`/`)), + capture(nameComponent, + optional(repeated(literal(`/`), nameComponent)))) // anchoredNameRegexp is used to parse a name value, capturing the // domain and trailing components. - anchoredNameRegexp = anchored( - optional(capture(DomainRegexp), literal(`/`)), - capture(nameComponentRegexp, - optional(repeated(literal(`/`), nameComponentRegexp)))) + anchoredNameRegexp = storageRegexp.Delayed(anchoredName) + referencePat = anchored(capture(namePat), + optional(literal(":"), capture(tag)), + optional(literal("@"), capture(digestPat))) // ReferenceRegexp is the full supported format of a reference. The regexp // is anchored and has capturing groups for name, tag, and digest // components. - ReferenceRegexp = anchored(capture(NameRegexp), - optional(literal(":"), capture(TagRegexp)), - optional(literal("@"), capture(DigestRegexp))) + ReferenceRegexp = re(referencePat) // IdentifierRegexp is the format for string identifier used as a // content addressable identifier using sha256. These identifiers // are like digests without the algorithm, since sha256 is used. - IdentifierRegexp = match(`([a-f0-9]{64})`) + IdentifierRegexp = re(identifier) // ShortIdentifierRegexp is the format used to represent a prefix // of an identifier. A prefix may be used to match a sha256 identifier // within a list of trusted identifiers. - ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) + ShortIdentifierRegexp = re(shortIdentifier) + anchoredIdentifier = anchored(identifier) // anchoredIdentifierRegexp is used to check or match an // identifier value, anchored at start and end of string. - anchoredIdentifierRegexp = anchored(IdentifierRegexp) - - // anchoredShortIdentifierRegexp is used to check if a value - // is a possible identifier prefix, anchored at start and end - // of string. - anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp) + anchoredIdentifierRegexp = storageRegexp.Delayed(anchoredIdentifier) ) -// match compiles the string to a regular expression. -var match = regexp.MustCompile +// re compiles the string to a regular expression. +var re = regexp.MustCompile // literal compiles s into a literal regular expression, escaping any regexp // reserved characters. -func literal(s string) *regexp.Regexp { - re := match(regexp.QuoteMeta(s)) - - if _, complete := re.LiteralPrefix(); !complete { - panic("must be a literal") - } - - return re +func literal(s string) string { + return regexp.QuoteMeta(s) } // expression defines a full expression, where each regular expression must // follow the previous. -func expression(res ...*regexp.Regexp) *regexp.Regexp { - var s string - for _, re := range res { - s += re.String() - } - - return match(s) +func expression(res ...string) string { + return strings.Join(res, "") } // optional wraps the expression in a non-capturing group and makes the // production optional. -func optional(res ...*regexp.Regexp) *regexp.Regexp { - return match(group(expression(res...)).String() + `?`) +func optional(res ...string) string { + return group(expression(res...)) + `?` } // repeated wraps the regexp in a non-capturing group to get one or more // matches. -func repeated(res ...*regexp.Regexp) *regexp.Regexp { - return match(group(expression(res...)).String() + `+`) +func repeated(res ...string) string { + return group(expression(res...)) + `+` } // group wraps the regexp in a non-capturing group. -func group(res ...*regexp.Regexp) *regexp.Regexp { - return match(`(?:` + expression(res...).String() + `)`) +func group(res ...string) string { + return `(?:` + expression(res...) + `)` } // capture wraps the expression in a capturing group. -func capture(res ...*regexp.Regexp) *regexp.Regexp { - return match(`(` + expression(res...).String() + `)`) +func capture(res ...string) string { + return `(` + expression(res...) + `)` } // anchored anchors the regular expression by adding start and end delimiters. -func anchored(res ...*regexp.Regexp) *regexp.Regexp { - return match(`^` + expression(res...).String() + `$`) +func anchored(res ...string) string { + return `^` + expression(res...) + `$` } diff --git a/vendor/github.com/containers/image/v5/docker/registries_d.go b/vendor/github.com/containers/image/v5/docker/registries_d.go index 37087dd8..61a4964b 100644 --- a/vendor/github.com/containers/image/v5/docker/registries_d.go +++ b/vendor/github.com/containers/image/v5/docker/registries_d.go @@ -163,17 +163,17 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { // the usage of the BaseURL is defined under docker/distribution registries—separate storage of docs/signature-protocols.md func (config *registryConfiguration) lookasideStorageBaseURL(dr dockerReference, write bool) (*url.URL, error) { topLevel := config.signatureTopLevel(dr, write) - var url *url.URL + var baseURL *url.URL if topLevel != "" { u, err := url.Parse(topLevel) if err != nil { return nil, fmt.Errorf("Invalid signature storage URL %s: %w", topLevel, err) } - url = u + baseURL = u } else { // returns default directory if no lookaside specified in configuration file - url = builtinDefaultLookasideStorageDir(rootless.GetRootlessEUID()) - logrus.Debugf(" No signature storage configuration found for %s, using built-in default %s", dr.PolicyConfigurationIdentity(), url.Redacted()) + baseURL = builtinDefaultLookasideStorageDir(rootless.GetRootlessEUID()) + logrus.Debugf(" No signature storage configuration found for %s, using built-in default %s", dr.PolicyConfigurationIdentity(), baseURL.Redacted()) } // NOTE: Keep this in sync with docs/signature-protocols.md! // FIXME? Restrict to explicitly supported schemes? @@ -181,8 +181,8 @@ func (config *registryConfiguration) lookasideStorageBaseURL(dr dockerReference, if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references return nil, fmt.Errorf("Unexpected path elements in Docker reference %s for signature storage", dr.ref.String()) } - url.Path = url.Path + "/" + repo - return url, nil + baseURL.Path = baseURL.Path + "/" + repo + return baseURL, nil } // builtinDefaultLookasideStorageDir returns default signature storage URL as per euid @@ -201,8 +201,8 @@ func (config *registryConfiguration) signatureTopLevel(ref dockerReference, writ identity := ref.PolicyConfigurationIdentity() if ns, ok := config.Docker[identity]; ok { logrus.Debugf(` Lookaside configuration: using "docker" namespace %s`, identity) - if url := ns.signatureTopLevel(write); url != "" { - return url + if ret := ns.signatureTopLevel(write); ret != "" { + return ret } } @@ -210,8 +210,8 @@ func (config *registryConfiguration) signatureTopLevel(ref dockerReference, writ for _, name := range ref.PolicyConfigurationNamespaces() { if ns, ok := config.Docker[name]; ok { logrus.Debugf(` Lookaside configuration: using "docker" namespace %s`, name) - if url := ns.signatureTopLevel(write); url != "" { - return url + if ret := ns.signatureTopLevel(write); ret != "" { + return ret } } } @@ -219,8 +219,8 @@ func (config *registryConfiguration) signatureTopLevel(ref dockerReference, writ // Look for a default location if config.DefaultDocker != nil { logrus.Debugf(` Lookaside configuration: using "default-docker" configuration`) - if url := config.DefaultDocker.signatureTopLevel(write); url != "" { - return url + if ret := config.DefaultDocker.signatureTopLevel(write); ret != "" { + return ret } } return "" @@ -287,7 +287,7 @@ func (ns registryNamespace) signatureTopLevel(write bool) string { // base is not nil from the caller // NOTE: Keep this in sync with docs/signature-protocols.md! func lookasideStorageURL(base lookasideStorageBase, manifestDigest digest.Digest, index int) *url.URL { - url := *base - url.Path = fmt.Sprintf("%s@%s=%s/signature-%d", url.Path, manifestDigest.Algorithm(), manifestDigest.Hex(), index+1) - return &url + sigURL := *base + sigURL.Path = fmt.Sprintf("%s@%s=%s/signature-%d", sigURL.Path, manifestDigest.Algorithm(), manifestDigest.Hex(), index+1) + return &sigURL } diff --git a/vendor/github.com/containers/image/v5/internal/imagesource/stubs/stubs.go b/vendor/github.com/containers/image/v5/internal/imagesource/stubs/stubs.go index 0ce6fd51..cb345395 100644 --- a/vendor/github.com/containers/image/v5/internal/imagesource/stubs/stubs.go +++ b/vendor/github.com/containers/image/v5/internal/imagesource/stubs/stubs.go @@ -15,7 +15,7 @@ // // Second, there are stubs with a constructor, like NoGetBlobAtInitialize. The Initialize marker // means that a constructor must be called: - +// // type yourSource struct { // stubs.NoGetBlobAtInitialize // … diff --git a/vendor/github.com/containers/image/v5/internal/signature/sigstore.go b/vendor/github.com/containers/image/v5/internal/signature/sigstore.go index 17342c8b..30aff4c1 100644 --- a/vendor/github.com/containers/image/v5/internal/signature/sigstore.go +++ b/vendor/github.com/containers/image/v5/internal/signature/sigstore.go @@ -7,6 +7,12 @@ const ( SigstoreSignatureMIMEType = "application/vnd.dev.cosign.simplesigning.v1+json" // from sigstore/cosign/pkg/oci/static.SignatureAnnotationKey SigstoreSignatureAnnotationKey = "dev.cosignproject.cosign/signature" + // from sigstore/cosign/pkg/oci/static.BundleAnnotationKey + SigstoreSETAnnotationKey = "dev.sigstore.cosign/bundle" + // from sigstore/cosign/pkg/oci/static.CertificateAnnotationKey + SigstoreCertificateAnnotationKey = "dev.sigstore.cosign/certificate" + // from sigstore/cosign/pkg/oci/static.ChainAnnotationKey + SigstoreIntermediateCertificateChainAnnotationKey = "dev.sigstore.cosign/chain" ) // Sigstore is a github.com/cosign/cosign signature. diff --git a/vendor/github.com/containers/image/v5/internal/signer/signer.go b/vendor/github.com/containers/image/v5/internal/signer/signer.go new file mode 100644 index 00000000..5720254d --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/signer/signer.go @@ -0,0 +1,47 @@ +package signer + +import ( + "context" + + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/signature" +) + +// Signer is an object, possibly carrying state, that can be used by copy.Image to sign one or more container images. +// This type is visible to external callers, so it has no public fields or methods apart from Close(). +// +// The owner of a Signer must call Close() when done. +type Signer struct { + implementation SignerImplementation +} + +// NewSigner creates a public Signer from a SignerImplementation +func NewSigner(impl SignerImplementation) *Signer { + return &Signer{implementation: impl} +} + +func (s *Signer) Close() error { + return s.implementation.Close() +} + +// ProgressMessage returns a human-readable sentence that makes sense to write before starting to create a single signature. +// Alternatively, should SignImageManifest be provided a logging writer of some kind? +func ProgressMessage(signer *Signer) string { + return signer.implementation.ProgressMessage() +} + +// SignImageManifest invokes a SignerImplementation. +// This is a function, not a method, so that it can only be called by code that is allowed to import this internal subpackage. +func SignImageManifest(ctx context.Context, signer *Signer, manifest []byte, dockerReference reference.Named) (signature.Signature, error) { + return signer.implementation.SignImageManifest(ctx, manifest, dockerReference) +} + +// SignerImplementation is an object, possibly carrying state, that can be used by copy.Image to sign one or more container images. +// This interface is distinct from Signer so that implementations can be created outside of this package. +type SignerImplementation interface { + // ProgressMessage returns a human-readable sentence that makes sense to write before starting to create a single signature. + ProgressMessage() string + // SignImageManifest creates a new signature for manifest m as dockerReference. + SignImageManifest(ctx context.Context, m []byte, dockerReference reference.Named) (signature.Signature, error) + Close() error +} diff --git a/vendor/github.com/containers/image/v5/internal/useragent/useragent.go b/vendor/github.com/containers/image/v5/internal/useragent/useragent.go new file mode 100644 index 00000000..7ac49693 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/useragent/useragent.go @@ -0,0 +1,6 @@ +package useragent + +import "github.com/containers/image/v5/version" + +// DefaultUserAgent is a value that should be used by User-Agent headers, unless the user specifically instructs us otherwise. +var DefaultUserAgent = "containers/" + version.Version + " (github.com/containers/image)" diff --git a/vendor/github.com/containers/image/v5/manifest/docker_schema1.go b/vendor/github.com/containers/image/v5/manifest/docker_schema1.go index 9df9bc66..8e260c03 100644 --- a/vendor/github.com/containers/image/v5/manifest/docker_schema1.go +++ b/vendor/github.com/containers/image/v5/manifest/docker_schema1.go @@ -4,12 +4,12 @@ import ( "encoding/json" "errors" "fmt" - "regexp" "strings" "time" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/regexp" "github.com/docker/docker/api/types/versions" "github.com/opencontainers/go-digest" ) @@ -206,7 +206,7 @@ func (m *Schema1) fixManifestLayers() error { return nil } -var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) +var validHex = regexp.Delayed(`^([a-f0-9]{64})$`) func validateV1ID(id string) error { if ok := validHex.MatchString(id); !ok { @@ -227,6 +227,7 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI Created: &s1.Created, DockerVersion: s1.DockerVersion, Architecture: s1.Architecture, + Variant: s1.Variant, Os: s1.OS, Layers: layerInfosToStrings(layerInfos), LayersData: imgInspectLayersFromLayerInfos(layerInfos), diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go index 0674eb47..2c52423d 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci.go +++ b/vendor/github.com/containers/image/v5/manifest/oci.go @@ -219,6 +219,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type DockerVersion: d1.DockerVersion, Labels: v1.Config.Labels, Architecture: v1.Architecture, + Variant: v1.Variant, Os: v1.OS, Layers: layerInfosToStrings(layerInfos), LayersData: imgInspectLayersFromLayerInfos(layerInfos), diff --git a/vendor/github.com/containers/image/v5/oci/archive/oci_src.go b/vendor/github.com/containers/image/v5/oci/archive/oci_src.go index e5ad2570..6c9ee334 100644 --- a/vendor/github.com/containers/image/v5/oci/archive/oci_src.go +++ b/vendor/github.com/containers/image/v5/oci/archive/oci_src.go @@ -17,6 +17,17 @@ import ( "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 { impl.Compat @@ -35,6 +46,10 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref ociArchiv unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, sys) if err != nil { + var notFound ocilayout.ImageNotFoundError + if errors.As(err, ¬Found) { + err = ImageNotFoundError{ref: ref} + } if err := tempDirRef.deleteTempDir(); err != nil { return nil, fmt.Errorf("deleting temp directory %q: %w", tempDirRef.tempDirectory, err) } diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go index b2d963b0..408af20a 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go @@ -21,6 +21,17 @@ import ( 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 { impl.Compat impl.PropertyMethodsInitialize @@ -96,7 +107,7 @@ func (s *ociImageSource) GetManifest(ctx context.Context, instanceDigest *digest var err error if instanceDigest == nil { - dig = digest.Digest(s.descriptor.Digest) + dig = s.descriptor.Digest mimeType = s.descriptor.MediaType } else { dig = *instanceDigest diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go b/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go index be22bed6..5375d332 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go @@ -179,35 +179,25 @@ func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) { return imgspecv1.Descriptor{}, err } - var d *imgspecv1.Descriptor if ref.image == "" { // return manifest if only one image is in the oci directory - if len(index.Manifests) == 1 { - d = &index.Manifests[0] - } else { + if len(index.Manifests) != 1 { // ask user to choose image when more than one image in the oci directory return imgspecv1.Descriptor{}, ErrMoreThanOneImage } + return index.Manifests[0], nil } else { // if image specified, look through all manifests for a match for _, md := range index.Manifests { if md.MediaType != imgspecv1.MediaTypeImageManifest && md.MediaType != imgspecv1.MediaTypeImageIndex { continue } - refName, ok := md.Annotations[imgspecv1.AnnotationRefName] - if !ok { - continue - } - if refName == ref.image { - d = &md - break + if refName, ok := md.Annotations[imgspecv1.AnnotationRefName]; ok && refName == ref.image { + return md, nil } } } - if d == nil { - return imgspecv1.Descriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.image) - } - return *d, nil + return imgspecv1.Descriptor{}, ImageNotFoundError{ref} } // LoadManifestDescriptor loads the manifest descriptor to be used to retrieve the image name diff --git a/vendor/github.com/containers/image/v5/openshift/openshift.go b/vendor/github.com/containers/image/v5/openshift/openshift.go index b2e4dfd9..38f5d531 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift.go @@ -67,14 +67,14 @@ func newOpenshiftClient(ref openshiftReference) (*openshiftClient, error) { // doRequest performs a correctly authenticated request to a specified path, and returns response body or an error object. func (c *openshiftClient) doRequest(ctx context.Context, method, path string, requestBody []byte) ([]byte, error) { - url := *c.baseURL - url.Path = path + requestURL := *c.baseURL + requestURL.Path = path var requestBodyReader io.Reader if requestBody != nil { logrus.Debugf("Will send body: %s", requestBody) requestBodyReader = bytes.NewReader(requestBody) } - req, err := http.NewRequestWithContext(ctx, method, url.String(), requestBodyReader) + req, err := http.NewRequestWithContext(ctx, method, requestURL.String(), requestBodyReader) if err != nil { return nil, err } @@ -90,7 +90,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re req.Header.Set("Content-Type", "application/json") } - logrus.Debugf("%s %s", method, url.Redacted()) + logrus.Debugf("%s %s", method, requestURL.Redacted()) res, err := c.httpClient.Do(req) if err != nil { return nil, err diff --git a/vendor/github.com/containers/image/v5/openshift/openshift_transport.go b/vendor/github.com/containers/image/v5/openshift/openshift_transport.go index f7971a48..15990394 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift_transport.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift_transport.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "regexp" "strings" "github.com/containers/image/v5/docker/policyconfiguration" @@ -12,6 +11,7 @@ import ( genericImage "github.com/containers/image/v5/internal/image" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/regexp" ) func init() { @@ -35,7 +35,7 @@ func (t openshiftTransport) ParseReference(reference string) (types.ImageReferen // Note that imageNameRegexp is namespace/stream:tag, this // is HOSTNAME/namespace/stream:tag or parent prefixes. // Keep this in sync with imageNameRegexp! -var scopeRegexp = regexp.MustCompile("^[^/]*(/[^:/]*(/[^:/]*(:[^:/]*)?)?)?$") +var scopeRegexp = regexp.Delayed("^[^/]*(/[^:/]*(/[^:/]*(:[^:/]*)?)?)?$") // ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys // (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value). diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_transport.go b/vendor/github.com/containers/image/v5/ostree/ostree_transport.go index 658d4e90..14a84414 100644 --- a/vendor/github.com/containers/image/v5/ostree/ostree_transport.go +++ b/vendor/github.com/containers/image/v5/ostree/ostree_transport.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "strings" "github.com/containers/image/v5/directory/explicitfilepath" @@ -18,6 +17,7 @@ import ( "github.com/containers/image/v5/internal/image" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/regexp" ) const defaultOSTreeRepo = "/ostree/repo" @@ -216,7 +216,7 @@ func (ref ostreeReference) DeleteImage(ctx context.Context, sys *types.SystemCon return errors.New("Deleting images not implemented for ostree: images") } -var ostreeRefRegexp = regexp.MustCompile(`^[A-Za-z0-9.-]$`) +var ostreeRefRegexp = regexp.Delayed(`^[A-Za-z0-9.-]$`) func encodeOStreeRef(in string) string { var buffer bytes.Buffer diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/src.go b/vendor/github.com/containers/image/v5/pkg/blobcache/src.go index 60677470..8cd1bd0e 100644 --- a/vendor/github.com/containers/image/v5/pkg/blobcache/src.go +++ b/vendor/github.com/containers/image/v5/pkg/blobcache/src.go @@ -138,7 +138,7 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest var replaceDigest []byte var err error blobFile := s.reference.blobPath(info.Digest, false) - alternate := "" + var alternate string switch s.reference.compress { case types.Compress: alternate = blobFile + compressedNote diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go index 0790a47f..c363cb53 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -32,11 +32,6 @@ type dockerConfigFile struct { CredHelpers map[string]string `json:"credHelpers,omitempty"` } -type authPath struct { - path string - legacyFormat bool -} - var ( defaultPerUIDPathFormat = filepath.FromSlash("/run/containers/%d/auth.json") xdgConfigHomePath = filepath.FromSlash("containers/auth.json") @@ -52,11 +47,24 @@ var ( ErrNotSupported = errors.New("not supported") ) +// authPath combines a path to a file with container registry access keys, +// along with expected properties of that path (currently just whether it's) +// legacy format or not. +type authPath struct { + path string + legacyFormat bool +} + +// newAuthPathDefault constructs an authPath in non-legacy format. +func newAuthPathDefault(path string) authPath { + return authPath{path: path, legacyFormat: false} +} + // SetCredentials stores the username and password in a location // appropriate for sys and the users’ configuration. // A valid key is a repository, a namespace within a registry, or a registry hostname; // using forms other than just a registry may fail depending on configuration. -// Returns a human-redable description of the location that was updated. +// Returns a human-readable description of the location that was updated. // NOTE: The return value is only intended to be read by humans; its form is not an API, // it may change (or new forms can be added) any time. func SetCredentials(sys *types.SystemContext, key, username, password string) (string, error) { @@ -78,25 +86,28 @@ func SetCredentials(sys *types.SystemContext, key, username, password string) (s switch helper { // Special-case the built-in helpers for auth files. case sysregistriesv2.AuthenticationFileHelper: - desc, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + desc, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, string, error) { if ch, exists := auths.CredHelpers[key]; exists { if isNamespaced { - return false, unsupportedNamespaceErr(ch) + return false, "", unsupportedNamespaceErr(ch) } - return false, setAuthToCredHelper(ch, key, username, password) + desc, err := setAuthToCredHelper(ch, key, username, password) + if err != nil { + return false, "", err + } + return false, desc, nil } creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) newCreds := dockerAuthConfig{Auth: creds} auths.AuthConfigs[key] = newCreds - return true, nil + return true, "", nil }) // External helpers. default: if isNamespaced { err = unsupportedNamespaceErr(helper) } else { - desc = fmt.Sprintf("credential helper: %s", helper) - err = setAuthToCredHelper(helper, key, username, password) + desc, err = setAuthToCredHelper(helper, key, username, password) } } if err != nil { @@ -146,8 +157,8 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon // Special-case the built-in helper for auth files. case sysregistriesv2.AuthenticationFileHelper: for _, path := range getAuthFilePaths(sys, homedir.Get()) { - // readJSONFile returns an empty map in case the path doesn't exist. - auths, err := readJSONFile(path.path, path.legacyFormat) + // parse returns an empty map in case the path doesn't exist. + auths, err := path.parse() if err != nil { return nil, fmt.Errorf("reading JSON file %q: %w", path.path, err) } @@ -205,32 +216,32 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon // by tests. func getAuthFilePaths(sys *types.SystemContext, homeDir string) []authPath { paths := []authPath{} - pathToAuth, lf, err := getPathToAuth(sys) + pathToAuth, userSpecifiedPath, err := getPathToAuth(sys) if err == nil { - paths = append(paths, authPath{path: pathToAuth, legacyFormat: lf}) + paths = append(paths, pathToAuth) } else { // Error means that the path set for XDG_RUNTIME_DIR does not exist // but we don't want to completely fail in the case that the user is pulling a public image // Logging the error as a warning instead and moving on to pulling the image logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err) } - xdgCfgHome := os.Getenv("XDG_CONFIG_HOME") - if xdgCfgHome == "" { - xdgCfgHome = filepath.Join(homeDir, ".config") - } - paths = append(paths, authPath{path: filepath.Join(xdgCfgHome, xdgConfigHomePath), legacyFormat: false}) - if dockerConfig := os.Getenv("DOCKER_CONFIG"); dockerConfig != "" { - paths = append(paths, - authPath{path: filepath.Join(dockerConfig, "config.json"), legacyFormat: false}, - ) - } else { + if !userSpecifiedPath { + xdgCfgHome := os.Getenv("XDG_CONFIG_HOME") + if xdgCfgHome == "" { + xdgCfgHome = filepath.Join(homeDir, ".config") + } + paths = append(paths, newAuthPathDefault(filepath.Join(xdgCfgHome, xdgConfigHomePath))) + if dockerConfig := os.Getenv("DOCKER_CONFIG"); dockerConfig != "" { + paths = append(paths, newAuthPathDefault(filepath.Join(dockerConfig, "config.json"))) + } else { + paths = append(paths, + newAuthPathDefault(filepath.Join(homeDir, dockerHomePath)), + ) + } paths = append(paths, - authPath{path: filepath.Join(homeDir, dockerHomePath), legacyFormat: false}, + authPath{path: filepath.Join(homeDir, dockerLegacyHomePath), legacyFormat: true}, ) } - paths = append(paths, - authPath{path: filepath.Join(homeDir, dockerLegacyHomePath), legacyFormat: true}, - ) return paths } @@ -276,7 +287,7 @@ func getCredentialsWithHomeDir(sys *types.SystemContext, key, homeDir string) (t // Anonymous function to query credentials from auth files. getCredentialsFromAuthFiles := func() (types.DockerAuthConfig, string, error) { for _, path := range getAuthFilePaths(sys, homeDir) { - authConfig, err := findCredentialsInFile(key, registry, path.path, path.legacyFormat) + authConfig, err := findCredentialsInFile(key, registry, path) if err != nil { return types.DockerAuthConfig{}, "", err } @@ -403,7 +414,7 @@ func RemoveAuthentication(sys *types.SystemContext, key string) error { switch helper { // Special-case the built-in helper for auth files. case sysregistriesv2.AuthenticationFileHelper: - _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, string, error) { if innerHelper, exists := auths.CredHelpers[key]; exists { removeFromCredHelper(innerHelper) } @@ -411,7 +422,7 @@ func RemoveAuthentication(sys *types.SystemContext, key string) error { isLoggedIn = true delete(auths.AuthConfigs, key) } - return true, multiErr + return true, "", multiErr }) if err != nil { multiErr = multierror.Append(multiErr, err) @@ -446,18 +457,18 @@ func RemoveAllAuthentication(sys *types.SystemContext) error { switch helper { // Special-case the built-in helper for auth files. case sysregistriesv2.AuthenticationFileHelper: - _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, string, error) { for registry, helper := range auths.CredHelpers { // Helpers in auth files are expected // to exist, so no special treatment // for them. if err := deleteAuthFromCredHelper(helper, registry); err != nil { - return false, err + return false, "", err } } auths.CredHelpers = make(map[string]string) auths.AuthConfigs = make(map[string]dockerAuthConfig) - return true, nil + return true, "", nil }) // External helpers. default: @@ -495,28 +506,28 @@ func listAuthsFromCredHelper(credHelper string) (map[string]string, error) { return helperclient.List(p) } -// getPathToAuth gets the path of the auth.json file used for reading and writing credentials -// returns the path, and a bool specifies whether the file is in legacy format -func getPathToAuth(sys *types.SystemContext) (string, bool, error) { +// getPathToAuth gets the path of the auth.json file used for reading and writing credentials, +// and a boolean indicating whether the return value came from an explicit user choice (i.e. not defaults) +func getPathToAuth(sys *types.SystemContext) (authPath, bool, error) { return getPathToAuthWithOS(sys, runtime.GOOS) } // getPathToAuthWithOS is an internal implementation detail of getPathToAuth, // it exists only to allow testing it with an artificial runtime.GOOS. -func getPathToAuthWithOS(sys *types.SystemContext, goOS string) (string, bool, error) { +func getPathToAuthWithOS(sys *types.SystemContext, goOS string) (authPath, bool, error) { if sys != nil { if sys.AuthFilePath != "" { - return sys.AuthFilePath, false, nil + return newAuthPathDefault(sys.AuthFilePath), true, nil } if sys.LegacyFormatAuthFilePath != "" { - return sys.LegacyFormatAuthFilePath, true, nil + return authPath{path: sys.LegacyFormatAuthFilePath, legacyFormat: true}, true, nil } if sys.RootForImplicitAbsolutePaths != "" { - return filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), false, nil + return newAuthPathDefault(filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid()))), false, nil } } if goOS == "windows" || goOS == "darwin" { - return filepath.Join(homedir.Get(), nonLinuxAuthFilePath), false, nil + return newAuthPathDefault(filepath.Join(homedir.Get(), nonLinuxAuthFilePath)), false, nil } runtimeDir := os.Getenv("XDG_RUNTIME_DIR") @@ -528,20 +539,20 @@ func getPathToAuthWithOS(sys *types.SystemContext, goOS string) (string, bool, e // This means the user set the XDG_RUNTIME_DIR variable and either forgot to create the directory // or made a typo while setting the environment variable, // so return an error referring to $XDG_RUNTIME_DIR instead of xdgRuntimeDirPath inside. - return "", false, fmt.Errorf("%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.: %w", runtimeDir, err) + return authPath{}, false, fmt.Errorf("%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.: %w", runtimeDir, err) } // else ignore err and let the caller fail accessing xdgRuntimeDirPath. - return filepath.Join(runtimeDir, xdgRuntimeDirPath), false, nil + return newAuthPathDefault(filepath.Join(runtimeDir, xdgRuntimeDirPath)), false, nil } - return fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid()), false, nil + return newAuthPathDefault(fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), false, nil } -// readJSONFile unmarshals the authentications stored in the auth.json file and returns it +// parse unmarshals the authentications stored in the auth.json file and returns it // or returns an empty dockerConfigFile data structure if auth.json does not exist -// if the file exists and is empty, readJSONFile returns an error -func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { +// if the file exists and is empty, this function returns an error. +func (path authPath) parse() (dockerConfigFile, error) { var auths dockerConfigFile - raw, err := os.ReadFile(path) + raw, err := os.ReadFile(path.path) if err != nil { if os.IsNotExist(err) { auths.AuthConfigs = map[string]dockerAuthConfig{} @@ -550,15 +561,15 @@ func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { return dockerConfigFile{}, err } - if legacyFormat { + if path.legacyFormat { if err = json.Unmarshal(raw, &auths.AuthConfigs); err != nil { - return dockerConfigFile{}, fmt.Errorf("unmarshaling JSON at %q: %w", path, err) + return dockerConfigFile{}, fmt.Errorf("unmarshaling JSON at %q: %w", path.path, err) } return auths, nil } if err = json.Unmarshal(raw, &auths); err != nil { - return dockerConfigFile{}, fmt.Errorf("unmarshaling JSON at %q: %w", path, err) + return dockerConfigFile{}, fmt.Errorf("unmarshaling JSON at %q: %w", path.path, err) } if auths.AuthConfigs == nil { @@ -573,42 +584,48 @@ func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { // modifyJSON finds an auth.json file, calls editor on the contents, and // writes it back if editor returns true. -// Returns a human-redable description of the file, to be returned by SetCredentials. -func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (bool, error)) (string, error) { - path, legacyFormat, err := getPathToAuth(sys) +// Returns a human-readable description of the file, to be returned by SetCredentials. +// +// The editor may also return a human-readable description of the updated location; if it is "", +// the file itself is used. +func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (bool, string, error)) (string, error) { + path, _, err := getPathToAuth(sys) if err != nil { return "", err } - if legacyFormat { - return "", fmt.Errorf("writes to %s using legacy format are not supported", path) + if path.legacyFormat { + return "", fmt.Errorf("writes to %s using legacy format are not supported", path.path) } - dir := filepath.Dir(path) + dir := filepath.Dir(path.path) if err = os.MkdirAll(dir, 0700); err != nil { return "", err } - auths, err := readJSONFile(path, false) + auths, err := path.parse() if err != nil { - return "", fmt.Errorf("reading JSON file %q: %w", path, err) + return "", fmt.Errorf("reading JSON file %q: %w", path.path, err) } - updated, err := editor(&auths) + updated, description, err := editor(&auths) if err != nil { - return "", fmt.Errorf("updating %q: %w", path, err) + return "", fmt.Errorf("updating %q: %w", path.path, err) } if updated { newData, err := json.MarshalIndent(auths, "", "\t") if err != nil { - return "", fmt.Errorf("marshaling JSON %q: %w", path, err) + return "", fmt.Errorf("marshaling JSON %q: %w", path.path, err) } - if err = ioutils.AtomicWriteFile(path, newData, 0600); err != nil { - return "", fmt.Errorf("writing to file %q: %w", path, err) + if err = ioutils.AtomicWriteFile(path.path, newData, 0600); err != nil { + return "", fmt.Errorf("writing to file %q: %w", path.path, err) } } - return path, nil + if description == "" { + description = path.path + } + return description, nil } func getAuthFromCredHelper(credHelper, registry string) (types.DockerAuthConfig, error) { @@ -636,7 +653,9 @@ func getAuthFromCredHelper(credHelper, registry string) (types.DockerAuthConfig, } } -func setAuthToCredHelper(credHelper, registry, username, password string) error { +// setAuthToCredHelper stores (username, password) for registry in credHelper. +// Returns a human-readable description of the destination, to be returned by SetCredentials. +func setAuthToCredHelper(credHelper, registry, username, password string) (string, error) { helperName := fmt.Sprintf("docker-credential-%s", credHelper) p := helperclient.NewShellProgramFunc(helperName) creds := &credentials.Credentials{ @@ -644,7 +663,10 @@ func setAuthToCredHelper(credHelper, registry, username, password string) error Username: username, Secret: password, } - return helperclient.Store(p, creds) + if err := helperclient.Store(p, creds); err != nil { + return "", err + } + return fmt.Sprintf("credential helper: %s", credHelper), nil } func deleteAuthFromCredHelper(credHelper, registry string) error { @@ -655,17 +677,17 @@ func deleteAuthFromCredHelper(credHelper, registry string) error { // findCredentialsInFile looks for credentials matching "key" // (which is "registry" or a namespace in "registry") in "path". -func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) { - auths, err := readJSONFile(path, legacyFormat) +func findCredentialsInFile(key, registry string, path authPath) (types.DockerAuthConfig, error) { + auths, err := path.parse() if err != nil { - return types.DockerAuthConfig{}, fmt.Errorf("reading JSON file %q: %w", path, err) + return types.DockerAuthConfig{}, fmt.Errorf("reading JSON file %q: %w", path.path, err) } // First try cred helpers. They should always be normalized. // This intentionally uses "registry", not "key"; we don't support namespaced // credentials in helpers. if ch, exists := auths.CredHelpers[registry]; exists { - logrus.Debugf("Looking up in credential helper %s based on credHelpers entry in %s", ch, path) + logrus.Debugf("Looking up in credential helper %s based on credHelpers entry in %s", ch, path.path) return getAuthFromCredHelper(ch, registry) } @@ -673,7 +695,7 @@ func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types // (This is not a feature of ~/.docker/config.json; we support it even for // those files as an extension.) var keys []string - if !legacyFormat { + if !path.legacyFormat { keys = authKeysForKey(key) } else { keys = []string{registry} @@ -683,7 +705,7 @@ func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types // keys we prefer exact matches as well. for _, key := range keys { if val, exists := auths.AuthConfigs[key]; exists { - return decodeDockerAuth(path, key, val) + return decodeDockerAuth(path.path, key, val) } } @@ -697,14 +719,14 @@ func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types // so account for that as well. registry = normalizeRegistry(registry) for k, v := range auths.AuthConfigs { - if normalizeAuthFileKey(k, legacyFormat) == registry { - return decodeDockerAuth(path, k, v) + if normalizeAuthFileKey(k, path.legacyFormat) == registry { + return decodeDockerAuth(path.path, k, v) } } // Only log this if we found nothing; getCredentialsWithHomeDir logs the // source of found data. - logrus.Debugf("No credentials matching %s found in %s", key, path) + logrus.Debugf("No credentials matching %s found in %s", key, path.path) return types.DockerAuthConfig{}, nil } diff --git a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go index 8793711f..eeb7c1ef 100644 --- a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go +++ b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go @@ -59,8 +59,6 @@ func parseUnnormalizedShortName(input string) (bool, reference.Named, error) { // the tag or digest and stores it in the return values so that both can be // re-added to a possible resolved alias' or USRs at a later point. func splitUserInput(named reference.Named) (isTagged bool, isDigested bool, normalized reference.Named, tag string, digest digest.Digest) { - normalized = named - tagged, isT := named.(reference.NamedTagged) if isT { isTagged = true @@ -170,7 +168,7 @@ func (r *Resolved) Description() string { // Note that nil is returned if len(pullErrors) == 0. Otherwise, the amount of // pull errors must equal the amount of pull candidates. func (r *Resolved) FormatPullErrors(pullErrors []error) error { - if len(pullErrors) >= 0 && len(pullErrors) != len(r.PullCandidates) { + if len(pullErrors) > 0 && len(pullErrors) != len(r.PullCandidates) { pullErrors = append(pullErrors, fmt.Errorf("internal error: expected %d instead of %d errors for %d pull candidates", len(r.PullCandidates), len(pullErrors), len(r.PullCandidates))) diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go index 12939b24..7ebd3fd2 100644 --- a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go @@ -335,7 +335,7 @@ func loadShortNameAliasConf(confPath string) (*shortNameAliasConf, *shortNameAli return &conf, cache, nil } -func shortNameAliasesConfPathAndLock(ctx *types.SystemContext) (string, lockfile.Locker, error) { +func shortNameAliasesConfPathAndLock(ctx *types.SystemContext) (string, *lockfile.LockFile, error) { shortNameAliasesConfPath, err := shortNameAliasesConfPath(ctx) if err != nil { return "", nil, err @@ -346,6 +346,6 @@ func shortNameAliasesConfPathAndLock(ctx *types.SystemContext) (string, lockfile } lockPath := shortNameAliasesConfPath + ".lock" - locker, err := lockfile.GetLockfile(lockPath) + locker, err := lockfile.GetLockFile(lockPath) return shortNameAliasesConfPath, locker, err } diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go index 41204dd9..463e7702 100644 --- a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "reflect" - "regexp" "sort" "strings" "sync" @@ -15,6 +14,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/homedir" + "github.com/containers/storage/pkg/regexp" "github.com/sirupsen/logrus" ) @@ -198,6 +198,7 @@ type V1RegistriesConf struct { } // Nonempty returns true if config contains at least one configuration entry. +// Empty arrays are treated as missing entries. func (config *V1RegistriesConf) Nonempty() bool { copy := *config // A shallow copy if copy.V1TOMLConfig.Search.Registries != nil && len(copy.V1TOMLConfig.Search.Registries) == 0 { @@ -209,7 +210,15 @@ func (config *V1RegistriesConf) Nonempty() bool { if copy.V1TOMLConfig.Block.Registries != nil && len(copy.V1TOMLConfig.Block.Registries) == 0 { copy.V1TOMLConfig.Block.Registries = nil } - return !reflect.DeepEqual(copy, V1RegistriesConf{}) + return copy.hasSetField() +} + +// hasSetField returns true if config contains at least one configuration entry. +// This is useful because of a subtlety of the behavior of the TOML decoder, where a missing array field +// is not modified while unmarshaling (in our case remains to nil), while an [] is unmarshaled +// as a non-nil []string{}. +func (config *V1RegistriesConf) hasSetField() bool { + return !reflect.DeepEqual(*config, V1RegistriesConf{}) } // V2RegistriesConf is the sysregistries v2 configuration format. @@ -257,7 +266,15 @@ func (config *V2RegistriesConf) Nonempty() bool { if !copy.shortNameAliasConf.nonempty() { copy.shortNameAliasConf = shortNameAliasConf{} } - return !reflect.DeepEqual(copy, V2RegistriesConf{}) + return copy.hasSetField() +} + +// hasSetField returns true if config contains at least one configuration entry. +// This is useful because of a subtlety of the behavior of the TOML decoder, where a missing array field +// is not modified while unmarshaling (in our case remains to nil), while an [] is unmarshaled +// as a non-nil []string{}. +func (config *V2RegistriesConf) hasSetField() bool { + return !reflect.DeepEqual(*config, V2RegistriesConf{}) } // parsedConfig is the result of parsing, and possibly merging, configuration files; @@ -367,7 +384,7 @@ func (config *V1RegistriesConf) ConvertToV2() (*V2RegistriesConf, error) { } // anchoredDomainRegexp is an internal implementation detail of postProcess, defining the valid values of elements of UnqualifiedSearchRegistries. -var anchoredDomainRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "$") +var anchoredDomainRegexp = regexp.Delayed("^" + reference.DomainRegexp.String() + "$") // postProcess checks the consistency of all the configuration, looks for conflicts, // and normalizes the configuration (e.g., sets the Prefix to Location if not set). @@ -923,15 +940,15 @@ func loadConfigFile(path string, forceV2 bool) (*parsedConfig, error) { logrus.Debugf("Failed to decode keys %q from %q", keys, path) } - if combinedTOML.V1RegistriesConf.Nonempty() { + if combinedTOML.V1RegistriesConf.hasSetField() { // Enforce the v2 format if requested. if forceV2 { return nil, &InvalidRegistries{s: "registry must be in v2 format but is in v1"} } // Convert a v1 config into a v2 config. - if combinedTOML.V2RegistriesConf.Nonempty() { - return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"} + if combinedTOML.V2RegistriesConf.hasSetField() { + return nil, &InvalidRegistries{s: fmt.Sprintf("mixing sysregistry v1/v2 is not supported: %#v", combinedTOML)} } converted, err := combinedTOML.V1RegistriesConf.ConvertToV2() if err != nil { diff --git a/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go b/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go index 9599aa3c..285203ba 100644 --- a/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go +++ b/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go @@ -2,6 +2,7 @@ package tlsclientconfig import ( "crypto/tls" + "crypto/x509" "fmt" "net" "net/http" @@ -10,8 +11,6 @@ import ( "strings" "time" - "github.com/docker/go-connections/sockets" - "github.com/docker/go-connections/tlsconfig" "github.com/sirupsen/logrus" ) @@ -47,7 +46,7 @@ func SetupCertificates(dir string, tlsc *tls.Config) error { return err } if tlsc.RootCAs == nil { - systemPool, err := tlsconfig.SystemCertPool() + systemPool, err := x509.SystemCertPool() if err != nil { return fmt.Errorf("unable to get system cert pool: %w", err) } @@ -103,8 +102,5 @@ func NewTransport() *http.Transport { // TODO(dmcgowan): Call close idle connections when complete and use keep alive DisableKeepAlives: true, } - if _, err := sockets.DialerFromEnvironment(direct); err != nil { - logrus.Debugf("Can't execute DialerFromEnvironment: %v", err) - } return tr } diff --git a/vendor/github.com/containers/image/v5/signature/fulcio_cert.go b/vendor/github.com/containers/image/v5/signature/fulcio_cert.go new file mode 100644 index 00000000..2e7f44ce --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/fulcio_cert.go @@ -0,0 +1,174 @@ +package signature + +import ( + "crypto" + "crypto/ecdsa" + "crypto/x509" + "encoding/asn1" + "errors" + "fmt" + "time" + + "github.com/containers/image/v5/signature/internal" + "github.com/sigstore/fulcio/pkg/certificate" + "github.com/sigstore/sigstore/pkg/cryptoutils" +) + +// fulcioTrustRoot contains policy allow validating Fulcio-issued certificates. +// Users should call validate() on the policy before using it. +type fulcioTrustRoot struct { + caCertificates *x509.CertPool + oidcIssuer string + subjectEmail string +} + +func (f *fulcioTrustRoot) validate() error { + if f.oidcIssuer == "" { + return errors.New("Internal inconsistency: Fulcio use set up without OIDC issuer") + } + if f.subjectEmail == "" { + return errors.New("Internal inconsistency: Fulcio use set up without subject email") + } + return nil +} + +func (f *fulcioTrustRoot) verifyFulcioCertificateAtTime(relevantTime time.Time, untrustedCertificateBytes []byte, untrustedIntermediateChainBytes []byte) (crypto.PublicKey, error) { + // == Verify the certificate is correctly signed + var untrustedIntermediatePool *x509.CertPool // = nil + // untrustedCertificateChainPool.AppendCertsFromPEM does something broadly similar, + // but it seems to optimize for memory usage at the cost of larger CPU usage (i.e. to load + // the hundreds of trusted CAs). Golang’s TLS code similarly calls individual AddCert + // for intermediate certificates. + if len(untrustedIntermediateChainBytes) > 0 { + untrustedIntermediateChain, err := cryptoutils.UnmarshalCertificatesFromPEM(untrustedIntermediateChainBytes) + if err != nil { + return nil, internal.NewInvalidSignatureError(fmt.Sprintf("loading certificate chain: %v", err)) + } + untrustedIntermediatePool = x509.NewCertPool() + if len(untrustedIntermediateChain) > 1 { + for _, untrustedIntermediateCert := range untrustedIntermediateChain[:len(untrustedIntermediateChain)-1] { + untrustedIntermediatePool.AddCert(untrustedIntermediateCert) + } + } + } + + untrustedLeafCerts, err := cryptoutils.UnmarshalCertificatesFromPEM(untrustedCertificateBytes) + if err != nil { + return nil, internal.NewInvalidSignatureError(fmt.Sprintf("parsing leaf certificate: %v", err)) + } + switch len(untrustedLeafCerts) { + case 0: + return nil, internal.NewInvalidSignatureError("no certificate found in signature certificate data") + case 1: + break // OK + default: + return nil, internal.NewInvalidSignatureError("unexpected multiple certificates present in signature certificate data") + } + untrustedCertificate := untrustedLeafCerts[0] + + // Go rejects Subject Alternative Name that has no DNSNames, EmailAddresses, IPAddresses and URIs; + // we match SAN ourselves, so override that. + if len(untrustedCertificate.UnhandledCriticalExtensions) > 0 { + var remaining []asn1.ObjectIdentifier + for _, oid := range untrustedCertificate.UnhandledCriticalExtensions { + if !oid.Equal(cryptoutils.SANOID) { + remaining = append(remaining, oid) + } + } + untrustedCertificate.UnhandledCriticalExtensions = remaining + } + + if _, err := untrustedCertificate.Verify(x509.VerifyOptions{ + Intermediates: untrustedIntermediatePool, + Roots: f.caCertificates, + // NOTE: Cosign uses untrustedCertificate.NotBefore here (i.e. uses _that_ time for intermediate certificate validation), + // and validates the leaf certificate against relevantTime manually. + // We verify the full certificate chain against relevantTime instead. + // Assuming the certificate is fulcio-generated and very short-lived, that should make little difference. + CurrentTime: relevantTime, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + }); err != nil { + return nil, internal.NewInvalidSignatureError(fmt.Sprintf("veryfing leaf certificate failed: %v", err)) + } + + // Cosign verifies a SCT of the certificate (either embedded, or even, probably irrelevant, externally-supplied). + // + // We don’t currently do that. + // + // At the very least, with Fulcio we require Rekor SETs to prove Rekor contains a log of the signature, and that + // already contains the full certificate; so a SCT of the certificate is superfluous (assuming Rekor allowed searching by + // certificate subject, which, well…). That argument might go away if we add support for RFC 3161 timestamps instead of Rekor. + // + // Secondarily, assuming a trusted Fulcio server (which, to be fair, might not be the case for the public one) SCT is not clearly + // better than the Fulcio server maintaining an audit log; a SCT can only reveal a misissuance if there is some other authoritative + // log of approved Fulcio invocations, and it’s not clear where that would come from, especially human users manually + // logging in using OpenID are not going to maintain a record of those actions. + // + // Also, the SCT does not help reveal _what_ was maliciously signed, nor does it protect against malicous signatures + // by correctly-issued certificates. + // + // So, pragmatically, the ideal design seem to be to only do signatures from a trusted build system (which is, by definition, + // the arbiter of desired vs. malicious signatures) that maintains an audit log of performed signature operations; and that seems to + // make make the SCT (and all of Rekor apart from the trusted timestamp) unnecessary. + + // == Validate the recorded OIDC issuer + gotOIDCIssuer := false + var oidcIssuer string + // certificate.ParseExtensions doesn’t reject duplicate extensions. + // Go 1.19 rejects duplicate extensions universally; but until we can require Go 1.19, + // reject duplicates manually. With Go 1.19, we could call certificate.ParseExtensions again. + for _, untrustedExt := range untrustedCertificate.Extensions { + if untrustedExt.Id.Equal(certificate.OIDIssuer) { + if gotOIDCIssuer { + // Coverage: This is unreachable in Go ≥1.19, which rejects certificates with duplicate extensions + // already in ParseCertificate. + return nil, internal.NewInvalidSignatureError("Fulcio certificate has a duplicate OIDC issuer extension") + } + oidcIssuer = string(untrustedExt.Value) + gotOIDCIssuer = true + } + } + if !gotOIDCIssuer { + return nil, internal.NewInvalidSignatureError("Fulcio certificate is missing the issuer extension") + } + if oidcIssuer != f.oidcIssuer { + return nil, internal.NewInvalidSignatureError(fmt.Sprintf("Unexpected Fulcio OIDC issuer %q", oidcIssuer)) + } + + // == Validate the OIDC subject + foundEmail := false + // TO DO: Use slices.Contains after we update to Go 1.18 + for _, certEmail := range untrustedCertificate.EmailAddresses { + if certEmail == f.subjectEmail { + foundEmail = true + break + } + } + if !foundEmail { + return nil, internal.NewInvalidSignatureError(fmt.Sprintf("Required email %s not found (got %#v)", + f.subjectEmail, + untrustedCertificate.EmailAddresses)) + } + // FIXME: Match more subject types? Cosign does: + // - .DNSNames (can’t be issued by Fulcio) + // - .IPAddresses (can’t be issued by Fulcio) + // - .URIs (CAN be issued by Fulcio) + // - OtherName values in SAN (CAN be issued by Fulcio) + // - Various values about GitHub workflows (CAN be issued by Fulcio) + // What does it… mean to get an OAuth2 identity for an IP address? + // FIXME: How far into Turing-completeness for the issuer/subject do we need to get? Simultaneously accepted alternatives, for + // issuers and/or subjects and/or combinations? Regexps? More? + + return untrustedCertificate.PublicKey, nil +} + +func verifyRekorFulcio(rekorPublicKey *ecdsa.PublicKey, fulcioTrustRoot *fulcioTrustRoot, untrustedRekorSET []byte, + untrustedCertificateBytes []byte, untrustedIntermediateChainBytes []byte, untrustedBase64Signature string, + untrustedPayloadBytes []byte) (crypto.PublicKey, error) { + rekorSETTime, err := internal.VerifyRekorSET(rekorPublicKey, untrustedRekorSET, untrustedCertificateBytes, + untrustedBase64Signature, untrustedPayloadBytes) + if err != nil { + return nil, err + } + return fulcioTrustRoot.verifyFulcioCertificateAtTime(rekorSETTime, untrustedCertificateBytes, untrustedIntermediateChainBytes) +} diff --git a/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go b/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go new file mode 100644 index 00000000..27c2c7e6 --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go @@ -0,0 +1,237 @@ +package internal + +import ( + "bytes" + "crypto/ecdsa" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "time" + + "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" + "github.com/sigstore/rekor/pkg/generated/models" +) + +// This is the github.com/sigstore/rekor/pkg/generated/models.Hashedrekord.APIVersion for github.com/sigstore/rekor/pkg/generated/models.HashedrekordV001Schema. +// We could alternatively use github.com/sigstore/rekor/pkg/types/hashedrekord.APIVERSION, but that subpackage adds too many dependencies. +const HashedRekordV001APIVersion = "0.0.1" + +// UntrustedRekorSET is a parsed content of the sigstore-signature Rekor SET +// (note that this a signature-specific format, not a format directly used by the Rekor API). +// This corresponds to github.com/sigstore/cosign/bundle.RekorBundle, but we impose a stricter decoder. +type UntrustedRekorSET struct { + UntrustedSignedEntryTimestamp []byte // A signature over some canonical JSON form of UntrustedPayload + UntrustedPayload json.RawMessage +} + +type UntrustedRekorPayload struct { + Body []byte // In cosign, this is an interface{}, but only a string works + IntegratedTime int64 + LogIndex int64 + LogID string +} + +// A compile-time check that UntrustedRekorSET implements json.Unmarshaler +var _ json.Unmarshaler = (*UntrustedRekorSET)(nil) + +// UnmarshalJSON implements the json.Unmarshaler interface +func (s *UntrustedRekorSET) UnmarshalJSON(data []byte) error { + err := s.strictUnmarshalJSON(data) + if err != nil { + if formatErr, ok := err.(JSONFormatError); ok { + err = NewInvalidSignatureError(formatErr.Error()) + } + } + return err +} + +// strictUnmarshalJSON is UnmarshalJSON, except that it may return the internal JSONFormatError error type. +// Splitting it into a separate function allows us to do the JSONFormatError → InvalidSignatureError in a single place, the caller. +func (s *UntrustedRekorSET) strictUnmarshalJSON(data []byte) error { + return ParanoidUnmarshalJSONObjectExactFields(data, map[string]interface{}{ + "SignedEntryTimestamp": &s.UntrustedSignedEntryTimestamp, + "Payload": &s.UntrustedPayload, + }) +} + +// A compile-time check that UntrustedRekorSET and *UntrustedRekorSET implements json.Marshaler +var _ json.Marshaler = UntrustedRekorSET{} +var _ json.Marshaler = (*UntrustedRekorSET)(nil) + +// MarshalJSON implements the json.Marshaler interface. +func (s UntrustedRekorSET) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "SignedEntryTimestamp": s.UntrustedSignedEntryTimestamp, + "Payload": s.UntrustedPayload, + }) +} + +// A compile-time check that UntrustedRekorPayload implements json.Unmarshaler +var _ json.Unmarshaler = (*UntrustedRekorPayload)(nil) + +// UnmarshalJSON implements the json.Unmarshaler interface +func (p *UntrustedRekorPayload) UnmarshalJSON(data []byte) error { + err := p.strictUnmarshalJSON(data) + if err != nil { + if formatErr, ok := err.(JSONFormatError); ok { + err = NewInvalidSignatureError(formatErr.Error()) + } + } + return err +} + +// strictUnmarshalJSON is UnmarshalJSON, except that it may return the internal JSONFormatError error type. +// Splitting it into a separate function allows us to do the JSONFormatError → InvalidSignatureError in a single place, the caller. +func (p *UntrustedRekorPayload) strictUnmarshalJSON(data []byte) error { + return ParanoidUnmarshalJSONObjectExactFields(data, map[string]interface{}{ + "body": &p.Body, + "integratedTime": &p.IntegratedTime, + "logIndex": &p.LogIndex, + "logID": &p.LogID, + }) +} + +// A compile-time check that UntrustedRekorPayload and *UntrustedRekorPayload implements json.Marshaler +var _ json.Marshaler = UntrustedRekorPayload{} +var _ json.Marshaler = (*UntrustedRekorPayload)(nil) + +// MarshalJSON implements the json.Marshaler interface. +func (p UntrustedRekorPayload) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "body": p.Body, + "integratedTime": p.IntegratedTime, + "logIndex": p.LogIndex, + "logID": p.LogID, + }) +} + +// VerifyRekorSET verifies that unverifiedRekorSET is correctly signed by publicKey and matches the rest of the data. +// Returns bundle upload time on success. +func VerifyRekorSET(publicKey *ecdsa.PublicKey, unverifiedRekorSET []byte, unverifiedKeyOrCertBytes []byte, unverifiedBase64Signature string, unverifiedPayloadBytes []byte) (time.Time, error) { + // FIXME: Should the publicKey parameter hard-code ecdsa? + + // == Parse SET bytes + var untrustedSET UntrustedRekorSET + // Sadly. we need to parse and transform untrusted data before verifying a cryptographic signature... + if err := json.Unmarshal(unverifiedRekorSET, &untrustedSET); err != nil { + return time.Time{}, NewInvalidSignatureError(err.Error()) + } + // == Verify SET signature + // Cosign unmarshals and re-marshals UntrustedPayload; that seems unnecessary, + // assuming jsoncanonicalizer is designed to operate on untrusted data. + untrustedSETPayloadCanonicalBytes, err := jsoncanonicalizer.Transform(untrustedSET.UntrustedPayload) + if err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("canonicalizing Rekor SET JSON: %v", err)) + } + untrustedSETPayloadHash := sha256.Sum256(untrustedSETPayloadCanonicalBytes) + if !ecdsa.VerifyASN1(publicKey, untrustedSETPayloadHash[:], untrustedSET.UntrustedSignedEntryTimestamp) { + return time.Time{}, NewInvalidSignatureError("cryptographic signature verification of Rekor SET failed") + } + + // == Parse SET payload + // Parse the cryptographically-verified canonicalized variant, NOT the originally-delivered representation, + // to decrease risk of exploiting the JSON parser. Note that if there were an arbitrary execution vulnerability, the attacker + // could have exploited the parsing of unverifiedRekorSET above already; so this, at best, ensures more consistent processing + // of the SET payload. + var rekorPayload UntrustedRekorPayload + if err := json.Unmarshal(untrustedSETPayloadCanonicalBytes, &rekorPayload); err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("parsing Rekor SET payload: %v", err.Error())) + } + // FIXME: Use a different decoder implementation? The Swagger-generated code is kinda ridiculous, with the need to re-marshal + // hashedRekor.Spec and so on. + // Especially if we anticipate needing to decode different data formats… + // That would also allow being much more strict about JSON. + // + // Alternatively, rely on the existing .Validate() methods instead of manually checking for nil all over the place. + var hashedRekord models.Hashedrekord + if err := json.Unmarshal(rekorPayload.Body, &hashedRekord); err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("decoding the body of a Rekor SET payload: %v", err)) + } + // The decode of models.HashedRekord validates the "kind": "hashedrecord" field, which is otherwise invisible to us. + if hashedRekord.APIVersion == nil { + return time.Time{}, NewInvalidSignatureError("missing Rekor SET Payload API version") + } + if *hashedRekord.APIVersion != HashedRekordV001APIVersion { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("unsupported Rekor SET Payload hashedrekord version %#v", hashedRekord.APIVersion)) + } + hashedRekordV001Bytes, err := json.Marshal(hashedRekord.Spec) + if err != nil { + // Coverage: hashedRekord.Spec is an interface{} that was just unmarshaled, + // so this should never fail. + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("re-creating hashedrekord spec: %v", err)) + } + var hashedRekordV001 models.HashedrekordV001Schema + if err := json.Unmarshal(hashedRekordV001Bytes, &hashedRekordV001); err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("decoding hashedrekod spec: %v", err)) + } + + // == Match unverifiedKeyOrCertBytes + if hashedRekordV001.Signature == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "signature" field in hashedrekord`) + } + if hashedRekordV001.Signature.PublicKey == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "signature.publicKey" field in hashedrekord`) + + } + rekorKeyOrCertPEM, rest := pem.Decode(hashedRekordV001.Signature.PublicKey.Content) + if rekorKeyOrCertPEM == nil { + return time.Time{}, NewInvalidSignatureError("publicKey in Rekor SET is not in PEM format") + } + if len(rest) != 0 { + return time.Time{}, NewInvalidSignatureError("publicKey in Rekor SET has trailing data") + } + // FIXME: For public keys, let the caller provide the DER-formatted blob instead + // of round-tripping through PEM. + unverifiedKeyOrCertPEM, rest := pem.Decode(unverifiedKeyOrCertBytes) + if unverifiedKeyOrCertPEM == nil { + return time.Time{}, NewInvalidSignatureError("public key or cert to be matched against publicKey in Rekor SET is not in PEM format") + } + if len(rest) != 0 { + return time.Time{}, NewInvalidSignatureError("public key or cert to be matched against publicKey in Rekor SET has trailing data") + } + // NOTE: This compares the PEM payload, but not the object type or headers. + if !bytes.Equal(rekorKeyOrCertPEM.Bytes, unverifiedKeyOrCertPEM.Bytes) { + return time.Time{}, NewInvalidSignatureError("publicKey in Rekor SET does not match") + } + // == Match unverifiedSignatureBytes + unverifiedSignatureBytes, err := base64.StdEncoding.DecodeString(unverifiedBase64Signature) + if err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("decoding signature base64: %v", err)) + } + if !bytes.Equal(hashedRekordV001.Signature.Content, unverifiedSignatureBytes) { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("signature in Rekor SET does not match: %#v vs. %#v", + string(hashedRekordV001.Signature.Content), string(unverifiedSignatureBytes))) + } + + // == Match unverifiedPayloadBytes + if hashedRekordV001.Data == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "data" field in hashedrekord`) + } + if hashedRekordV001.Data.Hash == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "data.hash" field in hashedrekord`) + } + if hashedRekordV001.Data.Hash.Algorithm == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "data.hash.algorithm" field in hashedrekord`) + } + if *hashedRekordV001.Data.Hash.Algorithm != models.HashedrekordV001SchemaDataHashAlgorithmSha256 { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf(`Unexpected "data.hash.algorithm" value %#v`, *hashedRekordV001.Data.Hash.Algorithm)) + } + if hashedRekordV001.Data.Hash.Value == nil { + return time.Time{}, NewInvalidSignatureError(`Missing "data.hash.value" field in hashedrekord`) + } + rekorPayloadHash, err := hex.DecodeString(*hashedRekordV001.Data.Hash.Value) + if err != nil { + return time.Time{}, NewInvalidSignatureError(fmt.Sprintf(`Invalid "data.hash.value" field in hashedrekord: %v`, err)) + + } + unverifiedPayloadHash := sha256.Sum256(unverifiedPayloadBytes) + if !bytes.Equal(rekorPayloadHash, unverifiedPayloadHash[:]) { + return time.Time{}, NewInvalidSignatureError("payload in Rekor SET does not match") + } + + // == All OK; return the relevant time. + return time.Unix(rekorPayload.IntegratedTime, 0), nil +} diff --git a/vendor/github.com/containers/image/v5/signature/internal/sigstore_payload.go b/vendor/github.com/containers/image/v5/signature/internal/sigstore_payload.go index bb5e9139..afacd820 100644 --- a/vendor/github.com/containers/image/v5/signature/internal/sigstore_payload.go +++ b/vendor/github.com/containers/image/v5/signature/internal/sigstore_payload.go @@ -46,7 +46,8 @@ func NewUntrustedSigstorePayload(dockerManifestDigest digest.Digest, dockerRefer } } -// Compile-time check that UntrustedSigstorePayload implements json.Marshaler +// A compile-time check that UntrustedSigstorePayload and *UntrustedSigstorePayload implements json.Marshaler +var _ json.Marshaler = UntrustedSigstorePayload{} var _ json.Marshaler = (*UntrustedSigstorePayload)(nil) // MarshalJSON implements the json.Marshaler interface. diff --git a/vendor/github.com/containers/image/v5/signature/policy_config.go b/vendor/github.com/containers/image/v5/signature/policy_config.go index f8fdce2d..5ca4ad71 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_config.go +++ b/vendor/github.com/containers/image/v5/signature/policy_config.go @@ -19,13 +19,13 @@ import ( "fmt" "os" "path/filepath" - "regexp" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/signature/internal" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/homedir" + "github.com/containers/storage/pkg/regexp" ) // systemDefaultPolicyPath is the policy path used for DefaultPolicy(). @@ -518,107 +518,6 @@ func (pr *prSignedBaseLayer) UnmarshalJSON(data []byte) error { return nil } -// newPRSigstoreSigned returns a new prSigstoreSigned if parameters are valid. -func newPRSigstoreSigned(keyPath string, keyData []byte, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) { - if len(keyPath) > 0 && len(keyData) > 0 { - return nil, InvalidPolicyFormatError("keyType and keyData cannot be used simultaneously") - } - if signedIdentity == nil { - return nil, InvalidPolicyFormatError("signedIdentity not specified") - } - return &prSigstoreSigned{ - prCommon: prCommon{Type: prTypeSigstoreSigned}, - KeyPath: keyPath, - KeyData: keyData, - SignedIdentity: signedIdentity, - }, nil -} - -// newPRSigstoreSignedKeyPath is NewPRSigstoreSignedKeyPath, except it returns the private type. -func newPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) { - return newPRSigstoreSigned(keyPath, nil, signedIdentity) -} - -// NewPRSigstoreSignedKeyPath returns a new "sigstoreSigned" PolicyRequirement using a KeyPath -func NewPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) { - return newPRSigstoreSignedKeyPath(keyPath, signedIdentity) -} - -// newPRSigstoreSignedKeyData is NewPRSigstoreSignedKeyData, except it returns the private type. -func newPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) { - return newPRSigstoreSigned("", keyData, signedIdentity) -} - -// NewPRSigstoreSignedKeyData returns a new "sigstoreSigned" PolicyRequirement using a KeyData -func NewPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) { - return newPRSigstoreSignedKeyData(keyData, signedIdentity) -} - -// Compile-time check that prSigstoreSigned implements json.Unmarshaler. -var _ json.Unmarshaler = (*prSigstoreSigned)(nil) - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (pr *prSigstoreSigned) UnmarshalJSON(data []byte) error { - *pr = prSigstoreSigned{} - var tmp prSigstoreSigned - var gotKeyPath, gotKeyData = false, false - var signedIdentity json.RawMessage - if err := internal.ParanoidUnmarshalJSONObject(data, func(key string) interface{} { - switch key { - case "type": - return &tmp.Type - case "keyPath": - gotKeyPath = true - return &tmp.KeyPath - case "keyData": - gotKeyData = true - return &tmp.KeyData - case "signedIdentity": - return &signedIdentity - default: - return nil - } - }); err != nil { - return err - } - - if tmp.Type != prTypeSigstoreSigned { - return InvalidPolicyFormatError(fmt.Sprintf("Unexpected policy requirement type \"%s\"", tmp.Type)) - } - if signedIdentity == nil { - tmp.SignedIdentity = NewPRMMatchRepoDigestOrExact() - } else { - si, err := newPolicyReferenceMatchFromJSON(signedIdentity) - if err != nil { - return err - } - tmp.SignedIdentity = si - } - - var res *prSigstoreSigned - var err error - switch { - case gotKeyPath && gotKeyData: - return InvalidPolicyFormatError("keyPath and keyData cannot be used simultaneously") - case gotKeyPath && !gotKeyData: - res, err = newPRSigstoreSignedKeyPath(tmp.KeyPath, tmp.SignedIdentity) - case !gotKeyPath && gotKeyData: - res, err = newPRSigstoreSignedKeyData(tmp.KeyData, tmp.SignedIdentity) - case !gotKeyPath && !gotKeyData: - return InvalidPolicyFormatError("At least one of keyPath and keyData must be specified") - default: // Coverage: This should never happen - return fmt.Errorf("Impossible keyPath/keyData presence combination!?") - } - if err != nil { - // Coverage: This cannot currently happen, creating a prSigstoreSigned only fails - // if signedIdentity is nil, which we replace with a default above. - return err - } - *pr = *res - - return nil -} - // newPolicyReferenceMatchFromJSON parses JSON data into a PolicyReferenceMatch implementation. func newPolicyReferenceMatchFromJSON(data []byte) (PolicyReferenceMatch, error) { var typeField prmCommon @@ -829,12 +728,12 @@ func (prm *prmExactRepository) UnmarshalJSON(data []byte) error { // Private objects for validateIdentityRemappingPrefix var ( // remapIdentityDomainRegexp matches exactly a reference domain (name[:port]) - remapIdentityDomainRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "$") + remapIdentityDomainRegexp = regexp.Delayed("^" + reference.DomainRegexp.String() + "$") // remapIdentityDomainPrefixRegexp matches a reference that starts with a domain; // we need this because reference.NameRegexp accepts short names with docker.io implied. - remapIdentityDomainPrefixRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "/") + remapIdentityDomainPrefixRegexp = regexp.Delayed("^" + reference.DomainRegexp.String() + "/") // remapIdentityNameRegexp matches exactly a reference.Named name (possibly unnormalized) - remapIdentityNameRegexp = regexp.MustCompile("^" + reference.NameRegexp.String() + "$") + remapIdentityNameRegexp = regexp.Delayed("^" + reference.NameRegexp.String() + "$") ) // validateIdentityRemappingPrefix returns an InvalidPolicyFormatError if s is detected to be invalid diff --git a/vendor/github.com/containers/image/v5/signature/policy_config_sigstore.go b/vendor/github.com/containers/image/v5/signature/policy_config_sigstore.go new file mode 100644 index 00000000..eeec6dc3 --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/policy_config_sigstore.go @@ -0,0 +1,343 @@ +package signature + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/containers/image/v5/signature/internal" +) + +// PRSigstoreSignedOption is way to pass values to NewPRSigstoreSigned +type PRSigstoreSignedOption func(*prSigstoreSigned) error + +// PRSigstoreSignedWithKeyPath specifies a value for the "keyPath" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithKeyPath(keyPath string) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.KeyPath != "" { + return errors.New(`"keyPath" already specified`) + } + pr.KeyPath = keyPath + return nil + } +} + +// PRSigstoreSignedWithKeyData specifies a value for the "keyData" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithKeyData(keyData []byte) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.KeyData != nil { + return errors.New(`"keyData" already specified`) + } + pr.KeyData = keyData + return nil + } +} + +// PRSigstoreSignedWithFulcio specifies a value for the "fulcio" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithFulcio(fulcio PRSigstoreSignedFulcio) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.Fulcio != nil { + return errors.New(`"fulcio" already specified`) + } + pr.Fulcio = fulcio + return nil + } +} + +// PRSigstoreSignedWithRekorPublicKeyPath specifies a value for the "rekorPublicKeyPath" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithRekorPublicKeyPath(rekorPublicKeyPath string) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.RekorPublicKeyPath != "" { + return errors.New(`"rekorPublicKeyPath" already specified`) + } + pr.RekorPublicKeyPath = rekorPublicKeyPath + return nil + } +} + +// PRSigstoreSignedWithRekorPublicKeyData specifies a value for the "rekorPublicKeyData" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithRekorPublicKeyData(rekorPublicKeyData []byte) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.RekorPublicKeyData != nil { + return errors.New(`"rekorPublicKeyData" already specified`) + } + pr.RekorPublicKeyData = rekorPublicKeyData + return nil + } +} + +// PRSigstoreSignedWithSignedIdentity specifies a value for the "signedIdentity" field when calling NewPRSigstoreSigned. +func PRSigstoreSignedWithSignedIdentity(signedIdentity PolicyReferenceMatch) PRSigstoreSignedOption { + return func(pr *prSigstoreSigned) error { + if pr.SignedIdentity != nil { + return errors.New(`"signedIdentity" already specified`) + } + pr.SignedIdentity = signedIdentity + return nil + } +} + +// newPRSigstoreSigned is NewPRSigstoreSigned, except it returns the private type. +func newPRSigstoreSigned(options ...PRSigstoreSignedOption) (*prSigstoreSigned, error) { + res := prSigstoreSigned{ + prCommon: prCommon{Type: prTypeSigstoreSigned}, + } + for _, o := range options { + if err := o(&res); err != nil { + return nil, err + } + } + + keySources := 0 + if res.KeyPath != "" { + keySources++ + } + if res.KeyData != nil { + keySources++ + } + if res.Fulcio != nil { + keySources++ + } + if keySources != 1 { + return nil, InvalidPolicyFormatError("exactly one of keyPath, keyData and fulcio must be specified") + } + + if res.RekorPublicKeyPath != "" && res.RekorPublicKeyData != nil { + return nil, InvalidPolicyFormatError("rekorPublickeyType and rekorPublickeyData cannot be used simultaneously") + } + if res.Fulcio != nil && res.RekorPublicKeyPath == "" && res.RekorPublicKeyData == nil { + return nil, InvalidPolicyFormatError("At least one of RekorPublickeyPath and RekorPublickeyData must be specified if fulcio is used") + } + + if res.SignedIdentity == nil { + return nil, InvalidPolicyFormatError("signedIdentity not specified") + } + + return &res, nil +} + +// NewPRSigstoreSigned returns a new "sigstoreSigned" PolicyRequirement based on options. +func NewPRSigstoreSigned(options ...PRSigstoreSignedOption) (PolicyRequirement, error) { + return newPRSigstoreSigned(options...) +} + +// NewPRSigstoreSignedKeyPath returns a new "sigstoreSigned" PolicyRequirement using a KeyPath +func NewPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) { + return NewPRSigstoreSigned( + PRSigstoreSignedWithKeyPath(keyPath), + PRSigstoreSignedWithSignedIdentity(signedIdentity), + ) +} + +// NewPRSigstoreSignedKeyData returns a new "sigstoreSigned" PolicyRequirement using a KeyData +func NewPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) { + return NewPRSigstoreSigned( + PRSigstoreSignedWithKeyData(keyData), + PRSigstoreSignedWithSignedIdentity(signedIdentity), + ) +} + +// Compile-time check that prSigstoreSigned implements json.Unmarshaler. +var _ json.Unmarshaler = (*prSigstoreSigned)(nil) + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (pr *prSigstoreSigned) UnmarshalJSON(data []byte) error { + *pr = prSigstoreSigned{} + var tmp prSigstoreSigned + var gotKeyPath, gotKeyData, gotFulcio, gotRekorPublicKeyPath, gotRekorPublicKeyData bool + var fulcio prSigstoreSignedFulcio + var signedIdentity json.RawMessage + if err := internal.ParanoidUnmarshalJSONObject(data, func(key string) interface{} { + switch key { + case "type": + return &tmp.Type + case "keyPath": + gotKeyPath = true + return &tmp.KeyPath + case "keyData": + gotKeyData = true + return &tmp.KeyData + case "fulcio": + gotFulcio = true + return &fulcio + case "rekorPublicKeyPath": + gotRekorPublicKeyPath = true + return &tmp.RekorPublicKeyPath + case "rekorPublicKeyData": + gotRekorPublicKeyData = true + return &tmp.RekorPublicKeyData + case "signedIdentity": + return &signedIdentity + default: + return nil + } + }); err != nil { + return err + } + + if tmp.Type != prTypeSigstoreSigned { + return InvalidPolicyFormatError(fmt.Sprintf("Unexpected policy requirement type \"%s\"", tmp.Type)) + } + if signedIdentity == nil { + tmp.SignedIdentity = NewPRMMatchRepoDigestOrExact() + } else { + si, err := newPolicyReferenceMatchFromJSON(signedIdentity) + if err != nil { + return err + } + tmp.SignedIdentity = si + } + + var opts []PRSigstoreSignedOption + if gotKeyPath { + opts = append(opts, PRSigstoreSignedWithKeyPath(tmp.KeyPath)) + } + if gotKeyData { + opts = append(opts, PRSigstoreSignedWithKeyData(tmp.KeyData)) + } + if gotFulcio { + opts = append(opts, PRSigstoreSignedWithFulcio(&fulcio)) + } + if gotRekorPublicKeyPath { + opts = append(opts, PRSigstoreSignedWithRekorPublicKeyPath(tmp.RekorPublicKeyPath)) + } + if gotRekorPublicKeyData { + opts = append(opts, PRSigstoreSignedWithRekorPublicKeyData(tmp.RekorPublicKeyData)) + } + opts = append(opts, PRSigstoreSignedWithSignedIdentity(tmp.SignedIdentity)) + + res, err := newPRSigstoreSigned(opts...) + if err != nil { + return err + } + *pr = *res + return nil +} + +// PRSigstoreSignedFulcioOption is a way to pass values to NewPRSigstoreSignedFulcio +type PRSigstoreSignedFulcioOption func(*prSigstoreSignedFulcio) error + +// PRSigstoreSignedFulcioWithCAPath specifies a value for the "caPath" field when calling NewPRSigstoreSignedFulcio +func PRSigstoreSignedFulcioWithCAPath(caPath string) PRSigstoreSignedFulcioOption { + return func(f *prSigstoreSignedFulcio) error { + if f.CAPath != "" { + return errors.New(`"caPath" already specified`) + } + f.CAPath = caPath + return nil + } +} + +// PRSigstoreSignedFulcioWithCAData specifies a value for the "caData" field when calling NewPRSigstoreSignedFulcio +func PRSigstoreSignedFulcioWithCAData(caData []byte) PRSigstoreSignedFulcioOption { + return func(f *prSigstoreSignedFulcio) error { + if f.CAData != nil { + return errors.New(`"caData" already specified`) + } + f.CAData = caData + return nil + } +} + +// PRSigstoreSignedFulcioWithOIDCIssuer specifies a value for the "oidcIssuer" field when calling NewPRSigstoreSignedFulcio +func PRSigstoreSignedFulcioWithOIDCIssuer(oidcIssuer string) PRSigstoreSignedFulcioOption { + return func(f *prSigstoreSignedFulcio) error { + if f.OIDCIssuer != "" { + return errors.New(`"oidcIssuer" already specified`) + } + f.OIDCIssuer = oidcIssuer + return nil + } +} + +// PRSigstoreSignedFulcioWithSubjectEmail specifies a value for the "subjectEmail" field when calling NewPRSigstoreSignedFulcio +func PRSigstoreSignedFulcioWithSubjectEmail(subjectEmail string) PRSigstoreSignedFulcioOption { + return func(f *prSigstoreSignedFulcio) error { + if f.SubjectEmail != "" { + return errors.New(`"subjectEmail" already specified`) + } + f.SubjectEmail = subjectEmail + return nil + } +} + +// newPRSigstoreSignedFulcio is NewPRSigstoreSignedFulcio, except it returns the private type +func newPRSigstoreSignedFulcio(options ...PRSigstoreSignedFulcioOption) (*prSigstoreSignedFulcio, error) { + res := prSigstoreSignedFulcio{} + for _, o := range options { + if err := o(&res); err != nil { + return nil, err + } + } + + if res.CAPath != "" && res.CAData != nil { + return nil, InvalidPolicyFormatError("caPath and caData cannot be used simultaneously") + } + if res.CAPath == "" && res.CAData == nil { + return nil, InvalidPolicyFormatError("At least one of caPath and caData must be specified") + } + if res.OIDCIssuer == "" { + return nil, InvalidPolicyFormatError("oidcIssuer not specified") + } + if res.SubjectEmail == "" { + return nil, InvalidPolicyFormatError("subjectEmail not specified") + } + + return &res, nil +} + +// NewPRSigstoreSignedFulcio returns a PRSigstoreSignedFulcio based on options. +func NewPRSigstoreSignedFulcio(options ...PRSigstoreSignedFulcioOption) (PRSigstoreSignedFulcio, error) { + return newPRSigstoreSignedFulcio(options...) +} + +// Compile-time check that prSigstoreSignedFulcio implements json.Unmarshaler. +var _ json.Unmarshaler = (*prSigstoreSignedFulcio)(nil) + +func (f *prSigstoreSignedFulcio) UnmarshalJSON(data []byte) error { + *f = prSigstoreSignedFulcio{} + var tmp prSigstoreSignedFulcio + var gotCAPath, gotCAData, gotOIDCIssuer, gotSubjectEmail bool // = false... + if err := internal.ParanoidUnmarshalJSONObject(data, func(key string) interface{} { + switch key { + case "caPath": + gotCAPath = true + return &tmp.CAPath + case "caData": + gotCAData = true + return &tmp.CAData + case "oidcIssuer": + gotOIDCIssuer = true + return &tmp.OIDCIssuer + case "subjectEmail": + gotSubjectEmail = true + return &tmp.SubjectEmail + default: + return nil + } + }); err != nil { + return err + } + + var opts []PRSigstoreSignedFulcioOption + if gotCAPath { + opts = append(opts, PRSigstoreSignedFulcioWithCAPath(tmp.CAPath)) + } + if gotCAData { + opts = append(opts, PRSigstoreSignedFulcioWithCAData(tmp.CAData)) + } + if gotOIDCIssuer { + opts = append(opts, PRSigstoreSignedFulcioWithOIDCIssuer(tmp.OIDCIssuer)) + } + if gotSubjectEmail { + opts = append(opts, PRSigstoreSignedFulcioWithSubjectEmail(tmp.SubjectEmail)) + } + + res, err := newPRSigstoreSignedFulcio(opts...) + if err != nil { + return err + } + + *f = *res + return nil +} diff --git a/vendor/github.com/containers/image/v5/signature/policy_eval_sigstore.go b/vendor/github.com/containers/image/v5/signature/policy_eval_sigstore.go index ccf1d80a..dcf5592a 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_eval_sigstore.go +++ b/vendor/github.com/containers/image/v5/signature/policy_eval_sigstore.go @@ -4,6 +4,9 @@ package signature import ( "context" + "crypto" + "crypto/ecdsa" + "crypto/x509" "errors" "fmt" "os" @@ -17,6 +20,100 @@ import ( "github.com/sigstore/sigstore/pkg/cryptoutils" ) +// loadBytesFromDataOrPath ensures there is at most one of ${prefix}Data and ${prefix}Path set, +// and returns the referenced data, or nil if neither is set. +func loadBytesFromDataOrPath(prefix string, data []byte, path string) ([]byte, error) { + switch { + case data != nil && path != "": + return nil, fmt.Errorf(`Internal inconsistency: both "%sPath" and "%sData" specified`, prefix, prefix) + case path != "": + d, err := os.ReadFile(path) + if err != nil { + return nil, err + } + return d, nil + case data != nil: + return data, nil + default: // Nothing + return nil, nil + } +} + +// prepareTrustRoot creates a fulcioTrustRoot from the input data. +// (This also prevents external implementations of this interface, ensuring that prSigstoreSignedFulcio is the only one.) +func (f *prSigstoreSignedFulcio) prepareTrustRoot() (*fulcioTrustRoot, error) { + caCertBytes, err := loadBytesFromDataOrPath("fulcioCA", f.CAData, f.CAPath) + if err != nil { + return nil, err + } + if caCertBytes == nil { + return nil, errors.New(`Internal inconsistency: Fulcio specified with neither "caPath" nor "caData"`) + } + certs := x509.NewCertPool() + if ok := certs.AppendCertsFromPEM(caCertBytes); !ok { + return nil, errors.New("error loading Fulcio CA certificates") + } + fulcio := fulcioTrustRoot{ + caCertificates: certs, + oidcIssuer: f.OIDCIssuer, + subjectEmail: f.SubjectEmail, + } + if err := fulcio.validate(); err != nil { + return nil, err + } + return &fulcio, nil +} + +// sigstoreSignedTrustRoot contains an already parsed version of the prSigstoreSigned policy +type sigstoreSignedTrustRoot struct { + publicKey crypto.PublicKey + fulcio *fulcioTrustRoot + rekorPublicKey *ecdsa.PublicKey +} + +func (pr *prSigstoreSigned) prepareTrustRoot() (*sigstoreSignedTrustRoot, error) { + res := sigstoreSignedTrustRoot{} + + publicKeyPEM, err := loadBytesFromDataOrPath("key", pr.KeyData, pr.KeyPath) + if err != nil { + return nil, err + } + if publicKeyPEM != nil { + pk, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyPEM) + if err != nil { + return nil, fmt.Errorf("parsing public key: %w", err) + } + res.publicKey = pk + } + + if pr.Fulcio != nil { + f, err := pr.Fulcio.prepareTrustRoot() + if err != nil { + return nil, err + } + res.fulcio = f + } + + rekorPublicKeyPEM, err := loadBytesFromDataOrPath("rekorPublicKey", pr.RekorPublicKeyData, pr.RekorPublicKeyPath) + if err != nil { + return nil, err + } + if rekorPublicKeyPEM != nil { + pk, err := cryptoutils.UnmarshalPEMToPublicKey(rekorPublicKeyPEM) + if err != nil { + return nil, fmt.Errorf("parsing Rekor public key: %w", err) + } + pkECDSA, ok := pk.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("Rekor public key is not using ECDSA") + + } + res.rekorPublicKey = pkECDSA + } + + return &res, nil +} + func (pr *prSigstoreSigned) isSignatureAuthorAccepted(ctx context.Context, image private.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { // We don’t know of a single user of this API, and we might return unexpected values in Signature. // For now, just punt. @@ -24,24 +121,10 @@ func (pr *prSigstoreSigned) isSignatureAuthorAccepted(ctx context.Context, image } func (pr *prSigstoreSigned) isSignatureAccepted(ctx context.Context, image private.UnparsedImage, sig signature.Sigstore) (signatureAcceptanceResult, error) { - if pr.KeyPath != "" && pr.KeyData != nil { - return sarRejected, errors.New(`Internal inconsistency: both "keyPath" and "keyData" specified`) - } // FIXME: move this to per-context initialization - var publicKeyPEM []byte - if pr.KeyData != nil { - publicKeyPEM = pr.KeyData - } else { - d, err := os.ReadFile(pr.KeyPath) - if err != nil { - return sarRejected, err - } - publicKeyPEM = d - } - - publicKey, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyPEM) + trustRoot, err := pr.prepareTrustRoot() if err != nil { - return sarRejected, fmt.Errorf("parsing public key: %w", err) + return sarRejected, err } untrustedAnnotations := sig.UntrustedAnnotations() @@ -49,8 +132,66 @@ func (pr *prSigstoreSigned) isSignatureAccepted(ctx context.Context, image priva if !ok { return sarRejected, fmt.Errorf("missing %s annotation", signature.SigstoreSignatureAnnotationKey) } + untrustedPayload := sig.UntrustedPayload() + + var publicKey crypto.PublicKey + switch { + case trustRoot.publicKey != nil && trustRoot.fulcio != nil: // newPRSigstoreSigned rejects such combinations. + return sarRejected, errors.New("Internal inconsistency: Both a public key and Fulcio CA specified") + case trustRoot.publicKey == nil && trustRoot.fulcio == nil: // newPRSigstoreSigned rejects such combinations. + return sarRejected, errors.New("Internal inconsistency: Neither a public key nor a Fulcio CA specified") - signature, err := internal.VerifySigstorePayload(publicKey, sig.UntrustedPayload(), untrustedBase64Signature, internal.SigstorePayloadAcceptanceRules{ + case trustRoot.publicKey != nil: + if trustRoot.rekorPublicKey != nil { + untrustedSET, ok := untrustedAnnotations[signature.SigstoreSETAnnotationKey] + if !ok { // For user convenience; passing an empty []byte to VerifyRekorSet should work. + return sarRejected, fmt.Errorf("missing %s annotation", signature.SigstoreSETAnnotationKey) + } + // We could use publicKeyPEM directly, but let’s re-marshal to avoid inconsistencies. + // FIXME: We could just generate DER instead of the full PEM text + recreatedPublicKeyPEM, err := cryptoutils.MarshalPublicKeyToPEM(trustRoot.publicKey) + if err != nil { + // Coverage: The key was loaded from a PEM format, so it’s unclear how this could fail. + // (PEM is not essential, MarshalPublicKeyToPEM can only fail if marshaling to ASN1.DER fails.) + return sarRejected, fmt.Errorf("re-marshaling public key to PEM: %w", err) + + } + // We don’t care about the Rekor timestamp, just about log presence. + if _, err := internal.VerifyRekorSET(trustRoot.rekorPublicKey, []byte(untrustedSET), recreatedPublicKeyPEM, untrustedBase64Signature, untrustedPayload); err != nil { + return sarRejected, err + } + } + publicKey = trustRoot.publicKey + + case trustRoot.fulcio != nil: + if trustRoot.rekorPublicKey == nil { // newPRSigstoreSigned rejects such combinations. + return sarRejected, errors.New("Internal inconsistency: Fulcio CA specified without a Rekor public key") + } + untrustedSET, ok := untrustedAnnotations[signature.SigstoreSETAnnotationKey] + if !ok { // For user convenience; passing an empty []byte to VerifyRekorSet should correctly reject it anyway. + return sarRejected, fmt.Errorf("missing %s annotation", signature.SigstoreSETAnnotationKey) + } + untrustedCert, ok := untrustedAnnotations[signature.SigstoreCertificateAnnotationKey] + if !ok { // For user convenience; passing an empty []byte to VerifyRekorSet should correctly reject it anyway. + return sarRejected, fmt.Errorf("missing %s annotation", signature.SigstoreCertificateAnnotationKey) + } + var untrustedIntermediateChainBytes []byte + if untrustedIntermediateChain, ok := untrustedAnnotations[signature.SigstoreIntermediateCertificateChainAnnotationKey]; ok { + untrustedIntermediateChainBytes = []byte(untrustedIntermediateChain) + } + pk, err := verifyRekorFulcio(trustRoot.rekorPublicKey, trustRoot.fulcio, + []byte(untrustedSET), []byte(untrustedCert), untrustedIntermediateChainBytes, untrustedBase64Signature, untrustedPayload) + if err != nil { + return sarRejected, err + } + publicKey = pk + } + + if publicKey == nil { + // Coverage: This should never happen, we have already excluded the possibility in the switch above. + return sarRejected, fmt.Errorf("Internal inconsistency: publicKey not set before verifying sigstore payload") + } + signature, err := internal.VerifySigstorePayload(publicKey, untrustedPayload, untrustedBase64Signature, internal.SigstorePayloadAcceptanceRules{ ValidateSignedDockerReference: func(ref string) error { if !pr.SignedIdentity.matchesDockerReference(image, ref) { return PolicyRequirementError(fmt.Sprintf("Signature for identity %s is not accepted", ref)) diff --git a/vendor/github.com/containers/image/v5/signature/policy_types.go b/vendor/github.com/containers/image/v5/signature/policy_types.go index 9e837452..96e91a0a 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_types.go +++ b/vendor/github.com/containers/image/v5/signature/policy_types.go @@ -111,13 +111,24 @@ type prSignedBaseLayer struct { type prSigstoreSigned struct { prCommon - // KeyPath is a pathname to a local file containing the trusted key. Exactly one of KeyPath and KeyData must be specified. + // KeyPath is a pathname to a local file containing the trusted key. Exactly one of KeyPath, KeyData, Fulcio must be specified. KeyPath string `json:"keyPath,omitempty"` - // KeyData contains the trusted key, base64-encoded. Exactly one of KeyPath and KeyData must be specified. + // KeyData contains the trusted key, base64-encoded. Exactly one of KeyPath, KeyData, Fulcio must be specified. KeyData []byte `json:"keyData,omitempty"` // FIXME: Multiple public keys? - // FIXME: Support fulcio+rekor as an alternative. + // Fulcio specifies which Fulcio-generated certificates are accepted. Exactly one of KeyPath, KeyData, Fulcio must be specified. + // If Fulcio is specified, one of RekorPublicKeyPath or RekorPublicKeyData must be specified as well. + Fulcio PRSigstoreSignedFulcio `json:"fulcio,omitempty"` + + // RekorPublicKeyPath is a pathname to local file containing a public key of a Rekor server which must record acceptable signatures. + // If Fulcio is used, one of RekorPublicKeyPath or RekorPublicKeyData must be specified as well; otherwise it is optional + // (and Rekor inclusion is not required if a Rekor public key is not specified). + RekorPublicKeyPath string `json:"rekorPublicKeyPath,omitempty"` + // RekorPublicKeyPath contain a base64-encoded public key of a Rekor server which must record acceptable signatures. + // If Fulcio is used, one of RekorPublicKeyPath or RekorPublicKeyData must be specified as well; otherwise it is optional + // (and Rekor inclusion is not required if a Rekor public key is not specified). + RekorPublicKeyData []byte `json:"rekorPublicKeyData,omitempty"` // SignedIdentity specifies what image identity the signature must be claiming about the image. // Defaults to "matchRepoDigestOrExact" if not specified. @@ -125,6 +136,26 @@ type prSigstoreSigned struct { SignedIdentity PolicyReferenceMatch `json:"signedIdentity"` } +// PRSigstoreSignedFulcio contains Fulcio configuration options for a "sigstoreSigned" PolicyRequirement. +// This is a public type with a single private implementation. +type PRSigstoreSignedFulcio interface { + // toFulcioTrustRoot creates a fulcioTrustRoot from the input data. + // (This also prevents external implementations of this interface, ensuring that prSigstoreSignedFulcio is the only one.) + prepareTrustRoot() (*fulcioTrustRoot, error) +} + +// prSigstoreSignedFulcio collects Fulcio configuration options for prSigstoreSigned +type prSigstoreSignedFulcio struct { + // CAPath a path to a file containing accepted CA root certificates, in PEM format. Exactly one of CAPath and CAData must be specified. + CAPath string `json:"caPath,omitempty"` + // CAData contains accepted CA root certificates in PEM format, all of that base64-encoded. Exactly one of CAPath and CAData must be specified. + CAData []byte `json:"caData,omitempty"` + // OIDCIssuer specifies the expected OIDC issuer, recorded by Fulcio into the generated certificates. + OIDCIssuer string `json:"oidcIssuer,omitempty"` + // SubjectEmail specifies the expected email address of the authenticated OIDC identity, recorded by Fulcio into the generated certificates. + SubjectEmail string `json:"subjectEmail,omitempty"` +} + // PolicyReferenceMatch specifies a set of image identities accepted in PolicyRequirement. // The type is public, but its implementation is private. diff --git a/vendor/github.com/containers/image/v5/signature/signer/signer.go b/vendor/github.com/containers/image/v5/signature/signer/signer.go new file mode 100644 index 00000000..73ae550a --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/signer/signer.go @@ -0,0 +1,9 @@ +package signer + +import "github.com/containers/image/v5/internal/signer" + +// Signer is an object, possibly carrying state, that can be used by copy.Image to sign one or more container images. +// It can only be created from within the containers/image package; it can’t be implemented externally. +// +// The owner of a Signer must call Close() when done. +type Signer = signer.Signer diff --git a/vendor/github.com/containers/image/v5/signature/sigstore/copied.go b/vendor/github.com/containers/image/v5/signature/sigstore/copied.go index dbc03ec0..0233c4cb 100644 --- a/vendor/github.com/containers/image/v5/signature/sigstore/copied.go +++ b/vendor/github.com/containers/image/v5/signature/sigstore/copied.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" + "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/theupdateframework/go-tuf/encrypted" ) @@ -68,3 +69,31 @@ func loadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { return nil, errors.New("unsupported key type") } } + +// simplified from sigstore/cosign/pkg/cosign.marshalKeyPair +// loadPrivateKey always requires a encryption, so this always requires a passphrase. +func marshalKeyPair(privateKey crypto.PrivateKey, publicKey crypto.PublicKey, password []byte) (_privateKey []byte, _publicKey []byte, err error) { + x509Encoded, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, nil, fmt.Errorf("x509 encoding private key: %w", err) + } + + encBytes, err := encrypted.Encrypt(x509Encoded, password) + if err != nil { + return nil, nil, err + } + + // store in PEM format + privBytes := pem.EncodeToMemory(&pem.Block{ + Bytes: encBytes, + Type: sigstorePrivateKeyPemType, + }) + + // Now do the public key + pubBytes, err := cryptoutils.MarshalPublicKeyToPEM(publicKey) + if err != nil { + return nil, nil, err + } + + return privBytes, pubBytes, nil +} diff --git a/vendor/github.com/containers/image/v5/signature/sigstore/generate.go b/vendor/github.com/containers/image/v5/signature/sigstore/generate.go new file mode 100644 index 00000000..77520c12 --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/sigstore/generate.go @@ -0,0 +1,35 @@ +package sigstore + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" +) + +// GenerateKeyPairResult is a struct to ensure the private and public parts can not be confused by the caller. +type GenerateKeyPairResult struct { + PublicKey []byte + PrivateKey []byte +} + +// GenerateKeyPair generates a public/private key pair usable for signing images using the sigstore format, +// and returns key representations suitable for storing in long-term files (with the private key encrypted using the provided passphrase). +// The specific key kind (e.g. algorithm, size), as well as the file format, are unspecified by this API, +// and can change with best practices over time. +func GenerateKeyPair(passphrase []byte) (*GenerateKeyPairResult, error) { + // https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md#signature-schemes + // only requires ECDSA-P256 to be supported, so that’s what we must use. + rawKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + // Coverage: This can fail only if the randomness source fails + return nil, err + } + private, public, err := marshalKeyPair(rawKey, rawKey.Public(), passphrase) + if err != nil { + return nil, err + } + return &GenerateKeyPairResult{ + PublicKey: public, + PrivateKey: private, + }, nil +} diff --git a/vendor/github.com/containers/image/v5/signature/sigstore/internal/signer.go b/vendor/github.com/containers/image/v5/signature/sigstore/internal/signer.go new file mode 100644 index 00000000..c6258f40 --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/sigstore/internal/signer.go @@ -0,0 +1,95 @@ +package internal + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/signature" + "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/signature/internal" + sigstoreSignature "github.com/sigstore/sigstore/pkg/signature" +) + +type Option func(*SigstoreSigner) error + +// SigstoreSigner is a signer.SignerImplementation implementation for sigstore signatures. +// It is initialized using various closures that implement Option, sadly over several subpackages, to decrease the +// dependency impact. +type SigstoreSigner struct { + PrivateKey sigstoreSignature.Signer // May be nil during initialization + SigningKeyOrCert []byte // For possible Rekor upload; always initialized together with PrivateKey + + // Fulcio results to include + FulcioGeneratedCertificate []byte // Or nil + FulcioGeneratedCertificateChain []byte // Or nil + + // Rekor state + RekorUploader func(ctx context.Context, keyOrCertBytes []byte, signatureBytes []byte, payloadBytes []byte) ([]byte, error) // Or nil +} + +// ProgressMessage returns a human-readable sentence that makes sense to write before starting to create a single signature. +func (s *SigstoreSigner) ProgressMessage() string { + return "Signing image using a sigstore signature" +} + +// SignImageManifest creates a new signature for manifest m as dockerReference. +func (s *SigstoreSigner) SignImageManifest(ctx context.Context, m []byte, dockerReference reference.Named) (signature.Signature, error) { + if s.PrivateKey == nil { + return nil, errors.New("internal error: nothing to sign with, should have been detected in NewSigner") + } + + if reference.IsNameOnly(dockerReference) { + return nil, fmt.Errorf("reference %s can’t be signed, it has neither a tag nor a digest", dockerReference.String()) + } + manifestDigest, err := manifest.Digest(m) + if err != nil { + return nil, err + } + // sigstore/cosign completely ignores dockerReference for actual policy decisions. + // They record the repo (but NOT THE TAG) in the value; without the tag we can’t detect version rollbacks. + // So, just do what simple signing does, and cosign won’t mind. + payloadData := internal.NewUntrustedSigstorePayload(manifestDigest, dockerReference.String()) + payloadBytes, err := json.Marshal(payloadData) + if err != nil { + return nil, err + } + + // github.com/sigstore/cosign/internal/pkg/cosign.payloadSigner uses signatureoptions.WithContext(), + // which seems to be not used by anything. So we don’t bother. + signatureBytes, err := s.PrivateKey.SignMessage(bytes.NewReader(payloadBytes)) + if err != nil { + return nil, fmt.Errorf("creating signature: %w", err) + } + base64Signature := base64.StdEncoding.EncodeToString(signatureBytes) + var rekorSETBytes []byte // = nil + if s.RekorUploader != nil { + set, err := s.RekorUploader(ctx, s.SigningKeyOrCert, signatureBytes, payloadBytes) + if err != nil { + return nil, err + } + rekorSETBytes = set + } + + annotations := map[string]string{ + signature.SigstoreSignatureAnnotationKey: base64Signature, + } + if s.FulcioGeneratedCertificate != nil { + annotations[signature.SigstoreCertificateAnnotationKey] = string(s.FulcioGeneratedCertificate) + } + if s.FulcioGeneratedCertificateChain != nil { + annotations[signature.SigstoreIntermediateCertificateChainAnnotationKey] = string(s.FulcioGeneratedCertificateChain) + } + if rekorSETBytes != nil { + annotations[signature.SigstoreSETAnnotationKey] = string(rekorSETBytes) + } + return signature.SigstoreFromComponents(signature.SigstoreSignatureMIMEType, payloadBytes, annotations), nil +} + +func (s *SigstoreSigner) Close() error { + return nil +} diff --git a/vendor/github.com/containers/image/v5/signature/sigstore/sign.go b/vendor/github.com/containers/image/v5/signature/sigstore/sign.go deleted file mode 100644 index daa6ab38..00000000 --- a/vendor/github.com/containers/image/v5/signature/sigstore/sign.go +++ /dev/null @@ -1,65 +0,0 @@ -package sigstore - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "fmt" - "os" - - "github.com/containers/image/v5/docker/reference" - "github.com/containers/image/v5/internal/signature" - "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/signature/internal" - sigstoreSignature "github.com/sigstore/sigstore/pkg/signature" -) - -// SignDockerManifestWithPrivateKeyFileUnstable returns a signature for manifest as the specified dockerReference, -// using a private key and an optional passphrase. -// -// Yes, this returns an internal type, and should currently not be used outside of c/image. -// There is NO COMITTMENT TO STABLE API. -func SignDockerManifestWithPrivateKeyFileUnstable(m []byte, dockerReference reference.Named, privateKeyFile string, passphrase []byte) (signature.Sigstore, error) { - privateKeyPEM, err := os.ReadFile(privateKeyFile) - if err != nil { - return signature.Sigstore{}, fmt.Errorf("reading private key from %s: %w", privateKeyFile, err) - } - signer, err := loadPrivateKey(privateKeyPEM, passphrase) - if err != nil { - return signature.Sigstore{}, fmt.Errorf("initializing private key: %w", err) - } - - return signDockerManifest(m, dockerReference, signer) -} - -func signDockerManifest(m []byte, dockerReference reference.Named, signer sigstoreSignature.Signer) (signature.Sigstore, error) { - if reference.IsNameOnly(dockerReference) { - return signature.Sigstore{}, fmt.Errorf("reference %s can’t be signed, it has neither a tag nor a digest", dockerReference.String()) - } - manifestDigest, err := manifest.Digest(m) - if err != nil { - return signature.Sigstore{}, err - } - // sigstore/cosign completely ignores dockerReference for actual policy decisions. - // They record the repo (but NOT THE TAG) in the value; without the tag we can’t detect version rollbacks. - // So, just do what simple signing does, and cosign won’t mind. - payloadData := internal.NewUntrustedSigstorePayload(manifestDigest, dockerReference.String()) - payloadBytes, err := json.Marshal(payloadData) - if err != nil { - return signature.Sigstore{}, err - } - - // github.com/sigstore/cosign/internal/pkg/cosign.payloadSigner uses signatureoptions.WithContext(), - // which seems to be not used by anything. So we don’t bother. - signatureBytes, err := signer.SignMessage(bytes.NewReader(payloadBytes)) - if err != nil { - return signature.Sigstore{}, fmt.Errorf("creating signature: %w", err) - } - base64Signature := base64.StdEncoding.EncodeToString(signatureBytes) - - return signature.SigstoreFromComponents(signature.SigstoreSignatureMIMEType, - payloadBytes, - map[string]string{ - signature.SigstoreSignatureAnnotationKey: base64Signature, - }), nil -} diff --git a/vendor/github.com/containers/image/v5/signature/sigstore/signer.go b/vendor/github.com/containers/image/v5/signature/sigstore/signer.go new file mode 100644 index 00000000..fb825ada --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/sigstore/signer.go @@ -0,0 +1,60 @@ +package sigstore + +import ( + "errors" + "fmt" + "os" + + internalSigner "github.com/containers/image/v5/internal/signer" + "github.com/containers/image/v5/signature/signer" + "github.com/containers/image/v5/signature/sigstore/internal" + "github.com/sigstore/sigstore/pkg/cryptoutils" +) + +type Option = internal.Option + +func WithPrivateKeyFile(file string, passphrase []byte) Option { + return func(s *internal.SigstoreSigner) error { + if s.PrivateKey != nil { + return fmt.Errorf("multiple private key sources specified when preparing to create sigstore signatures") + } + + if passphrase == nil { + return errors.New("private key passphrase not provided") + } + + privateKeyPEM, err := os.ReadFile(file) + if err != nil { + return fmt.Errorf("reading private key from %s: %w", file, err) + } + signerVerifier, err := loadPrivateKey(privateKeyPEM, passphrase) + if err != nil { + return fmt.Errorf("initializing private key: %w", err) + } + publicKey, err := signerVerifier.PublicKey() + if err != nil { + return fmt.Errorf("getting public key from private key: %w", err) + } + publicKeyPEM, err := cryptoutils.MarshalPublicKeyToPEM(publicKey) + if err != nil { + return fmt.Errorf("converting public key to PEM: %w", err) + } + s.PrivateKey = signerVerifier + s.SigningKeyOrCert = publicKeyPEM + return nil + } +} + +func NewSigner(opts ...Option) (*signer.Signer, error) { + s := internal.SigstoreSigner{} + for _, o := range opts { + if err := o(&s); err != nil { + return nil, err + } + } + if s.PrivateKey == nil { + return nil, errors.New("no private key source provided (neither a private key nor Fulcio) when preparing to create sigstore signatures") + } + + return internalSigner.NewSigner(&s), nil +} diff --git a/vendor/github.com/containers/image/v5/signature/simple.go b/vendor/github.com/containers/image/v5/signature/simple.go index 1ca571e5..e91f46ab 100644 --- a/vendor/github.com/containers/image/v5/signature/simple.go +++ b/vendor/github.com/containers/image/v5/signature/simple.go @@ -72,7 +72,8 @@ func newUntrustedSignature(dockerManifestDigest digest.Digest, dockerReference s } } -// Compile-time check that untrustedSignature implements json.Marshaler +// A compile-time check that untrustedSignature and *untrustedSignature implements json.Marshaler +var _ json.Marshaler = untrustedSignature{} var _ json.Marshaler = (*untrustedSignature)(nil) // MarshalJSON implements the json.Marshaler interface. diff --git a/vendor/github.com/containers/image/v5/signature/simplesigning/signer.go b/vendor/github.com/containers/image/v5/signature/simplesigning/signer.go new file mode 100644 index 00000000..983bbb10 --- /dev/null +++ b/vendor/github.com/containers/image/v5/signature/simplesigning/signer.go @@ -0,0 +1,105 @@ +package simplesigning + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/containers/image/v5/docker/reference" + internalSig "github.com/containers/image/v5/internal/signature" + internalSigner "github.com/containers/image/v5/internal/signer" + "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/signature/signer" +) + +// simpleSigner is a signer.SignerImplementation implementation for simple signing signatures. +type simpleSigner struct { + mech signature.SigningMechanism + keyFingerprint string + passphrase string // "" if not provided. +} + +type Option func(*simpleSigner) error + +// WithKeyFingerprint returns an Option for NewSigner, specifying a key to sign with, using the provided GPG key fingerprint. +func WithKeyFingerprint(keyFingerprint string) Option { + return func(s *simpleSigner) error { + s.keyFingerprint = keyFingerprint + return nil + } +} + +// WithPassphrase returns an Option for NewSigner, specifying a passphrase for the private key. +// If this is not specified, the system may interactively prompt using a gpg-agent / pinentry. +func WithPassphrase(passphrase string) Option { + return func(s *simpleSigner) error { + // The gpgme implementation can’t use passphrase with \n; reject it here for consistent behavior. + if strings.Contains(passphrase, "\n") { + return errors.New("invalid passphrase: must not contain a line break") + } + s.passphrase = passphrase + return nil + } +} + +// NewSigner returns a signature.Signer which creates “simple signing” signatures using the user’s default +// GPG configuration ($GNUPGHOME / ~/.gnupg). +// +// The set of options must identify a key to sign with, probably using a WithKeyFingerprint. +// +// The caller must call Close() on the returned Signer. +func NewSigner(opts ...Option) (*signer.Signer, error) { + mech, err := signature.NewGPGSigningMechanism() + if err != nil { + return nil, fmt.Errorf("initializing GPG: %w", err) + } + succeeded := false + defer func() { + if !succeeded { + mech.Close() + } + }() + if err := mech.SupportsSigning(); err != nil { + return nil, fmt.Errorf("Signing not supported: %w", err) + } + + s := simpleSigner{ + mech: mech, + } + for _, o := range opts { + if err := o(&s); err != nil { + return nil, err + } + } + if s.keyFingerprint == "" { + return nil, errors.New("no key identity provided for simple signing") + } + // Ideally, we should look up (and unlock?) the key at this point already, but our current SigningMechanism API does not allow that. + + succeeded = true + return internalSigner.NewSigner(&s), nil +} + +// ProgressMessage returns a human-readable sentence that makes sense to write before starting to create a single signature. +func (s *simpleSigner) ProgressMessage() string { + return "Signing image using simple signing" +} + +// SignImageManifest creates a new signature for manifest m as dockerReference. +func (s *simpleSigner) SignImageManifest(ctx context.Context, m []byte, dockerReference reference.Named) (internalSig.Signature, error) { + if reference.IsNameOnly(dockerReference) { + return nil, fmt.Errorf("reference %s can’t be signed, it has neither a tag nor a digest", dockerReference.String()) + } + simpleSig, err := signature.SignDockerManifestWithOptions(m, dockerReference.String(), s.mech, s.keyFingerprint, &signature.SignOptions{ + Passphrase: s.passphrase, + }) + if err != nil { + return nil, err + } + return internalSig.SimpleSigningFromBlob(simpleSig), nil +} + +func (s *simpleSigner) Close() error { + return s.mech.Close() +} diff --git a/vendor/github.com/containers/image/v5/storage/storage_src.go b/vendor/github.com/containers/image/v5/storage/storage_src.go index d4288dad..d1affc5e 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_src.go +++ b/vendor/github.com/containers/image/v5/storage/storage_src.go @@ -23,7 +23,6 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" - "github.com/containers/storage/pkg/ioutils" digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" @@ -89,14 +88,37 @@ func (s *storageImageSource) Close() error { // The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. // May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. func (s *storageImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (rc io.ReadCloser, n int64, err error) { - if info.Digest == image.GzippedEmptyLayerDigest { + // We need a valid digest value. + digest := info.Digest + err = digest.Validate() + if err != nil { + return nil, 0, err + } + + if digest == image.GzippedEmptyLayerDigest { return io.NopCloser(bytes.NewReader(image.GzippedEmptyLayer)), int64(len(image.GzippedEmptyLayer)), nil } + // Check if the blob corresponds to a diff that was used to initialize any layers. Our + // callers should try to retrieve layers using their uncompressed digests, so no need to + // check if they're using one of the compressed digests, which we can't reproduce anyway. + layers, _ := s.imageRef.transport.store.LayersByUncompressedDigest(digest) + + // If it's not a layer, then it must be a data item. + if len(layers) == 0 { + b, err := s.imageRef.transport.store.ImageBigData(s.image.ID, digest.String()) + if err != nil { + return nil, 0, err + } + r := bytes.NewReader(b) + logrus.Debugf("exporting opaque data as blob %q", digest.String()) + return io.NopCloser(r), int64(r.Len()), nil + } + // NOTE: the blob is first written to a temporary file and subsequently // closed. The intention is to keep the time we own the storage lock // as short as possible to allow other processes to access the storage. - rc, n, _, err = s.getBlobAndLayerID(info) + rc, n, _, err = s.getBlobAndLayerID(digest, layers) if err != nil { return nil, 0, err } @@ -106,53 +128,40 @@ func (s *storageImageSource) GetBlob(ctx context.Context, info types.BlobInfo, c if err != nil { return nil, 0, err } + success := false + defer func() { + if !success { + tmpFile.Close() + } + }() + // On Unix and modern Windows (2022 at least) we can eagerly unlink the file to ensure it's automatically + // cleaned up on process termination (or if the caller forgets to invoke Close()) + if err := os.Remove(tmpFile.Name()); err != nil { + return nil, 0, err + } if _, err := io.Copy(tmpFile, rc); err != nil { return nil, 0, err } - - if _, err := tmpFile.Seek(0, 0); err != nil { + if _, err := tmpFile.Seek(0, io.SeekStart); err != nil { return nil, 0, err } - wrapper := ioutils.NewReadCloserWrapper(tmpFile, func() error { - defer os.Remove(tmpFile.Name()) - return tmpFile.Close() - }) - - return wrapper, n, err + success = true + return tmpFile, n, nil } // getBlobAndLayer reads the data blob or filesystem layer which matches the digest and size, if given. -func (s *storageImageSource) getBlobAndLayerID(info types.BlobInfo) (rc io.ReadCloser, n int64, layerID string, err error) { +func (s *storageImageSource) getBlobAndLayerID(digest digest.Digest, layers []storage.Layer) (rc io.ReadCloser, n int64, layerID string, err error) { var layer storage.Layer var diffOptions *storage.DiffOptions - // We need a valid digest value. - err = info.Digest.Validate() - if err != nil { - return nil, -1, "", err - } - // Check if the blob corresponds to a diff that was used to initialize any layers. Our - // callers should try to retrieve layers using their uncompressed digests, so no need to - // check if they're using one of the compressed digests, which we can't reproduce anyway. - layers, _ := s.imageRef.transport.store.LayersByUncompressedDigest(info.Digest) - // If it's not a layer, then it must be a data item. - if len(layers) == 0 { - b, err := s.imageRef.transport.store.ImageBigData(s.image.ID, info.Digest.String()) - if err != nil { - return nil, -1, "", err - } - r := bytes.NewReader(b) - logrus.Debugf("exporting opaque data as blob %q", info.Digest.String()) - return io.NopCloser(r), int64(r.Len()), "", nil - } // Step through the list of matching layers. Tests may want to verify that if we have multiple layers // which claim to have the same contents, that we actually do have multiple layers, otherwise we could // just go ahead and use the first one every time. s.getBlobMutex.Lock() - i := s.layerPosition[info.Digest] - s.layerPosition[info.Digest] = i + 1 + i := s.layerPosition[digest] + s.layerPosition[digest] = i + 1 s.getBlobMutex.Unlock() if len(layers) > 0 { layer = layers[i%len(layers)] @@ -168,7 +177,7 @@ func (s *storageImageSource) getBlobAndLayerID(info types.BlobInfo) (rc io.ReadC } else { n = layer.UncompressedSize } - logrus.Debugf("exporting filesystem layer %q without compression for blob %q", layer.ID, info.Digest) + logrus.Debugf("exporting filesystem layer %q without compression for blob %q", layer.ID, digest) rc, err = s.imageRef.transport.store.Diff("", layer.ID, diffOptions) if err != nil { return nil, -1, "", err diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go index 7075b09e..33a54566 100644 --- a/vendor/github.com/containers/image/v5/types/types.go +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -125,6 +125,13 @@ type BlobInfo struct { URLs []string Annotations map[string]string MediaType string + + // NOTE: The following fields contain desired _edits_ to blob infos. + // Conceptually then don't belong in the BlobInfo object at all; + // the edits should be provided specifically as parameters to the edit implementation. + // We can’t remove the fields without breaking compatibility, but don’t + // add any more. + // CompressionOperation is used in Image.UpdateLayerInfos to instruct // whether the original layer's "compressed or not" should be preserved, // possibly while changing the compression algorithm from one to another, @@ -144,6 +151,7 @@ type BlobInfo struct { // TODO: To remove together with CompressionOperation in re-design to // remove field out out of BlobInfo. CryptoOperation LayerCrypto + // Before adding any fields to this struct, read the NOTE above. } // BICTransportScope encapsulates transport-dependent representation of a “scope” where blobs are or are not present. diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index b7734837..23c4982e 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,7 +6,7 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 23 + VersionMinor = 24 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 1 diff --git a/vendor/github.com/containers/libtrust/CODE-OF-CONDUCT.md b/vendor/github.com/containers/libtrust/CODE-OF-CONDUCT.md index 83b061c7..a7d8acbf 100644 --- a/vendor/github.com/containers/libtrust/CODE-OF-CONDUCT.md +++ b/vendor/github.com/containers/libtrust/CODE-OF-CONDUCT.md @@ -1,3 +1,3 @@ ## The libtrust Project Community Code of Conduct -The libtrust project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). +The libtrust project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md). diff --git a/vendor/github.com/containers/libtrust/SECURITY.md b/vendor/github.com/containers/libtrust/SECURITY.md index fab2c41e..966f4f05 100644 --- a/vendor/github.com/containers/libtrust/SECURITY.md +++ b/vendor/github.com/containers/libtrust/SECURITY.md @@ -1,3 +1,3 @@ ## Security and Disclosure Information Policy for the libtrust Project -The libtrust Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects. +The libtrust Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects. diff --git a/vendor/github.com/containers/ocicrypt/.gitignore b/vendor/github.com/containers/ocicrypt/.gitignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/vendor/github.com/containers/ocicrypt/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/vendor/github.com/containers/ocicrypt/.golangci.yml b/vendor/github.com/containers/ocicrypt/.golangci.yml new file mode 100644 index 00000000..12994baf --- /dev/null +++ b/vendor/github.com/containers/ocicrypt/.golangci.yml @@ -0,0 +1,31 @@ +linters: + enable: + - depguard + - staticcheck + - unconvert + - gofmt + - goimports + - revive + - ineffassign + - vet + - unused + - misspell + +linters-settings: + depguard: + list-type: denylist + include-go-root: true + packages: + # use "io" or "os" instead + # https://go.dev/doc/go1.16#ioutil + - io/ioutil + + revive: + severity: error + rules: + - name: indent-error-flow + severity: warning + disabled: false + + - name: error-strings + disabled: false diff --git a/vendor/github.com/containers/ocicrypt/.travis.yml b/vendor/github.com/containers/ocicrypt/.travis.yml deleted file mode 100644 index 1036c8d3..00000000 --- a/vendor/github.com/containers/ocicrypt/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -dist: bionic -language: go - -os: -- linux - -go: - - "1.13.x" - - "1.16.x" - -matrix: - include: - - os: linux - -addons: - apt: - packages: - - gnutls-bin - - softhsm2 - -go_import_path: github.com/containers/ocicrypt - -install: - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2 - -script: - - make - - make check - - make test diff --git a/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go b/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go index da403d95..0c485d51 100644 --- a/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go +++ b/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go @@ -17,10 +17,11 @@ package blockcipher import ( + "errors" + "fmt" "io" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) // LayerCipherType is the ciphertype as specified in the layer metadata @@ -129,7 +130,7 @@ func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCi } return encDataReader, fin, err } - return nil, nil, errors.Errorf("unsupported cipher type: %s", typ) + return nil, nil, fmt.Errorf("unsupported cipher type: %s", typ) } // Decrypt is the handler for the layer decryption routine @@ -138,10 +139,10 @@ func (h *LayerBlockCipherHandler) Decrypt(encDataReader io.Reader, opt LayerBloc if typ == "" { return nil, LayerBlockCipherOptions{}, errors.New("no cipher type provided") } - if c, ok := h.cipherMap[LayerCipherType(typ)]; ok { + if c, ok := h.cipherMap[typ]; ok { return c.Decrypt(encDataReader, opt) } - return nil, LayerBlockCipherOptions{}, errors.Errorf("unsupported cipher type: %s", typ) + return nil, LayerBlockCipherOptions{}, fmt.Errorf("unsupported cipher type: %s", typ) } // NewLayerBlockCipherHandler returns a new default handler @@ -153,7 +154,7 @@ func NewLayerBlockCipherHandler() (*LayerBlockCipherHandler, error) { var err error h.cipherMap[AES256CTR], err = NewAESCTRLayerBlockCipher(256) if err != nil { - return nil, errors.Wrap(err, "unable to set up Cipher AES-256-CTR") + return nil, fmt.Errorf("unable to set up Cipher AES-256-CTR: %w", err) } return &h, nil diff --git a/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go b/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go index 095a53e3..7db03e2e 100644 --- a/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go +++ b/vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go @@ -22,12 +22,12 @@ import ( "crypto/hmac" "crypto/rand" "crypto/sha256" + "errors" "fmt" "hash" "io" "github.com/containers/ocicrypt/utils" - "github.com/pkg/errors" ) // AESCTRLayerBlockCipher implements the AES CTR stream cipher @@ -74,7 +74,7 @@ func (r *aesctrcryptor) Read(p []byte) (int, error) { if !r.bc.encrypt { if _, err := r.bc.hmac.Write(p[:o]); err != nil { - r.bc.err = errors.Wrapf(err, "could not write to hmac") + r.bc.err = fmt.Errorf("could not write to hmac: %w", err) return 0, r.bc.err } @@ -92,7 +92,7 @@ func (r *aesctrcryptor) Read(p []byte) (int, error) { if r.bc.encrypt { if _, err := r.bc.hmac.Write(p[:o]); err != nil { - r.bc.err = errors.Wrapf(err, "could not write to hmac") + r.bc.err = fmt.Errorf("could not write to hmac: %w", err) return 0, r.bc.err } @@ -120,13 +120,13 @@ func (bc *AESCTRLayerBlockCipher) init(encrypt bool, reader io.Reader, opts Laye if !ok { nonce = make([]byte, aes.BlockSize) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to generate random nonce") + return LayerBlockCipherOptions{}, fmt.Errorf("unable to generate random nonce: %w", err) } } block, err := aes.NewCipher(key) if err != nil { - return LayerBlockCipherOptions{}, errors.Wrap(err, "aes.NewCipher failed") + return LayerBlockCipherOptions{}, fmt.Errorf("aes.NewCipher failed: %w", err) } bc.reader = reader diff --git a/vendor/github.com/containers/ocicrypt/config/constructors.go b/vendor/github.com/containers/ocicrypt/config/constructors.go index c537a20a..f7f29cd8 100644 --- a/vendor/github.com/containers/ocicrypt/config/constructors.go +++ b/vendor/github.com/containers/ocicrypt/config/constructors.go @@ -17,10 +17,11 @@ package config import ( - "github.com/containers/ocicrypt/crypto/pkcs11" + "errors" + "fmt" "strings" - "github.com/pkg/errors" + "github.com/containers/ocicrypt/crypto/pkcs11" "gopkg.in/yaml.v3" ) @@ -85,7 +86,7 @@ func EncryptWithPkcs11(pkcs11Config *pkcs11.Pkcs11Config, pkcs11Pubkeys, pkcs11Y } p11confYaml, err := yaml.Marshal(pkcs11Config) if err != nil { - return CryptoConfig{}, errors.Wrapf(err, "Could not marshal Pkcs11Config to Yaml") + return CryptoConfig{}, fmt.Errorf("Could not marshal Pkcs11Config to Yaml: %w", err) } dc = DecryptConfig{ @@ -223,7 +224,7 @@ func DecryptWithGpgPrivKeys(gpgPrivKeys, gpgPrivKeysPwds [][]byte) (CryptoConfig func DecryptWithPkcs11Yaml(pkcs11Config *pkcs11.Pkcs11Config, pkcs11Yamls [][]byte) (CryptoConfig, error) { p11confYaml, err := yaml.Marshal(pkcs11Config) if err != nil { - return CryptoConfig{}, errors.Wrapf(err, "Could not marshal Pkcs11Config to Yaml") + return CryptoConfig{}, fmt.Errorf("Could not marshal Pkcs11Config to Yaml: %w", err) } dc := DecryptConfig{ diff --git a/vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go b/vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go index b454b371..4785a831 100644 --- a/vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go +++ b/vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go @@ -18,8 +18,7 @@ package config import ( "encoding/json" - "github.com/pkg/errors" - "io/ioutil" + "fmt" "os" ) @@ -52,7 +51,7 @@ func parseConfigFile(filename string) (*OcicryptConfig, error) { return nil, nil } - data, err := ioutil.ReadFile(filename) + data, err := os.ReadFile(filename) if err != nil { return nil, err } @@ -72,7 +71,7 @@ func GetConfiguration() (*OcicryptConfig, error) { if len(filename) > 0 { ic, err = parseConfigFile(filename) if err != nil { - return nil, errors.Wrap(err, "Error while parsing keyprovider config file") + return nil, fmt.Errorf("Error while parsing keyprovider config file: %w", err) } } else { return nil, nil diff --git a/vendor/github.com/containers/ocicrypt/config/pkcs11config/config.go b/vendor/github.com/containers/ocicrypt/config/pkcs11config/config.go index 5a8bc022..b4f0e4d3 100644 --- a/vendor/github.com/containers/ocicrypt/config/pkcs11config/config.go +++ b/vendor/github.com/containers/ocicrypt/config/pkcs11config/config.go @@ -17,13 +17,12 @@ package pkcs11config import ( + "errors" "fmt" - "io/ioutil" "os" "path" "github.com/containers/ocicrypt/crypto/pkcs11" - "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -39,11 +38,11 @@ const ENVVARNAME = "OCICRYPT_CONFIG" // not exist, so no error is returned. // A config file may look like this: // module-directories: -// - /usr/lib64/pkcs11/ -// - /usr/lib/pkcs11/ +// - /usr/lib64/pkcs11/ +// - /usr/lib/pkcs11/ // allowed-module-paths: -// - /usr/lib64/pkcs11/ -// - /usr/lib/pkcs11/ +// - /usr/lib64/pkcs11/ +// - /usr/lib/pkcs11/ func parseConfigFile(filename string) (*OcicryptConfig, error) { // a non-existent config file is not an error _, err := os.Stat(filename) @@ -51,7 +50,7 @@ func parseConfigFile(filename string) (*OcicryptConfig, error) { return nil, nil } - data, err := ioutil.ReadFile(filename) + data, err := os.ReadFile(filename) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go index c6d47e83..473e23ff 100644 --- a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go +++ b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go @@ -15,7 +15,7 @@ package pkcs11 import ( "fmt" - "github.com/pkg/errors" + pkcs11uri "github.com/stefanberger/go-pkcs11uri" "gopkg.in/yaml.v3" ) @@ -42,7 +42,7 @@ func ParsePkcs11Uri(uri string) (*pkcs11uri.Pkcs11URI, error) { p11uri := pkcs11uri.New() err := p11uri.Parse(uri) if err != nil { - return nil, errors.Wrapf(err, "Could not parse Pkcs11URI from file") + return nil, fmt.Errorf("Could not parse Pkcs11URI from file: %w", err) } return p11uri, err } @@ -50,14 +50,14 @@ func ParsePkcs11Uri(uri string) (*pkcs11uri.Pkcs11URI, error) { // ParsePkcs11KeyFile parses a pkcs11 key file holding a pkcs11 URI describing a private key. // The file has the following yaml format: // pkcs11: -// - uri : +// - uri : // An error is returned if the pkcs11 URI is malformed func ParsePkcs11KeyFile(yamlstr []byte) (*Pkcs11KeyFileObject, error) { p11keyfile := Pkcs11KeyFile{} - err := yaml.Unmarshal([]byte(yamlstr), &p11keyfile) + err := yaml.Unmarshal(yamlstr, &p11keyfile) if err != nil { - return nil, errors.Wrapf(err, "Could not unmarshal pkcs11 keyfile") + return nil, fmt.Errorf("Could not unmarshal pkcs11 keyfile: %w", err) } p11uri, err := ParsePkcs11Uri(p11keyfile.Pkcs11.Uri) @@ -126,9 +126,9 @@ func GetDefaultModuleDirectoriesYaml(indent string) string { func ParsePkcs11ConfigFile(yamlstr []byte) (*Pkcs11Config, error) { p11conf := Pkcs11Config{} - err := yaml.Unmarshal([]byte(yamlstr), &p11conf) + err := yaml.Unmarshal(yamlstr, &p11conf) if err != nil { - return &p11conf, errors.Wrapf(err, "Could not parse Pkcs11Config") + return &p11conf, fmt.Errorf("Could not parse Pkcs11Config: %w", err) } return &p11conf, nil } diff --git a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go index 7d80f5f8..fe047a1e 100644 --- a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go +++ b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go @@ -1,3 +1,4 @@ +//go:build cgo // +build cgo /* @@ -25,6 +26,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "hash" "net/url" @@ -33,7 +35,6 @@ import ( "strings" "github.com/miekg/pkcs11" - "github.com/pkg/errors" pkcs11uri "github.com/stefanberger/go-pkcs11uri" ) @@ -76,11 +77,11 @@ func rsaPublicEncryptOAEP(pubKey *rsa.PublicKey, plaintext []byte) ([]byte, stri hashfunc = sha256.New() hashalg = "sha256" default: - return nil, "", errors.Errorf("Unsupported OAEP hash '%s'", oaephash) + return nil, "", fmt.Errorf("Unsupported OAEP hash '%s'", oaephash) } ciphertext, err := rsa.EncryptOAEP(hashfunc, rand.Reader, pubKey, plaintext, OAEPLabel) if err != nil { - return nil, "", errors.Wrapf(err, "rss.EncryptOAEP failed") + return nil, "", fmt.Errorf("rss.EncryptOAEP failed: %w", err) } return ciphertext, hashalg, nil @@ -104,7 +105,7 @@ func pkcs11UriGetLoginParameters(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperatio module, err := p11uri.GetModule() if err != nil { - return "", "", 0, errors.Wrap(err, "No module available in pkcs11 URI") + return "", "", 0, fmt.Errorf("No module available in pkcs11 URI: %w", err) } slotid := int64(-1) @@ -113,7 +114,7 @@ func pkcs11UriGetLoginParameters(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperatio if ok { slotid, err = strconv.ParseInt(slot, 10, 64) if err != nil { - return "", "", 0, errors.Wrap(err, "slot-id is not a valid number") + return "", "", 0, fmt.Errorf("slot-id is not a valid number: %w", err) } if slotid < 0 { return "", "", 0, fmt.Errorf("slot-id is a negative number") @@ -138,21 +139,21 @@ func pkcs11UriGetKeyIdAndLabel(p11uri *pkcs11uri.Pkcs11URI) (string, string, err // pkcs11OpenSession opens a session with a pkcs11 device at the given slot and logs in with the given PIN func pkcs11OpenSession(p11ctx *pkcs11.Ctx, slotid uint, pin string) (session pkcs11.SessionHandle, err error) { - session, err = p11ctx.OpenSession(uint(slotid), pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + session, err = p11ctx.OpenSession(slotid, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) if err != nil { - return 0, errors.Wrapf(err, "OpenSession to slot %d failed", slotid) + return 0, fmt.Errorf("OpenSession to slot %d failed: %w", slotid, err) } if len(pin) > 0 { err = p11ctx.Login(session, pkcs11.CKU_USER, pin) if err != nil { _ = p11ctx.CloseSession(session) - return 0, errors.Wrap(err, "Could not login to device") + return 0, fmt.Errorf("Could not login to device: %w", err) } } return session, nil } -// pkcs11UriLogin uses the given pkcs11 URI to select the pkcs11 module (share libary) and to get +// pkcs11UriLogin uses the given pkcs11 URI to select the pkcs11 module (shared library) and to get // the PIN to use for login; if the URI contains a slot-id, the given slot-id will be used, otherwise // one slot after the other will be attempted and the first one where login succeeds will be used func pkcs11UriLogin(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperation bool) (ctx *pkcs11.Ctx, session pkcs11.SessionHandle, err error) { @@ -170,40 +171,40 @@ func pkcs11UriLogin(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperation bool) (ctx if err != nil { p11Err := err.(pkcs11.Error) if p11Err != pkcs11.CKR_CRYPTOKI_ALREADY_INITIALIZED { - return nil, 0, errors.Wrap(err, "Initialize failed") + return nil, 0, fmt.Errorf("Initialize failed: %w", err) } } if slotid >= 0 { session, err := pkcs11OpenSession(p11ctx, uint(slotid), pin) return p11ctx, session, err - } else { - slots, err := p11ctx.GetSlotList(true) - if err != nil { - return nil, 0, errors.Wrap(err, "GetSlotList failed") - } + } - tokenlabel, ok := p11uri.GetPathAttribute("token", false) - if !ok { - return nil, 0, errors.New("Missing 'token' attribute since 'slot-id' was not given") - } + slots, err := p11ctx.GetSlotList(true) + if err != nil { + return nil, 0, fmt.Errorf("GetSlotList failed: %w", err) + } - for _, slot := range slots { - ti, err := p11ctx.GetTokenInfo(slot) - if err != nil || ti.Label != tokenlabel { - continue - } + tokenlabel, ok := p11uri.GetPathAttribute("token", false) + if !ok { + return nil, 0, errors.New("Missing 'token' attribute since 'slot-id' was not given") + } - session, err = pkcs11OpenSession(p11ctx, slot, pin) - if err == nil { - return p11ctx, session, err - } + for _, slot := range slots { + ti, err := p11ctx.GetTokenInfo(slot) + if err != nil || ti.Label != tokenlabel { + continue } - if len(pin) > 0 { - return nil, 0, errors.New("Could not create session to any slot and/or log in") + + session, err = pkcs11OpenSession(p11ctx, slot, pin) + if err == nil { + return p11ctx, session, err } - return nil, 0, errors.New("Could not create session to any slot") } + if len(pin) > 0 { + return nil, 0, errors.New("Could not create session to any slot and/or log in") + } + return nil, 0, errors.New("Could not create session to any slot") } func pkcs11Logout(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) { @@ -233,24 +234,24 @@ func findObject(p11ctx *pkcs11.Ctx, session pkcs11.SessionHandle, class uint, ke } if err := p11ctx.FindObjectsInit(session, template); err != nil { - return 0, errors.Wrap(err, "FindObjectsInit failed") + return 0, fmt.Errorf("FindObjectsInit failed: %w", err) } obj, _, err := p11ctx.FindObjects(session, 100) if err != nil { - return 0, errors.Wrap(err, "FindObjects failed") + return 0, fmt.Errorf("FindObjects failed: %w", err) } if err := p11ctx.FindObjectsFinal(session); err != nil { - return 0, errors.Wrap(err, "FindObjectsFinal failed") + return 0, fmt.Errorf("FindObjectsFinal failed: %w", err) } if len(obj) > 1 { - return 0, errors.Errorf("There are too many (=%d) keys with %s", len(obj), msg) + return 0, fmt.Errorf("There are too many (=%d) keys with %s", len(obj), msg) } else if len(obj) == 1 { return obj[0], nil } - return 0, errors.Errorf("Could not find any object with %s", msg) + return 0, fmt.Errorf("Could not find any object with %s", msg) } // publicEncryptOAEP uses a public key described by a pkcs11 URI to OAEP encrypt the given plaintext @@ -290,17 +291,17 @@ func publicEncryptOAEP(pubKey *Pkcs11KeyFileObject, plaintext []byte) ([]byte, s oaep = OAEPSha256Params hashalg = "sha256" default: - return nil, "", errors.Errorf("Unsupported OAEP hash '%s'", oaephash) + return nil, "", fmt.Errorf("Unsupported OAEP hash '%s'", oaephash) } err = p11ctx.EncryptInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP, oaep)}, p11PubKey) if err != nil { - return nil, "", errors.Wrap(err, "EncryptInit error") + return nil, "", fmt.Errorf("EncryptInit error: %w", err) } ciphertext, err := p11ctx.Encrypt(session, plaintext) if err != nil { - return nil, "", errors.Wrap(err, "Encrypt failed") + return nil, "", fmt.Errorf("Encrypt failed: %w", err) } return ciphertext, hashalg, nil } @@ -338,16 +339,16 @@ func privateDecryptOAEP(privKeyObj *Pkcs11KeyFileObject, ciphertext []byte, hash case "sha256": oaep = OAEPSha256Params default: - return nil, errors.Errorf("Unsupported hash algorithm '%s' for decryption", hashalg) + return nil, fmt.Errorf("Unsupported hash algorithm '%s' for decryption", hashalg) } err = p11ctx.DecryptInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP, oaep)}, p11PrivKey) if err != nil { - return nil, errors.Wrapf(err, "DecryptInit failed") + return nil, fmt.Errorf("DecryptInit failed: %w", err) } plaintext, err := p11ctx.Decrypt(session, ciphertext) if err != nil { - return nil, errors.Wrapf(err, "Decrypt failed") + return nil, fmt.Errorf("Decrypt failed: %w", err) } return plaintext, err } @@ -373,19 +374,19 @@ type Pkcs11Recipient struct { // may either be *rsa.PublicKey or *pkcs11uri.Pkcs11URI; the returned byte array is a JSON string of the // following format: // { -// recipients: [ // recipient list -// { -// "version": 0, -// "blob": , -// "hash": -// } , -// { -// "version": 0, -// "blob": , -// "hash": -// } , -// [...] -// ] +// recipients: [ // recipient list +// { +// "version": 0, +// "blob": , +// "hash": +// } , +// { +// "version": 0, +// "blob": , +// "hash": +// } , +// [...] +// ] // } func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) { var ( @@ -402,7 +403,7 @@ func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) { case *Pkcs11KeyFileObject: ciphertext, hashalg, err = publicEncryptOAEP(pkey, data) default: - err = errors.Errorf("Unsupported key object type for pkcs11 public key") + err = fmt.Errorf("Unsupported key object type for pkcs11 public key") } if err != nil { return nil, err @@ -422,33 +423,32 @@ func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) { // Decrypt tries to decrypt one of the recipients' blobs using a pkcs11 private key. // The input pkcs11blobstr is a string with the following format: // { -// recipients: [ // recipient list -// { -// "version": 0, -// "blob": , -// "hash": -// } , -// { -// "version": 0, -// "blob": , -// "hash": -// } , -// [...] +// recipients: [ // recipient list +// { +// "version": 0, +// "blob": , +// "hash": +// } , +// { +// "version": 0, +// "blob": , +// "hash": +// } , +// [...] // } // Note: More recent versions of this code explicitly write 'sha1' -// while older versions left it empty in case of 'sha1'. -// +// while older versions left it empty in case of 'sha1'. func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, error) { pkcs11blob := Pkcs11Blob{} err := json.Unmarshal(pkcs11blobstr, &pkcs11blob) if err != nil { - return nil, errors.Wrapf(err, "Could not parse Pkcs11Blob") + return nil, fmt.Errorf("Could not parse Pkcs11Blob: %w", err) } switch pkcs11blob.Version { case 0: // latest supported version default: - return nil, errors.Errorf("Found Pkcs11Blob with version %d but maximum supported version is 0.", pkcs11blob.Version) + return nil, fmt.Errorf("found Pkcs11Blob with version %d but maximum supported version is 0", pkcs11blob.Version) } // since we do trial and error, collect all encountered errors errs := "" @@ -458,7 +458,7 @@ func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, case 0: // last supported version default: - return nil, errors.Errorf("Found Pkcs11Recipient with version %d but maximum supported version is 0.", recipient.Version) + return nil, fmt.Errorf("found Pkcs11Recipient with version %d but maximum supported version is 0", recipient.Version) } ciphertext, err := base64.StdEncoding.DecodeString(recipient.Blob) @@ -481,5 +481,5 @@ func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, } } - return nil, errors.Errorf("Could not find a pkcs11 key for decryption:\n%s", errs) + return nil, fmt.Errorf("Could not find a pkcs11 key for decryption:\n%s", errs) } diff --git a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go index 6edf7526..6cf0aa2a 100644 --- a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go +++ b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go @@ -1,3 +1,4 @@ +//go:build !cgo // +build !cgo /* @@ -18,14 +19,12 @@ package pkcs11 -import ( - "github.com/pkg/errors" -) +import "fmt" func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) { - return nil, errors.Errorf("ocicrypt pkcs11 not supported on this build") + return nil, fmt.Errorf("ocicrypt pkcs11 not supported on this build") } func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, error) { - return nil, errors.Errorf("ocicrypt pkcs11 not supported on this build") + return nil, fmt.Errorf("ocicrypt pkcs11 not supported on this build") } diff --git a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go index 306e372d..391b98bd 100644 --- a/vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go +++ b/vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go @@ -17,12 +17,11 @@ package pkcs11 import ( + "fmt" "os" "runtime" "strings" "sync" - - "github.com/pkg/errors" ) var ( @@ -45,7 +44,7 @@ func setEnvVars(env map[string]string) ([]string, error) { err := os.Setenv(k, v) if err != nil { restoreEnv(oldenv) - return nil, errors.Wrapf(err, "Could not set environment variable '%s' to '%s'", k, v) + return nil, fmt.Errorf("Could not set environment variable '%s' to '%s': %w", k, v, err) } } diff --git a/vendor/github.com/containers/ocicrypt/encryption.go b/vendor/github.com/containers/ocicrypt/encryption.go index 328a32fb..b6fa9db4 100644 --- a/vendor/github.com/containers/ocicrypt/encryption.go +++ b/vendor/github.com/containers/ocicrypt/encryption.go @@ -19,22 +19,22 @@ package ocicrypt import ( "encoding/base64" "encoding/json" + "errors" "fmt" - keyproviderconfig "github.com/containers/ocicrypt/config/keyprovider-config" - "github.com/containers/ocicrypt/keywrap/keyprovider" "io" "strings" "github.com/containers/ocicrypt/blockcipher" "github.com/containers/ocicrypt/config" + keyproviderconfig "github.com/containers/ocicrypt/config/keyprovider-config" "github.com/containers/ocicrypt/keywrap" "github.com/containers/ocicrypt/keywrap/jwe" + "github.com/containers/ocicrypt/keywrap/keyprovider" "github.com/containers/ocicrypt/keywrap/pgp" "github.com/containers/ocicrypt/keywrap/pkcs11" "github.com/containers/ocicrypt/keywrap/pkcs7" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -133,11 +133,11 @@ func EncryptLayer(ec *config.EncryptConfig, encOrPlainLayerReader io.Reader, des } privOptsData, err = json.Marshal(opts.Private) if err != nil { - return nil, errors.Wrapf(err, "could not JSON marshal opts") + return nil, fmt.Errorf("could not JSON marshal opts: %w", err) } pubOptsData, err = json.Marshal(opts.Public) if err != nil { - return nil, errors.Wrapf(err, "could not JSON marshal opts") + return nil, fmt.Errorf("could not JSON marshal opts: %w", err) } } @@ -243,9 +243,9 @@ func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) } } if !privKeyGiven { - return nil, errors.New("missing private key needed for decryption") + return nil, fmt.Errorf("missing private key needed for decryption:\n%s", errs) } - return nil, errors.Errorf("no suitable key unwrapper found or none of the private keys could be used for decryption:\n%s", errs) + return nil, fmt.Errorf("no suitable key unwrapper found or none of the private keys could be used for decryption:\n%s", errs) } func getLayerPubOpts(desc ocispec.Descriptor) ([]byte, error) { @@ -276,7 +276,7 @@ func preUnwrapKey(keywrapper keywrap.KeyWrapper, dc *config.DecryptConfig, b64An } return optsData, nil } - return nil, errors.Errorf("no suitable key found for decrypting layer key:\n%s", errs) + return nil, fmt.Errorf("no suitable key found for decrypting layer key:\n%s", errs) } // commonEncryptLayer is a function to encrypt the plain layer using a new random @@ -311,7 +311,7 @@ func commonDecryptLayer(encLayerReader io.Reader, privOptsData []byte, pubOptsDa privOpts := blockcipher.PrivateLayerBlockCipherOptions{} err := json.Unmarshal(privOptsData, &privOpts) if err != nil { - return nil, "", errors.Wrapf(err, "could not JSON unmarshal privOptsData") + return nil, "", fmt.Errorf("could not JSON unmarshal privOptsData: %w", err) } lbch, err := blockcipher.NewLayerBlockCipherHandler() @@ -323,7 +323,7 @@ func commonDecryptLayer(encLayerReader io.Reader, privOptsData []byte, pubOptsDa if len(pubOptsData) > 0 { err := json.Unmarshal(pubOptsData, &pubOpts) if err != nil { - return nil, "", errors.Wrapf(err, "could not JSON unmarshal pubOptsData") + return nil, "", fmt.Errorf("could not JSON unmarshal pubOptsData: %w", err) } } diff --git a/vendor/github.com/containers/ocicrypt/gpg.go b/vendor/github.com/containers/ocicrypt/gpg.go index b9d55539..3912e82d 100644 --- a/vendor/github.com/containers/ocicrypt/gpg.go +++ b/vendor/github.com/containers/ocicrypt/gpg.go @@ -17,16 +17,17 @@ package ocicrypt import ( + "errors" "fmt" - "io/ioutil" + "io" "os" "os/exec" "regexp" "strconv" "strings" + "sync" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" "golang.org/x/term" ) @@ -132,7 +133,7 @@ func (gc *gpgv2Client) GetGPGPrivateKey(keyid uint64, passphrase string) ([]byte rfile, wfile, err := os.Pipe() if err != nil { - return nil, errors.Wrapf(err, "could not create pipe") + return nil, fmt.Errorf("could not create pipe: %w", err) } defer func() { rfile.Close() @@ -272,8 +273,8 @@ func runGPGGetOutput(cmd *exec.Cmd) ([]byte, error) { return nil, err } - stdoutstr, err2 := ioutil.ReadAll(stdout) - stderrstr, _ := ioutil.ReadAll(stderr) + stdoutstr, err2 := io.ReadAll(stdout) + stderrstr, _ := io.ReadAll(stderr) if err := cmd.Wait(); err != nil { return nil, fmt.Errorf("error from %s: %s", cmd.Path, string(stderrstr)) @@ -310,9 +311,15 @@ func resolveRecipients(gc GPGClient, recipients []string) []string { return result } -var emailPattern = regexp.MustCompile(`uid\s+\[.*\]\s.*\s<(?P.+)>`) +var ( + onceRegexp sync.Once + emailPattern *regexp.Regexp +) func extractEmailFromDetails(details []byte) string { + onceRegexp.Do(func() { + emailPattern = regexp.MustCompile(`uid\s+\[.*\]\s.*\s<(?P.+)>`) + }) loc := emailPattern.FindSubmatchIndex(details) if len(loc) == 0 { return "" @@ -352,7 +359,7 @@ func GPGGetPrivateKey(descs []ocispec.Descriptor, gpgClient GPGClient, gpgVault } keywrapper := GetKeyWrapper(scheme) if keywrapper == nil { - return nil, nil, errors.Errorf("could not get KeyWrapper for %s\n", scheme) + return nil, nil, fmt.Errorf("could not get KeyWrapper for %s", scheme) } keyIds, err := keywrapper.GetKeyIdsFromPacket(b64pgpPackets) if err != nil { @@ -411,7 +418,7 @@ func GPGGetPrivateKey(descs []ocispec.Descriptor, gpgClient GPGClient, gpgVault if !found && len(b64pgpPackets) > 0 && mustFindKey { ids := uint64ToStringArray("0x%x", keyIds) - return nil, nil, errors.Errorf("missing key for decryption of layer %x of %s. Need one of the following keys: %s", desc.Digest, desc.Platform, strings.Join(ids, ", ")) + return nil, nil, fmt.Errorf("missing key for decryption of layer %x of %s. Need one of the following keys: %s", desc.Digest, desc.Platform, strings.Join(ids, ", ")) } } } diff --git a/vendor/github.com/containers/ocicrypt/gpgvault.go b/vendor/github.com/containers/ocicrypt/gpgvault.go index dd9a1000..f1bd0d98 100644 --- a/vendor/github.com/containers/ocicrypt/gpgvault.go +++ b/vendor/github.com/containers/ocicrypt/gpgvault.go @@ -18,9 +18,9 @@ package ocicrypt import ( "bytes" - "io/ioutil" + "fmt" + "os" - "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -55,7 +55,7 @@ func (g *gpgVault) AddSecretKeyRingData(gpgSecretKeyRingData []byte) error { r := bytes.NewReader(gpgSecretKeyRingData) entityList, err := openpgp.ReadKeyRing(r) if err != nil { - return errors.Wrapf(err, "could not read keyring") + return fmt.Errorf("could not read keyring: %w", err) } g.entityLists = append(g.entityLists, entityList) g.keyDataList = append(g.keyDataList, gpgSecretKeyRingData) @@ -76,7 +76,7 @@ func (g *gpgVault) AddSecretKeyRingDataArray(gpgSecretKeyRingDataArray [][]byte) // AddSecretKeyRingFiles adds the secret key rings given their filenames func (g *gpgVault) AddSecretKeyRingFiles(filenames []string) error { for _, filename := range filenames { - gpgSecretKeyRingData, err := ioutil.ReadFile(filename) + gpgSecretKeyRingData, err := os.ReadFile(filename) if err != nil { return err } diff --git a/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go index 717e7f21..18f4fa92 100644 --- a/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go +++ b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go @@ -1,8 +1,8 @@ package helpers import ( + "errors" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -12,8 +12,6 @@ import ( "github.com/containers/ocicrypt/config/pkcs11config" "github.com/containers/ocicrypt/crypto/pkcs11" encutils "github.com/containers/ocicrypt/utils" - - "github.com/pkg/errors" ) // processRecipientKeys sorts the array of recipients by type. Recipients may be either @@ -43,9 +41,9 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, [] gpgRecipients = append(gpgRecipients, []byte(value)) case "jwe": - tmp, err := ioutil.ReadFile(value) + tmp, err := os.ReadFile(value) if err != nil { - return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file") + return nil, nil, nil, nil, nil, nil, fmt.Errorf("Unable to read file: %w", err) } if !encutils.IsPublicKey(tmp) { return nil, nil, nil, nil, nil, nil, errors.New("File provided is not a public key") @@ -53,9 +51,9 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, [] pubkeys = append(pubkeys, tmp) case "pkcs7": - tmp, err := ioutil.ReadFile(value) + tmp, err := os.ReadFile(value) if err != nil { - return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file") + return nil, nil, nil, nil, nil, nil, fmt.Errorf("Unable to read file: %w", err) } if !encutils.IsCertificate(tmp) { return nil, nil, nil, nil, nil, nil, errors.New("File provided is not an x509 cert") @@ -63,9 +61,9 @@ func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, [] x509s = append(x509s, tmp) case "pkcs11": - tmp, err := ioutil.ReadFile(value) + tmp, err := os.ReadFile(value) if err != nil { - return nil, nil, nil, nil, nil, nil, errors.Wrap(err, "Unable to read file") + return nil, nil, nil, nil, nil, nil, fmt.Errorf("Unable to read file: %w", err) } if encutils.IsPkcs11PublicKey(tmp) { pkcs11Yamls = append(pkcs11Yamls, tmp) @@ -93,9 +91,9 @@ func processx509Certs(keys []string) ([][]byte, error) { if _, err := os.Stat(fileName); os.IsNotExist(err) { continue } - tmp, err := ioutil.ReadFile(fileName) + tmp, err := os.ReadFile(fileName) if err != nil { - return nil, errors.Wrap(err, "Unable to read file") + return nil, fmt.Errorf("Unable to read file: %w", err) } if !encutils.IsCertificate(tmp) { continue @@ -113,14 +111,14 @@ func processx509Certs(keys []string) ([][]byte, error) { // - func processPwdString(pwdString string) ([]byte, error) { if strings.HasPrefix(pwdString, "file=") { - return ioutil.ReadFile(pwdString[5:]) + return os.ReadFile(pwdString[5:]) } else if strings.HasPrefix(pwdString, "pass=") { return []byte(pwdString[5:]), nil } else if strings.HasPrefix(pwdString, "fd=") { fdStr := pwdString[3:] fd, err := strconv.Atoi(fdStr) if err != nil { - return nil, errors.Wrapf(err, "could not parse file descriptor %s", fdStr) + return nil, fmt.Errorf("could not parse file descriptor %s: %w", fdStr, err) } f := os.NewFile(uintptr(fd), "pwdfile") if f == nil { @@ -130,7 +128,7 @@ func processPwdString(pwdString string) ([]byte, error) { pwd := make([]byte, 64) n, err := f.Read(pwd) if err != nil { - return nil, errors.Wrapf(err, "could not read from file descriptor") + return nil, fmt.Errorf("could not read from file descriptor: %w", err) } return pwd[:n], nil } @@ -174,7 +172,7 @@ func processPrivateKeyFiles(keyFilesAndPwds []string) ([][]byte, [][]byte, [][]b } keyfile := parts[0] - tmp, err := ioutil.ReadFile(keyfile) + tmp, err := os.ReadFile(keyfile) if err != nil { return nil, nil, nil, nil, nil, nil, err } @@ -374,7 +372,6 @@ func CreateCryptoConfig(recipients []string, keys []string) (encconfig.CryptoCon if len(ccs) > 0 { return encconfig.CombineCryptoConfigs(ccs), nil - } else { - return encconfig.CryptoConfig{}, nil } + return encconfig.CryptoConfig{}, nil } diff --git a/vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go b/vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go index 41d0f1b3..9d1fe206 100644 --- a/vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go +++ b/vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go @@ -18,11 +18,12 @@ package jwe import ( "crypto/ecdsa" + "errors" + "fmt" "github.com/containers/ocicrypt/config" "github.com/containers/ocicrypt/keywrap" "github.com/containers/ocicrypt/utils" - "github.com/pkg/errors" jose "gopkg.in/square/go-jose.v2" ) @@ -54,11 +55,11 @@ func (kw *jweKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([] encrypter, err := jose.NewMultiEncrypter(jose.A256GCM, joseRecipients, nil) if err != nil { - return nil, errors.Wrapf(err, "jose.NewMultiEncrypter failed") + return nil, fmt.Errorf("jose.NewMultiEncrypter failed: %w", err) } jwe, err := encrypter.Encrypt(optsData) if err != nil { - return nil, errors.Wrapf(err, "JWE Encrypt failed") + return nil, fmt.Errorf("JWE Encrypt failed: %w", err) } return []byte(jwe.FullSerialize()), nil } diff --git a/vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go b/vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go index 3b4c47ed..ddb244a8 100644 --- a/vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go +++ b/vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go @@ -19,12 +19,14 @@ package keyprovider import ( "context" "encoding/json" + "errors" + "fmt" + "github.com/containers/ocicrypt/config" keyproviderconfig "github.com/containers/ocicrypt/config/keyprovider-config" "github.com/containers/ocicrypt/keywrap" "github.com/containers/ocicrypt/utils" keyproviderpb "github.com/containers/ocicrypt/utils/keyprovider" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" "google.golang.org/grpc" ) @@ -112,13 +114,13 @@ func (kw *keyProviderKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []b if kw.attrs.Command != nil { protocolOuput, err := getProviderCommandOutput(input, kw.attrs.Command) if err != nil { - return nil, errors.Wrap(err, "error while retrieving keyprovider protocol command output") + return nil, fmt.Errorf("error while retrieving keyprovider protocol command output: %w", err) } return protocolOuput.KeyWrapResults.Annotation, nil } else if kw.attrs.Grpc != "" { protocolOuput, err := getProviderGRPCOutput(input, kw.attrs.Grpc, OpKeyWrap) if err != nil { - return nil, errors.Wrap(err, "error while retrieving keyprovider protocol grpc output") + return nil, fmt.Errorf("error while retrieving keyprovider protocol grpc output: %w", err) } return protocolOuput.KeyWrapResults.Annotation, nil @@ -170,7 +172,7 @@ func getProviderGRPCOutput(input []byte, connString string, operation KeyProvide var grpcOutput *keyproviderpb.KeyProviderKeyWrapProtocolOutput cc, err := grpc.Dial(connString, grpc.WithInsecure()) if err != nil { - return nil, errors.Wrap(err, "error while dialing rpc server") + return nil, fmt.Errorf("error while dialing rpc server: %w", err) } defer func() { derr := cc.Close() @@ -187,12 +189,12 @@ func getProviderGRPCOutput(input []byte, connString string, operation KeyProvide if operation == OpKeyWrap { grpcOutput, err = client.WrapKey(context.Background(), req) if err != nil { - return nil, errors.Wrap(err, "Error from grpc method") + return nil, fmt.Errorf("Error from grpc method: %w", err) } } else if operation == OpKeyUnwrap { grpcOutput, err = client.UnWrapKey(context.Background(), req) if err != nil { - return nil, errors.Wrap(err, "Error from grpc method") + return nil, fmt.Errorf("Error from grpc method: %w", err) } } else { return nil, errors.New("Unsupported operation") @@ -201,7 +203,7 @@ func getProviderGRPCOutput(input []byte, connString string, operation KeyProvide respBytes := grpcOutput.GetKeyProviderKeyWrapProtocolOutput() err = json.Unmarshal(respBytes, &protocolOuput) if err != nil { - return nil, errors.Wrap(err, "Error while unmarshalling grpc method output") + return nil, fmt.Errorf("Error while unmarshalling grpc method output: %w", err) } return &protocolOuput, nil @@ -216,7 +218,7 @@ func getProviderCommandOutput(input []byte, command *keyproviderconfig.Command) } err = json.Unmarshal(respBytes, &protocolOuput) if err != nil { - return nil, errors.Wrap(err, "Error while unmarshalling binary executable command output") + return nil, fmt.Errorf("Error while unmarshalling binary executable command output: %w", err) } return &protocolOuput, nil } diff --git a/vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go b/vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go index 275a3d8b..4ab9bd97 100644 --- a/vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go +++ b/vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go @@ -21,16 +21,15 @@ import ( "crypto" "crypto/rand" "encoding/base64" + "errors" "fmt" "io" - "io/ioutil" "net/mail" "strconv" "strings" "github.com/containers/ocicrypt/config" "github.com/containers/ocicrypt/keywrap" - "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -64,7 +63,7 @@ func (kw *gpgKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([] ciphertext := new(bytes.Buffer) el, err := kw.createEntityList(ec) if err != nil { - return nil, errors.Wrap(err, "unable to create entity list") + return nil, fmt.Errorf("unable to create entity list: %w", err) } if len(el) == 0 { // nothing to do -- not an error @@ -100,7 +99,7 @@ func (kw *gpgKeyWrapper) UnwrapKey(dc *config.DecryptConfig, pgpPacket []byte) ( r := bytes.NewBuffer(pgpPrivateKey) entityList, err := openpgp.ReadKeyRing(r) if err != nil { - return nil, errors.Wrap(err, "unable to parse private keys") + return nil, fmt.Errorf("unable to parse private keys: %w", err) } var prompt openpgp.PromptFunction @@ -126,7 +125,7 @@ func (kw *gpgKeyWrapper) UnwrapKey(dc *config.DecryptConfig, pgpPacket []byte) ( continue } // we get the plain key options back - optsData, err := ioutil.ReadAll(md.UnverifiedBody) + optsData, err := io.ReadAll(md.UnverifiedBody) if err != nil { continue } @@ -142,7 +141,7 @@ func (kw *gpgKeyWrapper) GetKeyIdsFromPacket(b64pgpPackets string) ([]uint64, er for _, b64pgpPacket := range strings.Split(b64pgpPackets, ",") { pgpPacket, err := base64.StdEncoding.DecodeString(b64pgpPacket) if err != nil { - return nil, errors.Wrapf(err, "could not decode base64 encoded PGP packet") + return nil, fmt.Errorf("could not decode base64 encoded PGP packet: %w", err) } newids, err := kw.getKeyIDs(pgpPacket) if err != nil { @@ -166,7 +165,7 @@ ParsePackets: break ParsePackets } if err != nil { - return []uint64{}, errors.Wrapf(err, "packets.Next() failed") + return []uint64{}, fmt.Errorf("packets.Next() failed: %w", err) } switch p := p.(type) { case *packet.EncryptedKey: diff --git a/vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go b/vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go index c44c4533..236764d2 100644 --- a/vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go +++ b/vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go @@ -17,12 +17,13 @@ package pkcs11 import ( + "errors" + "fmt" + "github.com/containers/ocicrypt/config" "github.com/containers/ocicrypt/crypto/pkcs11" "github.com/containers/ocicrypt/keywrap" "github.com/containers/ocicrypt/utils" - - "github.com/pkg/errors" ) type pkcs11KeyWrapper struct { @@ -51,7 +52,7 @@ func (kw *pkcs11KeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) jsonString, err := pkcs11.EncryptMultiple(pkcs11Recipients, optsData) if err != nil { - return nil, errors.Wrapf(err, "PKCS11 EncryptMulitple failed") + return nil, fmt.Errorf("PKCS11 EncryptMulitple failed: %w", err) } return jsonString, nil } @@ -91,7 +92,7 @@ func (kw *pkcs11KeyWrapper) UnwrapKey(dc *config.DecryptConfig, jsonString []byt return plaintext, nil } - return nil, errors.Wrapf(err, "PKCS11: No suitable private key found for decryption") + return nil, fmt.Errorf("PKCS11: No suitable private key found for decryption: %w", err) } func (kw *pkcs11KeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool { diff --git a/vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go b/vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go index 1feae462..603925df 100644 --- a/vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go +++ b/vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go @@ -19,11 +19,12 @@ package pkcs7 import ( "crypto" "crypto/x509" + "errors" + "fmt" "github.com/containers/ocicrypt/config" "github.com/containers/ocicrypt/keywrap" "github.com/containers/ocicrypt/utils" - "github.com/pkg/errors" "go.mozilla.org/pkcs7" ) @@ -104,7 +105,7 @@ func (kw *pkcs7KeyWrapper) UnwrapKey(dc *config.DecryptConfig, pkcs7Packet []byt p7, err := pkcs7.Parse(pkcs7Packet) if err != nil { - return nil, errors.Wrapf(err, "could not parse PKCS7 packet") + return nil, fmt.Errorf("could not parse PKCS7 packet: %w", err) } for idx, privKey := range privKeys { diff --git a/vendor/github.com/containers/ocicrypt/utils/ioutils.go b/vendor/github.com/containers/ocicrypt/utils/ioutils.go index e4107bee..c6265168 100644 --- a/vendor/github.com/containers/ocicrypt/utils/ioutils.go +++ b/vendor/github.com/containers/ocicrypt/utils/ioutils.go @@ -18,7 +18,7 @@ package utils import ( "bytes" - "github.com/pkg/errors" + "fmt" "io" "os/exec" ) @@ -44,13 +44,15 @@ type Runner struct{} // ExecuteCommand is used to execute a linux command line command and return the output of the command with an error if it exists. func (r Runner) Exec(cmdName string, args []string, input []byte) ([]byte, error) { var out bytes.Buffer + var stderr bytes.Buffer stdInputBuffer := bytes.NewBuffer(input) cmd := exec.Command(cmdName, args...) cmd.Stdin = stdInputBuffer cmd.Stdout = &out + cmd.Stderr = &stderr err := cmd.Run() if err != nil { - return nil, errors.Wrapf(err, "Error while running command: %s", cmdName) + return nil, fmt.Errorf("Error while running command: %s. stderr: %s: %w", cmdName, stderr.String(), err) } return out.Bytes(), nil } diff --git a/vendor/github.com/containers/ocicrypt/utils/testing.go b/vendor/github.com/containers/ocicrypt/utils/testing.go index 38633b19..69bb9d12 100644 --- a/vendor/github.com/containers/ocicrypt/utils/testing.go +++ b/vendor/github.com/containers/ocicrypt/utils/testing.go @@ -24,17 +24,16 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "time" - - "github.com/pkg/errors" ) // CreateRSAKey creates an RSA key func CreateRSAKey(bits int) (*rsa.PrivateKey, error) { key, err := rsa.GenerateKey(rand.Reader, bits) if err != nil { - return nil, errors.Wrap(err, "rsa.GenerateKey failed") + return nil, fmt.Errorf("rsa.GenerateKey failed: %w", err) } return key, nil } @@ -49,7 +48,7 @@ func CreateRSATestKey(bits int, password []byte, pemencode bool) ([]byte, []byte pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { - return nil, nil, errors.Wrap(err, "x509.MarshalPKIXPublicKey failed") + return nil, nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err) } privData := x509.MarshalPKCS1PrivateKey(key) @@ -69,7 +68,7 @@ func CreateRSATestKey(bits int, password []byte, pemencode bool) ([]byte, []byte if len(password) > 0 { block, err = x509.EncryptPEMBlock(rand.Reader, typ, privData, password, x509.PEMCipherAES256) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility if err != nil { - return nil, nil, errors.Wrap(err, "x509.EncryptPEMBlock failed") + return nil, nil, fmt.Errorf("x509.EncryptPEMBlock failed: %w", err) } } else { block = &pem.Block{ @@ -88,17 +87,17 @@ func CreateRSATestKey(bits int, password []byte, pemencode bool) ([]byte, []byte func CreateECDSATestKey(curve elliptic.Curve) ([]byte, []byte, error) { key, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { - return nil, nil, errors.Wrapf(err, "ecdsa.GenerateKey failed") + return nil, nil, fmt.Errorf("ecdsa.GenerateKey failed: %w", err) } pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { - return nil, nil, errors.Wrapf(err, "x509.MarshalPKIXPublicKey failed") + return nil, nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err) } privData, err := x509.MarshalECPrivateKey(key) if err != nil { - return nil, nil, errors.Wrapf(err, "x509.MarshalECPrivateKey failed") + return nil, nil, fmt.Errorf("x509.MarshalECPrivateKey failed: %w", err) } return pubData, privData, nil @@ -108,7 +107,7 @@ func CreateECDSATestKey(curve elliptic.Curve) ([]byte, []byte, error) { func CreateTestCA() (*rsa.PrivateKey, *x509.Certificate, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return nil, nil, errors.Wrap(err, "rsa.GenerateKey failed") + return nil, nil, fmt.Errorf("rsa.GenerateKey failed: %w", err) } ca := &x509.Certificate{ @@ -154,12 +153,12 @@ func certifyKey(pub interface{}, template *x509.Certificate, caKey *rsa.PrivateK certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, pub, caKey) if err != nil { - return nil, errors.Wrap(err, "x509.CreateCertificate failed") + return nil, fmt.Errorf("x509.CreateCertificate failed: %w", err) } cert, err := x509.ParseCertificate(certDER) if err != nil { - return nil, errors.Wrap(err, "x509.ParseCertificate failed") + return nil, fmt.Errorf("x509.ParseCertificate failed: %w", err) } return cert, nil diff --git a/vendor/github.com/containers/ocicrypt/utils/utils.go b/vendor/github.com/containers/ocicrypt/utils/utils.go index 07fe6d36..c24ee3b3 100644 --- a/vendor/github.com/containers/ocicrypt/utils/utils.go +++ b/vendor/github.com/containers/ocicrypt/utils/utils.go @@ -21,12 +21,12 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" + "errors" "fmt" "strings" "github.com/containers/ocicrypt/crypto/pkcs11" - "github.com/pkg/errors" "golang.org/x/crypto/openpgp" json "gopkg.in/square/go-jose.v2" ) @@ -36,7 +36,7 @@ func parseJWKPrivateKey(privKey []byte, prefix string) (interface{}, error) { jwk := json.JSONWebKey{} err := jwk.UnmarshalJSON(privKey) if err != nil { - return nil, errors.Wrapf(err, "%s: Could not parse input as JWK", prefix) + return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err) } if jwk.IsPublic() { return nil, fmt.Errorf("%s: JWK is not a private key", prefix) @@ -49,7 +49,7 @@ func parseJWKPublicKey(privKey []byte, prefix string) (interface{}, error) { jwk := json.JSONWebKey{} err := jwk.UnmarshalJSON(privKey) if err != nil { - return nil, errors.Wrapf(err, "%s: Could not parse input as JWK", prefix) + return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err) } if !jwk.IsPublic() { return nil, fmt.Errorf("%s: JWK is not a public key", prefix) @@ -97,11 +97,11 @@ func ParsePrivateKey(privKey, privKeyPassword []byte, prefix string) (interface{ var der []byte if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility if privKeyPassword == nil { - return nil, errors.Errorf("%s: Missing password for encrypted private key", prefix) + return nil, fmt.Errorf("%s: Missing password for encrypted private key", prefix) } der, err = x509.DecryptPEMBlock(block, privKeyPassword) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility if err != nil { - return nil, errors.Errorf("%s: Wrong password: could not decrypt private key", prefix) + return nil, fmt.Errorf("%s: Wrong password: could not decrypt private key", prefix) } } else { der = block.Bytes @@ -111,7 +111,7 @@ func ParsePrivateKey(privKey, privKeyPassword []byte, prefix string) (interface{ if err != nil { key, err = x509.ParsePKCS1PrivateKey(der) if err != nil { - return nil, errors.Wrapf(err, "%s: Could not parse private key", prefix) + return nil, fmt.Errorf("%s: Could not parse private key: %w", prefix, err) } } } else { @@ -145,7 +145,7 @@ func ParsePublicKey(pubKey []byte, prefix string) (interface{}, error) { if block != nil { key, err = x509.ParsePKIXPublicKey(block.Bytes) if err != nil { - return nil, errors.Wrapf(err, "%s: Could not parse public key", prefix) + return nil, fmt.Errorf("%s: Could not parse public key: %w", prefix, err) } } else { key, err = parseJWKPublicKey(pubKey, prefix) @@ -179,7 +179,7 @@ func ParseCertificate(certBytes []byte, prefix string) (*x509.Certificate, error } x509Cert, err = x509.ParseCertificate(block.Bytes) if err != nil { - return nil, errors.Wrapf(err, "%s: Could not parse x509 certificate", prefix) + return nil, fmt.Errorf("%s: Could not parse x509 certificate: %w", prefix, err) } } return x509Cert, err diff --git a/vendor/github.com/containers/podman/v4/cmd/podman/parse/json.go b/vendor/github.com/containers/podman/v4/cmd/podman/parse/json.go deleted file mode 100644 index 85572a05..00000000 --- a/vendor/github.com/containers/podman/v4/cmd/podman/parse/json.go +++ /dev/null @@ -1,10 +0,0 @@ -package parse - -import "regexp" - -var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*(\.)?\s*}})\s*$`) - -// MatchesJSONFormat test CLI --format string to be a JSON request. -func MatchesJSONFormat(s string) bool { - return jsonFormatRegex.Match([]byte(s)) -} diff --git a/vendor/github.com/containers/podman/v4/cmd/podman/parse/net.go b/vendor/github.com/containers/podman/v4/cmd/podman/parse/net.go index a5c7a0d9..0d739059 100644 --- a/vendor/github.com/containers/podman/v4/cmd/podman/parse/net.go +++ b/vendor/github.com/containers/podman/v4/cmd/podman/parse/net.go @@ -8,8 +8,9 @@ import ( "net" "net/url" "os" - "regexp" "strings" + + "github.com/containers/storage/pkg/regexp" ) const ( @@ -22,8 +23,8 @@ const ( var ( whiteSpaces = " \t" - alphaRegexp = regexp.MustCompile(`[a-zA-Z]`) - domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) + alphaRegexp = regexp.Delayed(`[a-zA-Z]`) + domainRegexp = regexp.Delayed(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) ) // validateExtraHost validates that the specified string is a valid extrahost and returns it. diff --git a/vendor/github.com/containers/podman/v4/cmd/podman/registry/config.go b/vendor/github.com/containers/podman/v4/cmd/podman/registry/config.go index a118fdc4..3e5920c0 100644 --- a/vendor/github.com/containers/podman/v4/cmd/podman/registry/config.go +++ b/vendor/github.com/containers/podman/v4/cmd/podman/registry/config.go @@ -34,7 +34,7 @@ var ( // ABIMode used in cobra.Annotations registry.EngineMode when command only supports ABIMode ABIMode = entities.ABIMode.String() - // TunnelMode used in in cobra.Annotations registry.EngineMode when command only supports TunnelMode + // TunnelMode used in cobra.Annotations registry.EngineMode when command only supports TunnelMode TunnelMode = entities.TunnelMode.String() ) @@ -51,7 +51,7 @@ func newPodmanConfig() { os.Exit(1) } - cfg, err := config.NewConfig("") + defaultConfig, err := config.Default() if err != nil { fmt.Fprint(os.Stderr, "Failed to obtain podman configuration: "+err.Error()) os.Exit(1) @@ -76,11 +76,11 @@ func newPodmanConfig() { // If EngineMode==Tunnel has not been set on the command line or environment // but has been set in containers.conf... - if mode == entities.ABIMode && cfg.Engine.Remote { + if mode == entities.ABIMode && defaultConfig.Engine.Remote { mode = entities.TunnelMode } - podmanOptions = entities.PodmanConfig{Config: cfg, EngineMode: mode} + podmanOptions = entities.PodmanConfig{ContainersConf: &config.Config{}, ContainersConfDefaultsRO: defaultConfig, EngineMode: mode} } // setXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set. @@ -105,6 +105,10 @@ func setXdgDirs() error { if _, found := os.LookupEnv("DBUS_SESSION_BUS_ADDRESS"); !found { sessionAddr := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "bus") if _, err := os.Stat(sessionAddr); err == nil { + sessionAddr, err = filepath.EvalSymlinks(sessionAddr) + if err != nil { + return err + } os.Setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path="+sessionAddr) } } diff --git a/vendor/github.com/containers/podman/v4/libpod/boltdb_state.go b/vendor/github.com/containers/podman/v4/libpod/boltdb_state.go index 77c23489..a31f34be 100644 --- a/vendor/github.com/containers/podman/v4/libpod/boltdb_state.go +++ b/vendor/github.com/containers/podman/v4/libpod/boltdb_state.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net" - "os" "strconv" "strings" "sync" @@ -57,10 +56,13 @@ type BoltState struct { // operations. // - execBkt: Map of exec session ID to container ID - used for resolving // exec session IDs to the containers that hold the exec session. -// - aliasesBkt - Contains a bucket for each CNI network, which contain a map of -// network alias (an extra name for containers in DNS) to the ID of the -// container holding the alias. Aliases must be unique per-network, and cannot -// conflict with names registered in nameRegistryBkt. +// - networksBkt: Contains all network names as key with their options json +// encoded as value. +// - aliasesBkt - Deprecated, use the networksBkt. Used to contain a bucket +// for each CNI network which contain a map of network alias (an extra name +// for containers in DNS) to the ID of the container holding the alias. +// Aliases must be unique per-network, and cannot conflict with names +// registered in nameRegistryBkt. // - runtimeConfigBkt: Contains configuration of the libpod instance that // initially created the database. This must match for any further instances // that access the database, to ensure that state mismatches with @@ -596,7 +598,7 @@ func (s *BoltState) Container(id string) (*Container, error) { return err } - return s.getContainerFromDB(ctrID, ctr, ctrBucket) + return s.getContainerFromDB(ctrID, ctr, ctrBucket, false) }) if err != nil { return nil, err @@ -701,7 +703,7 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) { return err } - return s.getContainerFromDB(id, ctr, ctrBucket) + return s.getContainerFromDB(id, ctr, ctrBucket, false) }) if err != nil { return nil, err @@ -813,9 +815,6 @@ func (s *BoltState) UpdateContainer(ctr *Container) error { return fmt.Errorf("container %s is in namespace %q, does not match our namespace %q: %w", ctr.ID(), ctr.config.Namespace, s.namespace, define.ErrNSMismatch) } - newState := new(ContainerState) - netNSPath := "" - ctrID := []byte(ctr.ID()) db, err := s.getDBCon() @@ -824,51 +823,13 @@ func (s *BoltState) UpdateContainer(ctr *Container) error { } defer s.deferredCloseDBCon(db) - err = db.View(func(tx *bolt.Tx) error { + return db.View(func(tx *bolt.Tx) error { ctrBucket, err := getCtrBucket(tx) if err != nil { return err } - - ctrToUpdate := ctrBucket.Bucket(ctrID) - if ctrToUpdate == nil { - ctr.valid = false - return fmt.Errorf("container %s does not exist in database: %w", ctr.ID(), define.ErrNoSuchCtr) - } - - newStateBytes := ctrToUpdate.Get(stateKey) - if newStateBytes == nil { - return fmt.Errorf("container %s does not have a state key in DB: %w", ctr.ID(), define.ErrInternal) - } - - if err := json.Unmarshal(newStateBytes, newState); err != nil { - return fmt.Errorf("unmarshalling container %s state: %w", ctr.ID(), err) - } - - netNSBytes := ctrToUpdate.Get(netNSKey) - if netNSBytes != nil { - netNSPath = string(netNSBytes) - } - - return nil + return s.getContainerStateDB(ctrID, ctr, ctrBucket) }) - if err != nil { - return err - } - - // Handle network namespace. - if os.Geteuid() == 0 { - // Do it only when root, either on the host or as root in the - // user namespace. - if err := replaceNetNS(netNSPath, ctr, newState); err != nil { - return err - } - } - - // New state compiled successfully, swap it into the current state - ctr.state = newState - - return nil } // SaveContainer saves a container's current state in the database @@ -889,7 +850,7 @@ func (s *BoltState) SaveContainer(ctr *Container) error { if err != nil { return fmt.Errorf("marshalling container %s state to JSON: %w", ctr.ID(), err) } - netNSPath := getNetNSPath(ctr) + netNSPath := ctr.state.NetNS ctrID := []byte(ctr.ID()) @@ -916,11 +877,7 @@ func (s *BoltState) SaveContainer(ctr *Container) error { return fmt.Errorf("updating container %s state in DB: %w", ctr.ID(), err) } - if netNSPath != "" { - if err := ctrToSave.Put(netNSKey, []byte(netNSPath)); err != nil { - return fmt.Errorf("updating network namespace path for container %s in DB: %w", ctr.ID(), err) - } - } else { + if netNSPath == "" { // Delete the existing network namespace if err := ctrToSave.Delete(netNSKey); err != nil { return fmt.Errorf("removing network namespace path for container %s in DB: %w", ctr.ID(), err) @@ -993,7 +950,8 @@ func (s *BoltState) ContainerInUse(ctr *Container) ([]string, error) { } // AllContainers retrieves all the containers in the database -func (s *BoltState) AllContainers() ([]*Container, error) { +// If `loadState` is set, the containers' state will be loaded as well. +func (s *BoltState) AllContainers(loadState bool) ([]*Container, error) { if !s.valid { return nil, define.ErrDBClosed } @@ -1030,7 +988,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) { ctr.config = new(ContainerConfig) ctr.state = new(ContainerState) - if err := s.getContainerFromDB(id, ctr, ctrBucket); err != nil { + if err := s.getContainerFromDB(id, ctr, ctrBucket, loadState); err != nil { // If the error is a namespace mismatch, we can // ignore it safely. // We just won't include the container in the @@ -1056,7 +1014,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) { return ctrs, nil } -// GetNetworks returns the CNI networks this container is a part of. +// GetNetworks returns the networks this container is a part of. func (s *BoltState) GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error) { if !s.valid { return nil, define.ErrDBClosed @@ -1232,6 +1190,16 @@ func (s *BoltState) GetNetworks(ctr *Container) (map[string]types.PerNetworkOpti // NetworkConnect adds the given container to the given network. If aliases are // specified, those will be added to the given network. func (s *BoltState) NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error { + return s.networkModify(ctr, network, opts, true) +} + +// NetworkModify will allow you to set new options on an existing connected network +func (s *BoltState) NetworkModify(ctr *Container, network string, opts types.PerNetworkOptions) error { + return s.networkModify(ctr, network, opts, false) +} + +// networkModify allows you to modify or add a new network, to add a new network use the new bool +func (s *BoltState) networkModify(ctr *Container, network string, opts types.PerNetworkOptions, new bool) error { if !s.valid { return define.ErrDBClosed } @@ -1278,11 +1246,14 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, opts types.Pe return fmt.Errorf("container %s does not have a network bucket: %w", ctr.ID(), define.ErrNoSuchNetwork) } netConnected := ctrNetworksBkt.Get([]byte(network)) - if netConnected != nil { + + if new && netConnected != nil { return fmt.Errorf("container %s is already connected to network %q: %w", ctr.ID(), network, define.ErrNetworkConnected) + } else if !new && netConnected == nil { + return fmt.Errorf("container %s is not connected to network %q: %w", ctr.ID(), network, define.ErrNoSuchNetwork) } - // Add the network + // Modify/Add the network if err := ctrNetworksBkt.Put([]byte(network), optBytes); err != nil { return fmt.Errorf("adding container %s to network %s in DB: %w", ctr.ID(), network, err) } @@ -1333,11 +1304,11 @@ func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error { ctrAliasesBkt := dbCtr.Bucket(aliasesBkt) ctrNetworksBkt := dbCtr.Bucket(networksBkt) if ctrNetworksBkt == nil { - return fmt.Errorf("container %s is not connected to any CNI networks, so cannot disconnect: %w", ctr.ID(), define.ErrNoSuchNetwork) + return fmt.Errorf("container %s is not connected to any networks, so cannot disconnect: %w", ctr.ID(), define.ErrNoSuchNetwork) } netConnected := ctrNetworksBkt.Get([]byte(network)) if netConnected == nil { - return fmt.Errorf("container %s is not connected to CNI network %q: %w", ctr.ID(), network, define.ErrNoSuchNetwork) + return fmt.Errorf("container %s is not connected to network %q: %w", ctr.ID(), network, define.ErrNoSuchNetwork) } if err := ctrNetworksBkt.Delete([]byte(network)); err != nil { @@ -2493,7 +2464,7 @@ func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) { newCtr.state = new(ContainerState) ctrs = append(ctrs, newCtr) - return s.getContainerFromDB(id, newCtr, ctrBkt) + return s.getContainerFromDB(id, newCtr, ctrBkt, false) }) if err != nil { return err @@ -3451,7 +3422,7 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { return fmt.Errorf("pod %s is in namespace %q but we are in namespace %q: %w", pod.ID(), pod.config.Namespace, s.namespace, define.ErrNSMismatch) } if s.namespace != ctr.config.Namespace { - return fmt.Errorf("container %s in in namespace %q but we are in namespace %q: %w", ctr.ID(), ctr.config.Namespace, s.namespace, define.ErrNSMismatch) + return fmt.Errorf("container %s in namespace %q but we are in namespace %q: %w", ctr.ID(), ctr.config.Namespace, s.namespace, define.ErrNSMismatch) } } diff --git a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_freebsd.go b/vendor/github.com/containers/podman/v4/libpod/boltdb_state_freebsd.go deleted file mode 100644 index d0a2d4f2..00000000 --- a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_freebsd.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build freebsd -// +build freebsd - -package libpod - -// replaceNetNS handle network namespace transitions after updating a -// container's state. -func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error { - if netNSPath != "" { - // On FreeBSD, we just record the network jail's name in our state. - newState.NetNS = &jailNetNS{Name: netNSPath} - } else { - newState.NetNS = nil - } - return nil -} - -// getNetNSPath retrieves the netns path to be stored in the database -func getNetNSPath(ctr *Container) string { - if ctr.state.NetNS != nil { - return ctr.state.NetNS.Name - } else { - return "" - } -} diff --git a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_internal.go b/vendor/github.com/containers/podman/v4/libpod/boltdb_state_internal.go index 7f2d49b3..044ea2f4 100644 --- a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_internal.go +++ b/vendor/github.com/containers/podman/v4/libpod/boltdb_state_internal.go @@ -428,11 +428,45 @@ func (s *BoltState) getContainerConfigFromDB(id []byte, config *ContainerConfig, return nil } -func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error { +func (s *BoltState) getContainerStateDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error { + newState := new(ContainerState) + ctrToUpdate := ctrsBkt.Bucket(id) + if ctrToUpdate == nil { + ctr.valid = false + return fmt.Errorf("container %s does not exist in database: %w", ctr.ID(), define.ErrNoSuchCtr) + } + + newStateBytes := ctrToUpdate.Get(stateKey) + if newStateBytes == nil { + return fmt.Errorf("container %s does not have a state key in DB: %w", ctr.ID(), define.ErrInternal) + } + + if err := json.Unmarshal(newStateBytes, newState); err != nil { + return fmt.Errorf("unmarshalling container %s state: %w", ctr.ID(), err) + } + + // backwards compat, previously we used a extra bucket for the netns so try to get it from there + netNSBytes := ctrToUpdate.Get(netNSKey) + if netNSBytes != nil && newState.NetNS == "" { + newState.NetNS = string(netNSBytes) + } + + // New state compiled successfully, swap it into the current state + ctr.state = newState + return nil +} + +func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket, loadState bool) error { if err := s.getContainerConfigFromDB(id, ctr.config, ctrsBkt); err != nil { return err } + if loadState { + if err := s.getContainerStateDB(id, ctr, ctrsBkt); err != nil { + return err + } + } + // Get the lock lock, err := s.runtime.lockManager.RetrieveLock(ctr.config.LockID) if err != nil { @@ -590,7 +624,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { if err != nil { return fmt.Errorf("marshalling container %s state to JSON: %w", ctr.ID(), err) } - netNSPath := getNetNSPath(ctr) dependsCtrs := ctr.Dependencies() ctrID := []byte(ctr.ID()) @@ -741,11 +774,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { return fmt.Errorf("adding container %s pod to DB: %w", ctr.ID(), err) } } - if netNSPath != "" { - if err := newCtrBkt.Put(netNSKey, []byte(netNSPath)); err != nil { - return fmt.Errorf("adding container %s netns path to DB: %w", ctr.ID(), err) - } - } if len(networks) > 0 { ctrNetworksBkt, err := newCtrBkt.CreateBucket(networksBkt) if err != nil { diff --git a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_linux.go b/vendor/github.com/containers/podman/v4/libpod/boltdb_state_linux.go deleted file mode 100644 index 3d9f5b6a..00000000 --- a/vendor/github.com/containers/podman/v4/libpod/boltdb_state_linux.go +++ /dev/null @@ -1,57 +0,0 @@ -//go:build linux -// +build linux - -package libpod - -import ( - "fmt" - - "github.com/containers/podman/v4/libpod/define" - "github.com/sirupsen/logrus" -) - -// replaceNetNS handle network namespace transitions after updating a -// container's state. -func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error { - if netNSPath != "" { - // Check if the container's old state has a good netns - if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() { - newState.NetNS = ctr.state.NetNS - } else { - // Close the existing namespace. - // Whoever removed it from the database already tore it down. - if err := ctr.runtime.closeNetNS(ctr); err != nil { - return err - } - - // Open the new network namespace - ns, err := joinNetNS(netNSPath) - if err == nil { - newState.NetNS = ns - } else { - if ctr.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { - return fmt.Errorf("joining network namespace of container %s: %w", ctr.ID(), err) - } - - logrus.Errorf("Joining network namespace for container %s: %v", ctr.ID(), err) - ctr.state.NetNS = nil - } - } - } else { - // The container no longer has a network namespace - // Close the old one, whoever removed it from the DB should have - // cleaned it up already. - if err := ctr.runtime.closeNetNS(ctr); err != nil { - return err - } - } - return nil -} - -// getNetNSPath retrieves the netns path to be stored in the database -func getNetNSPath(ctr *Container) string { - if ctr.state.NetNS != nil { - return ctr.state.NetNS.Path() - } - return "" -} diff --git a/vendor/github.com/containers/podman/v4/libpod/container.go b/vendor/github.com/containers/podman/v4/libpod/container.go index 560f9ab9..b92255d7 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container.go +++ b/vendor/github.com/containers/podman/v4/libpod/container.go @@ -2,6 +2,7 @@ package libpod import ( "bytes" + "errors" "fmt" "io" "net" @@ -20,6 +21,7 @@ import ( "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) // CgroupfsDefaultCgroupParent is the cgroup parent for CgroupFS in libpod @@ -150,6 +152,8 @@ type ContainerState struct { ExitCode int32 `json:"exitCode,omitempty"` // Exited is whether the container has exited Exited bool `json:"exited,omitempty"` + // Error holds the last known error message during start, stop, or remove + Error string `json:"error,omitempty"` // OOMKilled indicates that the container was killed as it ran out of // memory OOMKilled bool `json:"oomKilled,omitempty"` @@ -167,6 +171,8 @@ type ContainerState struct { // Podman. // These are DEPRECATED and will be removed in a future release. LegacyExecSessions map[string]*legacyExecSession `json:"execSessions,omitempty"` + // NetNS is the path or name of the NetNS + NetNS string `json:"netns,omitempty"` // NetworkStatusOld contains the configuration results for all networks // the pod is attached to. Only populated if we created a network // namespace for the container, and the network namespace is currently @@ -198,6 +204,18 @@ type ContainerState struct { // (only by restart policy). RestartCount uint `json:"restartCount,omitempty"` + // StartupHCPassed indicates that the startup healthcheck has + // succeeded and the main healthcheck can begin. + StartupHCPassed bool `json:"startupHCPassed,omitempty"` + // StartupHCSuccessCount indicates the number of successes of the + // startup healthcheck. A startup HC can require more than one success + // to be marked as passed. + StartupHCSuccessCount int `json:"startupHCSuccessCount,omitempty"` + // StartupHCFailureCount indicates the number of failures of the startup + // healthcheck. The container will be restarted if this exceed a set + // number in the startup HC config. + StartupHCFailureCount int `json:"startupHCFailureCount,omitempty"` + // ExtensionStageHooks holds hooks which will be executed by libpod // and not delegated to the OCI runtime. ExtensionStageHooks map[string][]spec.Hook `json:"extensionStageHooks,omitempty"` @@ -214,9 +232,6 @@ type ContainerState struct { // `podman-play-kube`. Service Service - // containerPlatformState holds platform-specific container state. - containerPlatformState - // Following checkpoint/restore related information is displayed // if the container has been checkpointed or restored. CheckpointedTime time.Time `json:"checkpointedTime,omitempty"` @@ -240,9 +255,11 @@ type ContainerNamedVolume struct { // IsAnonymous sets the named volume as anonymous even if it has a name // This is used for emptyDir volumes from a kube yaml IsAnonymous bool `json:"setAnonymous,omitempty"` + // SubPath determines which part of the Source will be mounted in the container + SubPath string } -// ContainerOverlayVolume is a overlay volume that will be mounted into the +// ContainerOverlayVolume is an overlay volume that will be mounted into the // container. Each volume is a libpod Volume present in the state. type ContainerOverlayVolume struct { // Destination is the absolute path where the mount will be placed in the container. @@ -426,6 +443,7 @@ func (c *Container) NamedVolumes() []*ContainerNamedVolume { newVol.Name = vol.Name newVol.Dest = vol.Dest newVol.Options = vol.Options + newVol.SubPath = vol.SubPath volumes = append(volumes, newVol) } @@ -688,6 +706,14 @@ func (c *Container) Terminal() bool { return false } +// LinuxResources return the containers Linux Resources (if any) +func (c *Container) LinuxResources() *spec.LinuxResources { + if c.config.Spec != nil && c.config.Spec.Linux != nil { + return c.config.Spec.Linux.Resources + } + return nil +} + // State Accessors // Require locking @@ -919,6 +945,20 @@ func (c *Container) StoppedByUser() (bool, error) { return c.state.StoppedByUser, nil } +// StartupHCPassed returns whether the container's startup healthcheck passed. +func (c *Container) StartupHCPassed() (bool, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return false, err + } + } + + return c.state.StartupHCPassed, nil +} + // Misc Accessors // Most will require locking @@ -1001,8 +1041,9 @@ func (c *Container) cGroupPath() (string, error) { lines, err := os.ReadFile(procPath) if err != nil { // If the file doesn't exist, it means the container could have been terminated - // so report it. - if os.IsNotExist(err) { + // so report it. Also check for ESRCH, which means the container could have been + // terminated after the file under /proc was opened but before it was read. + if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ESRCH) { return "", fmt.Errorf("cannot get cgroup path unless container %s is running: %w", c.ID(), define.ErrCtrStopped) } return "", err @@ -1211,12 +1252,7 @@ func (c *Container) Secrets() []*ContainerSecret { // Networks gets all the networks this container is connected to. // Please do NOT use ctr.config.Networks, as this can be changed from those // values at runtime via network connect and disconnect. -// If the container is configured to use CNI and this function returns an empty -// array, the container will still be connected to the default network. -// The second return parameter, a bool, indicates that the container container -// is joining the default CNI network - the network name will be included in the -// returned array of network names, but the container did not explicitly join -// this network. +// Returned array of network names or error. func (c *Container) Networks() ([]string, error) { if !c.batched { c.lock.Lock() diff --git a/vendor/github.com/containers/podman/v4/libpod/container_api.go b/vendor/github.com/containers/podman/v4/libpod/container_api.go index 35fed93e..ab798562 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_api.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_api.go @@ -82,7 +82,24 @@ func (c *Container) Init(ctx context.Context, recursive bool) error { // Start requites that all dependency containers (e.g. pod infra containers) be // running before being run. The recursive parameter, if set, will start all // dependencies before starting this container. -func (c *Container) Start(ctx context.Context, recursive bool) error { +func (c *Container) Start(ctx context.Context, recursive bool) (finalErr error) { + defer func() { + if finalErr != nil { + // Have to re-lock. + // As this is the first defer, it's the last thing to + // happen in the function - so `defer c.lock.Unlock()` + // below already fired. + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + } + + if err := saveContainerError(c, finalErr); err != nil { + logrus.Debug(err) + } + } + }() + if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -114,7 +131,24 @@ func (c *Container) Update(res *spec.LinuxResources) error { // Attach call occurs before Start). // In overall functionality, it is identical to the Start call, with the added // side effect that an attach session will also be started. -func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize, recursive bool) (<-chan error, error) { +func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan resize.TerminalSize, recursive bool) (retChan <-chan error, finalErr error) { + defer func() { + if finalErr != nil { + // Have to re-lock. + // As this is the first defer, it's the last thing to + // happen in the function - so `defer c.lock.Unlock()` + // below already fired. + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + } + + if err := saveContainerError(c, finalErr); err != nil { + logrus.Debug(err) + } + } + }() + if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -193,7 +227,24 @@ func (c *Container) Stop() error { // StopWithTimeout is a version of Stop that allows a timeout to be specified // manually. If timeout is 0, SIGKILL will be used immediately to kill the // container. -func (c *Container) StopWithTimeout(timeout uint) error { +func (c *Container) StopWithTimeout(timeout uint) (finalErr error) { + defer func() { + if finalErr != nil { + // Have to re-lock. + // As this is the first defer, it's the last thing to + // happen in the function - so `defer c.lock.Unlock()` + // below already fired. + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + } + + if err := saveContainerError(c, finalErr); err != nil { + logrus.Debug(err) + } + } + }() + if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -282,6 +333,8 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <- if c.Terminal() { go func() { <-attachRdy + c.lock.Lock() + defer c.lock.Unlock() if err := c.ociRuntime.KillContainer(c, uint(signal.SIGWINCH), false); err != nil { logrus.Warnf("Unable to send SIGWINCH to container %s after attach: %v", c.ID(), err) } @@ -460,7 +513,7 @@ func (c *Container) Unpause() error { // Export exports a container's root filesystem as a tar archive // The archive will be saved as a file at the given path -func (c *Container) Export(path string) error { +func (c *Container) Export(out io.Writer) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -475,7 +528,7 @@ func (c *Container) Export(path string) error { } defer c.newContainerEvent(events.Mount) - return c.export(path) + return c.export(out) } // AddArtifact creates and writes to an artifact file for the container @@ -733,6 +786,19 @@ func (c *Container) Cleanup(ctx context.Context) error { // If we didn't restart, we perform a normal cleanup + // make sure all the container processes are terminated if we are running without a pid namespace. + hasPidNs := false + for _, i := range c.config.Spec.Linux.Namespaces { + if i.Type == spec.PIDNamespace { + hasPidNs = true + break + } + } + if !hasPidNs { + // do not fail on errors + _ = c.ociRuntime.KillContainer(c, uint(unix.SIGKILL), true) + } + // Check for running exec sessions sessions, err := c.getActiveExecSessions() if err != nil { @@ -763,10 +829,6 @@ func (c *Container) Batch(batchFunc func(*Container) error) error { c.lock.Lock() defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return err - } - newCtr := new(Container) newCtr.config = c.config newCtr.state = c.state @@ -1035,3 +1097,8 @@ func (c *Container) Stat(ctx context.Context, containerPath string) (*define.Fil info, _, _, err := c.stat(mountPoint, containerPath) return info, err } + +func saveContainerError(c *Container, err error) error { + c.state.Error = err.Error() + return c.save() +} diff --git a/vendor/github.com/containers/podman/v4/libpod/container_commit.go b/vendor/github.com/containers/podman/v4/libpod/container_commit.go index f447816e..df0a77bc 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_commit.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_commit.go @@ -12,7 +12,6 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" - libpodutil "github.com/containers/podman/v4/pkg/util" "github.com/sirupsen/logrus" ) @@ -148,7 +147,7 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai importBuilder.SetWorkDir(c.config.Spec.Process.Cwd) // Process user changes - newImageConfig, err := libpodutil.GetImageConfig(options.Changes) + newImageConfig, err := libimage.ImageConfigFromChanges(options.Changes) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_config.go b/vendor/github.com/containers/podman/v4/libpod/container_config.go index f3585d22..3720e110 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_config.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_config.go @@ -24,7 +24,7 @@ type ContainerConfig struct { // in when the container is created, but it is not the final spec used // to run the container - it will be modified by Libpod to add things we // manage (e.g. bind mounts for /etc/resolv.conf, named volumes, a - // network namespace prepared by CNI or slirp4netns) in the + // network namespace prepared by the network backend) in the // generateSpec() function. Spec *spec.Spec `json:"spec"` @@ -395,6 +395,10 @@ type ContainerMiscConfig struct { HealthCheckConfig *manifest.Schema2HealthConfig `json:"healthcheck"` // HealthCheckOnFailureAction defines an action to take once the container turns unhealthy. HealthCheckOnFailureAction define.HealthCheckOnFailureAction `json:"healthcheck_on_failure_action"` + // StartupHealthCheckConfig is the configuration of the startup + // healthcheck for the container. This will run before the regular HC + // runs, and when it passes the regular HC will be activated. + StartupHealthCheckConfig *define.StartupHealthCheck `json:"startupHealthCheck,omitempty"` // PreserveFDs is a number of additional file descriptors (in addition // to 0, 1, 2) that will be passed to the executed process. The total FDs // passed will be 3 + PreserveFDs. diff --git a/vendor/github.com/containers/podman/v4/libpod/container_freebsd.go b/vendor/github.com/containers/podman/v4/libpod/container_freebsd.go index 87fb494d..cb0a465b 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_freebsd.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_freebsd.go @@ -3,29 +3,12 @@ package libpod -type containerPlatformState struct { - // NetNS is the name of the container's network VNET - // jail. Will only be set if config.CreateNetNS is true, or - // the container was told to join another container's network - // namespace. - NetNS *jailNetNS `json:"-"` -} - -type jailNetNS struct { - Name string `json:"-"` -} - -func (ns *jailNetNS) Path() string { - // The jail name approximately corresponds to the Linux netns path - return ns.Name -} - func networkDisabled(c *Container) (bool, error) { if c.config.CreateNetNS { return false, nil } if !c.config.PostConfigureNetNS { - return c.state.NetNS != nil, nil + return c.state.NetNS != "", nil } return false, nil } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_inspect.go b/vendor/github.com/containers/podman/v4/libpod/container_inspect.go index 4dc1ca3a..1e7dbcde 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_inspect.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_inspect.go @@ -129,7 +129,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver Pid: runtimeInfo.PID, ConmonPid: runtimeInfo.ConmonPID, ExitCode: runtimeInfo.ExitCode, - Error: "", // can't get yet + Error: runtimeInfo.Error, StartedAt: runtimeInfo.StartedTime, FinishedAt: runtimeInfo.FinishedTime, Checkpointed: runtimeInfo.Checkpointed, @@ -552,9 +552,9 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named hostConfig.NetworkMode = networkMode // Port bindings. - // Only populate if we're using CNI to configure the network. + // Only populate if we are creating the network namespace to configure the network. if c.config.CreateNetNS { - hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts) + hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings) } else { hostConfig.PortBindings = make(map[string][]define.InspectHostPort) } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_inspect_linux.go b/vendor/github.com/containers/podman/v4/libpod/container_inspect_linux.go index 355690d7..b6872bd0 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_inspect_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_inspect_linux.go @@ -139,15 +139,15 @@ func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostC if ctrSpec.Process != nil { // Max an O(1) lookup table for default bounding caps. boundingCaps := make(map[string]bool) - g, err := generate.New("linux") - if err != nil { - return err - } if !hostConfig.Privileged { - for _, cap := range g.Config.Process.Capabilities.Bounding { + for _, cap := range c.runtime.config.Containers.DefaultCapabilities { boundingCaps[cap] = true } } else { + g, err := generate.New("linux") + if err != nil { + return err + } // If we are privileged, use all caps. for _, cap := range capability.List() { if g.HostSpecific && cap > validate.LastCap() { @@ -156,7 +156,7 @@ func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostC boundingCaps[fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))] = true } } - // Iterate through spec caps. + // Iterate through default caps. // If it's not in default bounding caps, it was added. // If it is, delete from the default set. Whatever remains after // we finish are the dropped caps. @@ -183,13 +183,22 @@ func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostC // If there is none, it's ipc=host. // If there is one and it has a path, it's "ns:". // If no path, it's default - the empty string. + hostConfig.IpcMode = "host" for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == spec.IPCNamespace { if ns.Path != "" { hostConfig.IpcMode = fmt.Sprintf("ns:%s", ns.Path) } else { - break + switch { + case c.config.NoShm: + hostConfig.IpcMode = "none" + case c.config.NoShmShare: + hostConfig.IpcMode = "private" + default: + hostConfig.IpcMode = "shareable" + } } + break } } case c.config.NoShm: @@ -197,9 +206,6 @@ func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostC case c.config.NoShmShare: hostConfig.IpcMode = "private" } - if hostConfig.IpcMode == "" { - hostConfig.IpcMode = "shareable" - } // Cgroup namespace mode cgroupMode := "" diff --git a/vendor/github.com/containers/podman/v4/libpod/container_internal.go b/vendor/github.com/containers/podman/v4/libpod/container_internal.go index 13feb2ff..ee6a7b6c 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_internal.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_internal.go @@ -34,7 +34,7 @@ import ( "github.com/containers/podman/v4/pkg/systemd/notifyproxy" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" - "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/chrootarchive" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/mount" @@ -128,6 +128,9 @@ func (c *Container) rwSize() (int64, error) { // bundlePath returns the path to the container's root filesystem - where the OCI spec will be // placed, amongst other things func (c *Container) bundlePath() string { + if c.runtime.storageConfig.TransientStore { + return c.state.RunDir + } return c.config.StaticDir } @@ -622,6 +625,12 @@ func resetState(state *ContainerState) { state.CheckpointPath = "" state.CheckpointLog = "" state.RestoreLog = "" + state.StartupHCPassed = false + state.StartupHCSuccessCount = 0 + state.StartupHCFailureCount = 0 + state.NetNS = "" + state.NetworkStatus = nil + state.NetworkStatusOld = nil } // Refresh refreshes the container's state after a restart. @@ -739,7 +748,7 @@ func (c *Container) removeConmonFiles() error { return nil } -func (c *Container) export(path string) error { +func (c *Container) export(out io.Writer) error { mountPoint := c.state.Mountpoint if !c.state.Mounted { containerMount, err := c.runtime.store.Mount(c.ID(), c.config.MountLabel) @@ -754,18 +763,12 @@ func (c *Container) export(path string) error { }() } - input, err := archive.Tar(mountPoint, archive.Uncompressed) + input, err := chrootarchive.Tar(mountPoint, nil, mountPoint) if err != nil { return fmt.Errorf("reading container directory %q: %w", c.ID(), err) } - outFile, err := os.Create(path) - if err != nil { - return fmt.Errorf("creating file %q: %w", path, err) - } - defer outFile.Close() - - _, err = io.Copy(outFile, input) + _, err = io.Copy(out, input) return err } @@ -981,7 +984,7 @@ func (c *Container) completeNetworkSetup() error { return err } state := c.state - // collect any dns servers that cni tells us to use (dnsname) + // collect any dns servers that the network backend tells us to use for _, status := range c.getNetworkStatus() { for _, server := range status.DNSServerIPs { nameservers = append(nameservers, server.String()) @@ -1072,17 +1075,33 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { c.state.State = define.ContainerStateCreated c.state.StoppedByUser = false c.state.RestartPolicyMatch = false + c.state.StartupHCFailureCount = 0 + c.state.StartupHCSuccessCount = 0 + c.state.StartupHCPassed = false if !retainRetries { c.state.RestartCount = 0 } + // bugzilla.redhat.com/show_bug.cgi?id=2144754: + // In case of a restart, make sure to remove the healthcheck log to + // have a clean state. + if path := c.healthCheckLogPath(); path != "" { + if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Error(err) + } + } + if err := c.save(); err != nil { return err } if c.config.HealthCheckConfig != nil { - if err := c.createTimer(); err != nil { + timer := c.config.HealthCheckConfig.Interval.String() + if c.config.StartupHealthCheckConfig != nil { + timer = c.config.StartupHealthCheckConfig.Interval.String() + } + if err := c.createTimer(timer, c.config.StartupHealthCheckConfig != nil); err != nil { logrus.Error(err) } } @@ -1235,7 +1254,7 @@ func (c *Container) start() error { if err := c.updateHealthStatus(define.HealthCheckStarting); err != nil { logrus.Error(err) } - if err := c.startTimer(); err != nil { + if err := c.startTimer(c.config.StartupHealthCheckConfig != nil); err != nil { logrus.Error(err) } } @@ -1277,6 +1296,7 @@ func (c *Container) stop(timeout uint) error { // demonstrates nicely that a high stop timeout will block even simple // commands such as `podman ps` from progressing if the container lock // is held when busy-waiting for the container to be stopped. + c.state.StoppedByUser = true c.state.State = define.ContainerStateStopping if err := c.save(); err != nil { return fmt.Errorf("saving container %s state before stopping: %w", c.ID(), err) @@ -1324,7 +1344,6 @@ func (c *Container) stop(timeout uint) error { } c.newContainerEvent(events.Stop) - c.state.StoppedByUser = true return c.waitForConmonToExitAndSave() } @@ -1413,7 +1432,7 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (retEr return err } if c.config.HealthCheckConfig != nil { - if err := c.removeTransientFiles(context.Background()); err != nil { + if err := c.removeTransientFiles(context.Background(), c.config.StartupHealthCheckConfig != nil && !c.state.StartupHCPassed); err != nil { logrus.Error(err.Error()) } } @@ -1850,7 +1869,7 @@ func (c *Container) cleanup(ctx context.Context) error { // Remove healthcheck unit/timer file if it execs if c.config.HealthCheckConfig != nil { - if err := c.removeTransientFiles(ctx); err != nil { + if err := c.removeTransientFiles(ctx, c.config.StartupHealthCheckConfig != nil && !c.state.StartupHCPassed); err != nil { logrus.Errorf("Removing timer for container %s healthcheck: %v", c.ID(), err) } } @@ -1865,7 +1884,7 @@ func (c *Container) cleanup(ctx context.Context) error { if hoststFile, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { if _, err := os.Stat(hoststFile); err == nil { // we cannot use the dependency container lock due ABBA deadlocks - if lock, err := lockfile.GetLockfile(hoststFile); err == nil { + if lock, err := lockfile.GetLockFile(hoststFile); err == nil { lock.Lock() // make sure to ignore ENOENT error in case the netns container was cleaned up before this one if err := etchosts.Remove(hoststFile, getLocalhostHostEntry(c)); err != nil && !errors.Is(err, os.ErrNotExist) { @@ -2191,7 +2210,7 @@ func (c *Container) checkReadyForRemoval() error { return fmt.Errorf("container %s is in invalid state: %w", c.ID(), define.ErrCtrStateInvalid) } - if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) && !c.IsInfra() { + if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopping) && !c.IsInfra() { return fmt.Errorf("cannot remove container %s as it is %s - running or paused containers cannot be removed without force: %w", c.ID(), c.state.State.String(), define.ErrCtrStateInvalid) } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_internal_common.go b/vendor/github.com/containers/podman/v4/libpod/container_internal_common.go index 29107d4b..f603c51d 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_internal_common.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_internal_common.go @@ -19,7 +19,7 @@ import ( "time" metadata "github.com/checkpoint-restore/checkpointctl/lib" - "github.com/checkpoint-restore/go-criu/v5/stats" + "github.com/checkpoint-restore/go-criu/v6/stats" cdi "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" "github.com/containers/buildah" "github.com/containers/buildah/pkg/chrootuser" @@ -47,6 +47,7 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/lockfile" + stypes "github.com/containers/storage/types" securejoin "github.com/cyphar/filepath-securejoin" runcuser "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -56,6 +57,66 @@ import ( "github.com/sirupsen/logrus" ) +func parseOptionIDs(option string) ([]idtools.IDMap, error) { + ranges := strings.Split(option, "#") + ret := make([]idtools.IDMap, len(ranges)) + for i, m := range ranges { + var v idtools.IDMap + _, err := fmt.Sscanf(m, "%d-%d-%d", &v.ContainerID, &v.HostID, &v.Size) + if err != nil { + return nil, err + } + if v.ContainerID < 0 || v.HostID < 0 || v.Size < 1 { + return nil, fmt.Errorf("invalid value for %q", option) + } + ret[i] = v + } + return ret, nil +} + +func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string) ([]spec.LinuxIDMapping, []spec.LinuxIDMapping, error) { + uidMap := idMappings.UIDMap + gidMap := idMappings.GIDMap + if strings.HasPrefix(option, "idmap=") { + var err error + options := strings.Split(strings.SplitN(option, "=", 2)[1], ";") + for _, i := range options { + switch { + case strings.HasPrefix(i, "uids="): + uidMap, err = parseOptionIDs(strings.Replace(i, "uids=", "", 1)) + if err != nil { + return nil, nil, err + } + case strings.HasPrefix(i, "gids="): + gidMap, err = parseOptionIDs(strings.Replace(i, "gids=", "", 1)) + if err != nil { + return nil, nil, err + } + default: + return nil, nil, fmt.Errorf("unknown option %q", i) + } + } + } + + uidMappings := make([]spec.LinuxIDMapping, len(uidMap)) + gidMappings := make([]spec.LinuxIDMapping, len(gidMap)) + for i, uidmap := range uidMap { + uidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(uidmap.ContainerID), + ContainerID: uint32(uidmap.HostID), + Size: uint32(uidmap.Size), + } + } + for i, gidmap := range gidMap { + gidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(gidmap.ContainerID), + ContainerID: uint32(gidmap.HostID), + Size: uint32(gidmap.Size), + } + } + return uidMappings, gidMappings, nil +} + // Internal only function which returns upper and work dir from // overlay options. func getOverlayUpperAndWorkDir(options []string) (string, string, error) { @@ -157,6 +218,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, err } + if len(namedVol.SubPath) > 0 { + mountPoint = filepath.Join(mountPoint, namedVol.SubPath) + } + overlayFlag := false upperDir := "" workDir := "" @@ -213,13 +278,22 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } - // Check if the spec file mounts contain the options z, Z or U. + // Check if the spec file mounts contain the options z, Z, U or idmap. // If they have z or Z, relabel the source directory and then remove the option. // If they have U, chown the source directory and them remove the option. + // If they have idmap, then calculate the mappings to use in the OCI config file. for i := range g.Config.Mounts { m := &g.Config.Mounts[i] var options []string for _, o := range m.Options { + if o == "idmap" || strings.HasPrefix(o, "idmap=") { + var err error + m.UIDMappings, m.GIDMappings, err = parseIDMapMountOption(c.config.IDMappings, o) + if err != nil { + return nil, err + } + continue + } switch o { case "U": if m.Type == "tmpfs" { @@ -425,7 +499,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } g.SetRootPath(c.state.Mountpoint) - g.AddAnnotation(annotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano)) g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal)) if _, exists := g.Config.Annotations[annotations.ContainerManager]; !exists { @@ -1255,6 +1328,25 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti c.state.RestoreLog = path.Join(c.bundlePath(), "restore.log") c.state.CheckpointPath = c.CheckpointPath() + if options.IgnoreStaticIP || options.IgnoreStaticMAC { + networks, err := c.networks() + if err != nil { + return nil, 0, err + } + + for net, opts := range networks { + if options.IgnoreStaticIP { + opts.StaticIPs = nil + } + if options.IgnoreStaticMAC { + opts.StaticMAC = nil + } + if err := c.runtime.state.NetworkModify(c, net, opts); err != nil { + return nil, 0, fmt.Errorf("failed to rewrite network config: %w", err) + } + } + } + // Read network configuration from checkpoint var netStatus map[string]types.StatusBlock _, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile) @@ -1282,7 +1374,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti perNetOpts.StaticIPs = nil for name, netInt := range netStatus[network].Interfaces { perNetOpts.InterfaceName = name - if !options.IgnoreStaticIP { + if !options.IgnoreStaticMAC { perNetOpts.StaticMAC = netInt.MacAddress } if !options.IgnoreStaticIP { @@ -1685,7 +1777,7 @@ func (c *Container) makeBindMounts() error { hostsPath, exists := bindMounts[config.DefaultHostsFile] if !c.config.UseImageHosts && exists { // we cannot use the dependency container lock due ABBA deadlocks in cleanup() - lock, err := lockfile.GetLockfile(hostsPath) + lock, err := lockfile.GetLockFile(hostsPath) if err != nil { return fmt.Errorf("failed to lock hosts file: %w", err) } @@ -1901,24 +1993,35 @@ func (c *Container) generateResolvConf() error { return err } + networkBackend := c.runtime.config.Network.NetworkBackend nameservers := make([]string, 0, len(c.runtime.config.Containers.DNSServers)+len(c.config.DNSServer)) - nameservers = append(nameservers, c.runtime.config.Containers.DNSServers...) - for _, ip := range c.config.DNSServer { - nameservers = append(nameservers, ip.String()) + + // If NetworkBackend is `netavark` do not populate `/etc/resolv.conf` + // with custom dns server since after https://github.com/containers/netavark/pull/452 + // netavark will always set required `nameservers` in statsBlock and libpod + // will correctly populate `networkNameServers`. Also see https://github.com/containers/podman/issues/16172 + + // Exception: Populate `/etc/resolv.conf` if container is not connected to any network + // ( i.e len(netStatus)==0 ) since in such case netavark is not invoked at all. + if networkBackend != string(types.Netavark) || len(netStatus) == 0 { + nameservers = append(nameservers, c.runtime.config.Containers.DNSServers...) + for _, ip := range c.config.DNSServer { + nameservers = append(nameservers, ip.String()) + } } // If the user provided dns, it trumps all; then dns masq; then resolv.conf - var search []string keepHostServers := false if len(nameservers) == 0 { keepHostServers = true // first add the nameservers from the networks status nameservers = networkNameServers - // when we add network dns server we also have to add the search domains - search = networkSearchDomains // slirp4netns has a built in DNS forwarder. nameservers = c.addSlirp4netnsDNS(nameservers) } + // Set DNS search domains + search := networkSearchDomains + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 { customSearch := make([]string, 0, len(c.config.DNSSearch)+len(c.runtime.config.Containers.DNSSearches)) customSearch = append(customSearch, c.runtime.config.Containers.DNSSearches...) @@ -2362,6 +2465,10 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, error) { return entry, nil } + u, err := user.LookupId(fmt.Sprintf("%d", uid)) + if err == nil { + return fmt.Sprintf("%s:*:%d:%d:%s:%s:/bin/sh\n", u.Username, uid, gid, u.Name, c.WorkingDir()), nil + } return fmt.Sprintf("%d:*:%d:%d:container user:%s:/bin/sh\n", uid, uid, gid, c.WorkingDir()), nil } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_internal_freebsd.go b/vendor/github.com/containers/podman/v4/libpod/container_internal_freebsd.go index cae11cd2..995116a8 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_internal_freebsd.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_internal_freebsd.go @@ -36,7 +36,7 @@ func (c *Container) unmountSHM(path string) error { func (c *Container) prepare() error { var ( wg sync.WaitGroup - ctrNS *jailNetNS + ctrNS string networkStatus map[string]types.StatusBlock createNetNSErr, mountStorageErr error mountPoint string @@ -48,7 +48,7 @@ func (c *Container) prepare() error { go func() { defer wg.Done() // Set up network namespace if not already set up - noNetNS := c.state.NetNS == nil + noNetNS := c.state.NetNS == "" if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS { ctrNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c) if createNetNSErr != nil { @@ -85,6 +85,9 @@ func (c *Container) prepare() error { wg.Wait() var createErr error + if createNetNSErr != nil { + createErr = createNetNSErr + } if mountStorageErr != nil { if createErr != nil { logrus.Errorf("Preparing container %s: %v", c.ID(), createErr) @@ -92,7 +95,23 @@ func (c *Container) prepare() error { createErr = mountStorageErr } + // Only trigger storage cleanup if mountStorage was successful. + // Otherwise, we may mess up mount counters. if createErr != nil { + if mountStorageErr == nil { + if err := c.cleanupStorage(); err != nil { + // createErr is guaranteed non-nil, so print + // unconditionally + logrus.Errorf("Preparing container %s: %v", c.ID(), createErr) + createErr = fmt.Errorf("unmounting storage for container %s after network create failure: %w", c.ID(), err) + } + } + // It's OK to unconditionally trigger network cleanup. If the network + // isn't ready it will do nothing. + if err := c.cleanupNetwork(); err != nil { + logrus.Errorf("Preparing container %s: %v", c.ID(), createErr) + createErr = fmt.Errorf("cleaning up container %s network after setup failure: %w", c.ID(), err) + } return createErr } @@ -145,11 +164,13 @@ func (c *Container) reloadNetwork() error { // Add an existing container's network jail func (c *Container) addNetworkContainer(g *generate.Generator, ctr string) error { nsCtr, err := c.runtime.state.Container(ctr) - c.runtime.state.UpdateContainer(nsCtr) if err != nil { return fmt.Errorf("retrieving dependency %s of container %s from state: %w", ctr, c.ID(), err) } - g.AddAnnotation("org.freebsd.parentJail", nsCtr.state.NetNS.Name) + c.runtime.state.UpdateContainer(nsCtr) + if nsCtr.state.NetNS != "" { + g.AddAnnotation("org.freebsd.parentJail", nsCtr.state.NetNS) + } return nil } @@ -167,12 +188,20 @@ func (c *Container) getOCICgroupPath() (string, error) { func openDirectory(path string) (fd int, err error) { const O_PATH = 0x00400000 - return unix.Open(path, unix.O_RDONLY|O_PATH, 0) + return unix.Open(path, unix.O_RDONLY|O_PATH|unix.O_CLOEXEC, 0) } func (c *Container) addNetworkNamespace(g *generate.Generator) error { if c.config.CreateNetNS { - g.AddAnnotation("org.freebsd.parentJail", c.state.NetNS.Name) + if c.state.NetNS == "" { + // This should not happen since network setup + // errors should be propagated correctly from + // (*Runtime).createNetNS. Check for it anyway + // since it caused nil pointer dereferences in + // the past (see #16333). + return fmt.Errorf("Inconsistent state: c.config.CreateNetNS is set but c.state.NetNS is nil") + } + g.AddAnnotation("org.freebsd.parentJail", c.state.NetNS) } return nil } @@ -257,7 +286,7 @@ func (c *Container) isSlirp4netnsIPv6() (bool, error) { // check for net=none func (c *Container) hasNetNone() bool { - return c.state.NetNS == nil + return c.state.NetNS == "" } func setVolumeAtime(mountPoint string, st os.FileInfo) error { @@ -281,8 +310,8 @@ func (c *Container) getConmonPidFd() int { } func (c *Container) jailName() string { - if c.state.NetNS != nil { - return c.state.NetNS.Name + "." + c.ID() + if c.state.NetNS != "" { + return c.state.NetNS + "." + c.ID() } else { return c.ID() } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_internal_linux.go b/vendor/github.com/containers/podman/v4/libpod/container_internal_linux.go index 7161383a..b309b32c 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_internal_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_internal_linux.go @@ -14,7 +14,6 @@ import ( "syscall" "time" - "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" @@ -56,7 +55,7 @@ func (c *Container) unmountSHM(mount string) error { func (c *Container) prepare() error { var ( wg sync.WaitGroup - netNS ns.NetNS + netNS string networkStatus map[string]types.StatusBlock createNetNSErr, mountStorageErr error mountPoint string @@ -68,7 +67,7 @@ func (c *Container) prepare() error { go func() { defer wg.Done() // Set up network namespace if not already set up - noNetNS := c.state.NetNS == nil + noNetNS := c.state.NetNS == "" if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS { netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c) if createNetNSErr != nil { @@ -159,7 +158,7 @@ func (c *Container) cleanupNetwork() error { if netDisabled { return nil } - if c.state.NetNS == nil { + if c.state.NetNS == "" { logrus.Debugf("Network is already cleaned up, skipping...") return nil } @@ -169,7 +168,7 @@ func (c *Container) cleanupNetwork() error { logrus.Errorf("Unable to clean up network for container %s: %q", c.ID(), err) } - c.state.NetNS = nil + c.state.NetNS = "" c.state.NetworkStatus = nil c.state.NetworkStatusOld = nil @@ -411,7 +410,7 @@ func (c *Container) setupRootlessNetwork() error { // set up rootlesskit port forwarder again since it dies when conmon exits // we use rootlesskit port forwarder only as rootless and when bridge network is used if rootless.IsRootless() && c.config.NetMode.IsBridge() && len(c.config.PortMappings) > 0 { - err := c.runtime.setupRootlessPortMappingViaRLK(c, c.state.NetNS.Path(), c.state.NetworkStatus) + err := c.runtime.setupRootlessPortMappingViaRLK(c, c.state.NetNS, c.state.NetworkStatus) if err != nil { return err } @@ -430,7 +429,7 @@ func (c *Container) addNetworkNamespace(g *generate.Generator) error { return err } } else { - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), c.state.NetNS.Path()); err != nil { + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), c.state.NetNS); err != nil { return err } } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_linux.go b/vendor/github.com/containers/podman/v4/libpod/container_linux.go index 9c17a196..a1a64f5d 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_linux.go @@ -4,17 +4,9 @@ package libpod import ( - "github.com/containernetworking/plugins/pkg/ns" spec "github.com/opencontainers/runtime-spec/specs-go" ) -type containerPlatformState struct { - // NetNSPath is the path of the container's network namespace - // Will only be set if config.CreateNetNS is true, or the container was - // told to join another container's network namespace - NetNS ns.NetNS `json:"-"` -} - func networkDisabled(c *Container) (bool, error) { if c.config.CreateNetNS { return false, nil diff --git a/vendor/github.com/containers/podman/v4/libpod/container_log.go b/vendor/github.com/containers/podman/v4/libpod/container_log.go index c49b54eb..e33ede5d 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_log.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_log.go @@ -72,6 +72,14 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption } } } + go func() { + if options.Until.After(time.Now()) { + time.Sleep(time.Until(options.Until)) + if err := t.Stop(); err != nil { + logrus.Errorf("Stopping logger: %v", err) + } + } + }() go func() { defer options.WaitGroup.Done() diff --git a/vendor/github.com/containers/podman/v4/libpod/container_log_linux.go b/vendor/github.com/containers/podman/v4/libpod/container_log_linux.go index 7e95f244..de5a66de 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_log_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_log_linux.go @@ -7,12 +7,14 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "time" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/libpod/logs" + "github.com/containers/podman/v4/pkg/rootless" "github.com/coreos/go-systemd/v22/journal" "github.com/coreos/go-systemd/v22/sdjournal" "github.com/sirupsen/logrus" @@ -69,6 +71,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption if err := journal.AddMatch(match.String()); err != nil { return fmt.Errorf("adding filter to journald logger: %v: %w", match, err) } + // Make sure we only read events for the current user, while it is unlikely that there + // is a container ID duplication for two users, it is better to have it just in case. + uidMatch := sdjournal.Match{Field: "_UID", Value: strconv.Itoa(rootless.GetRootlessUID())} + if err := journal.AddMatch(uidMatch.String()); err != nil { + return fmt.Errorf("adding filter to journald logger: %v: %w", uidMatch, err) + } // Add the filter for logs. Note the disjunction so that we match // either the events or the logs. @@ -79,40 +87,31 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption if err := journal.AddMatch(match.String()); err != nil { return fmt.Errorf("adding filter to journald logger: %v: %w", match, err) } - - if err := journal.SeekHead(); err != nil { - return err - } - // API requires Next() immediately after SeekHead(). - if _, err := journal.Next(); err != nil { - return fmt.Errorf("next journal: %w", err) + if err := journal.AddMatch(uidMatch.String()); err != nil { + return fmt.Errorf("adding filter to journald logger: %v: %w", uidMatch, err) } - // API requires a next|prev before getting a cursor. - if _, err := journal.Previous(); err != nil { - return fmt.Errorf("previous journal: %w", err) - } - - // Note that the initial cursor may not yet be ready, so we'll do an - // exponential backoff. - var cursor string - var cursorError error - var containerCouldBeLogging bool - for i := 1; i <= 3; i++ { - cursor, cursorError = journal.GetCursor() - hundreds := 1 - for j := 1; j < i; j++ { - hundreds *= 2 + if options.Since.IsZero() { + if err := journal.SeekHead(); err != nil { + return err } - if cursorError != nil { - time.Sleep(time.Duration(hundreds*100) * time.Millisecond) - continue + } else { + // seek based on time which helps to reduce unnecessary event reads + if err := journal.SeekRealtimeUsec(uint64(options.Since.UnixMicro())); err != nil { + return err } - break } - if cursorError != nil { - return fmt.Errorf("initial journal cursor: %w", cursorError) + + c.lock.Lock() + if err := c.syncContainer(); err != nil { + c.lock.Unlock() + return err } + // The initial "containerCouldBeLogging" state must be correct, we cannot rely on the start event being still in the journal. + // This can happen if the journal was rotated after the container was started or when --since is used. + // https://github.com/containers/podman/issues/16950 + containerCouldBeLogging := c.ensureState(define.ContainerStateRunning, define.ContainerStateStopping) + c.lock.Unlock() options.WaitGroup.Add(1) go func() { @@ -142,57 +141,18 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption tailQueue = nil doTail = false } - lastReadCursor := "" for { - select { - case <-ctx.Done(): - // Remote client may have closed/lost the connection. - return - default: - // Fallthrough - } - - if lastReadCursor != "" { - // Advance to next entry if we read this one. - if _, err := journal.Next(); err != nil { - logrus.Errorf("Failed to move journal cursor to next entry: %v", err) - return - } - } - - // Fetch the location of this entry, presumably either - // the one that follows the last one we read, or that - // same last one, if there is no next entry (yet). - cursor, err = journal.GetCursor() + entry, err := events.GetNextEntry(ctx, journal, !doTail && options.Follow && containerCouldBeLogging, options.Until) if err != nil { - logrus.Errorf("Failed to get journal cursor: %v", err) + logrus.Errorf("Failed to get journal entry: %v", err) return } - - // Hit the end of the journal (so far?). - if cursor == lastReadCursor { + // entry nil == EOF in journal + if entry == nil { if doTail { doTailFunc() + continue } - // Unless we follow, quit. - if !options.Follow || !containerCouldBeLogging { - return - } - - // journal.Wait() is blocking, this would cause the goroutine to hang forever - // if no more journal entries are generated and thus if the client - // has closed the connection in the meantime to leak memory. - // Waiting only 5 seconds makes sure we can check if the client closed in the - // meantime at least every 5 seconds. - journal.Wait(5 * time.Second) - continue - } - lastReadCursor = cursor - - // Read the journal entry. - entry, err := journal.GetEntry() - if err != nil { - logrus.Errorf("Failed to get journal entry: %v", err) return } diff --git a/vendor/github.com/containers/podman/v4/libpod/container_validate.go b/vendor/github.com/containers/podman/v4/libpod/container_validate.go index 7224ec7d..caa633d4 100644 --- a/vendor/github.com/containers/podman/v4/libpod/container_validate.go +++ b/vendor/github.com/containers/podman/v4/libpod/container_validate.go @@ -82,7 +82,7 @@ func (c *Container) validate() error { return fmt.Errorf("cannot set static IP or MAC address if not creating a network namespace: %w", define.ErrInvalidArg) } - // Cannot set static IP or MAC if joining >1 CNI network. + // Cannot set static IP or MAC if joining >1 network. if len(c.config.Networks) > 1 && (c.config.StaticIP != nil || c.config.StaticMAC != nil) { return fmt.Errorf("cannot set static IP or MAC address if joining more than one network: %w", define.ErrInvalidArg) } @@ -131,7 +131,7 @@ func (c *Container) validate() error { return fmt.Errorf("please set User explicitly via WithUser() instead of in OCI spec directly: %w", define.ErrInvalidArg) } - // Init-ctrs must be used inside a Pod. Check if a init container type is + // Init-ctrs must be used inside a Pod. Check if an init container type is // passed and if no pod is passed if len(c.config.InitContainerType) > 0 && len(c.config.Pod) < 1 { return fmt.Errorf("init containers must be created in a pod: %w", define.ErrInvalidArg) @@ -156,6 +156,11 @@ func (c *Container) validate() error { } } + // Cannot set startup HC without a healthcheck + if c.config.HealthCheckConfig == nil && c.config.StartupHealthCheckConfig != nil { + return fmt.Errorf("cannot set a startup healthcheck when there is no regular healthcheck: %w", define.ErrInvalidArg) + } + return nil } diff --git a/vendor/github.com/containers/podman/v4/libpod/define/annotations.go b/vendor/github.com/containers/podman/v4/libpod/define/annotations.go index 580286d6..50bc5257 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/annotations.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/annotations.go @@ -13,7 +13,7 @@ const ( // InspectResponseFalse) it will be used in the output of Inspect(). InspectAnnotationAutoremove = "io.podman.annotations.autoremove" // InspectAnnotationVolumesFrom is used by Inspect to identify - // containers whose volumes are are being used by this container. + // containers whose volumes are being used by this container. // It is expected to be a comma-separated list of container names and/or // IDs. // If an annotation with this key is found in the OCI spec, it will be diff --git a/vendor/github.com/containers/podman/v4/libpod/define/config.go b/vendor/github.com/containers/podman/v4/libpod/define/config.go index 0427206e..7295f142 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/config.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/config.go @@ -3,7 +3,6 @@ package define import ( "bufio" "io" - "regexp" "github.com/containers/common/libnetwork/types" ) @@ -20,8 +19,6 @@ var ( NameRegex = types.NameRegex // RegexError is thrown in presence of an invalid container/pod name. RegexError = types.RegexError - // UmaskRegex is a regular expression to validate Umask. - UmaskRegex = regexp.MustCompile(`^[0-7]{1,4}$`) ) const ( diff --git a/vendor/github.com/containers/podman/v4/libpod/define/container_inspect.go b/vendor/github.com/containers/podman/v4/libpod/define/container_inspect.go index 690b255e..038d4971 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/container_inspect.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/container_inspect.go @@ -606,7 +606,7 @@ type InspectBasicNetworkConfig struct { AdditionalMacAddresses []string `json:"AdditionalMACAddresses,omitempty"` } -// InspectAdditionalNetwork holds information about non-default CNI networks the +// InspectAdditionalNetwork holds information about non-default networks the // container has been connected to. // As with InspectNetworkSettings, many fields are unused and maintained only // for compatibility with Docker. @@ -642,7 +642,7 @@ type InspectNetworkSettings struct { LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen"` Ports map[string][]InspectHostPort `json:"Ports"` SandboxKey string `json:"SandboxKey"` - // Networks contains information on non-default CNI networks this + // Networks contains information on non-default networks this // container has joined. // It is a map of network name to network information. Networks map[string]*InspectAdditionalNetwork `json:"Networks,omitempty"` diff --git a/vendor/github.com/containers/podman/v4/libpod/define/containerstate.go b/vendor/github.com/containers/podman/v4/libpod/define/containerstate.go index 00080ef3..56890339 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/containerstate.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/containerstate.go @@ -25,7 +25,7 @@ const ( ContainerStateStopped ContainerStatus = iota // ContainerStatePaused indicates that the container has been paused ContainerStatePaused ContainerStatus = iota - // ContainerStateExited indicates the the container has stopped and been + // ContainerStateExited indicates the container has stopped and been // cleaned up ContainerStateExited ContainerStatus = iota // ContainerStateRemoving indicates the container is in the process of diff --git a/vendor/github.com/containers/podman/v4/libpod/define/healthchecks.go b/vendor/github.com/containers/podman/v4/libpod/define/healthchecks.go index 274e0256..15ea79fc 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/healthchecks.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/healthchecks.go @@ -3,6 +3,8 @@ package define import ( "fmt" "strings" + + "github.com/containers/image/v5/manifest" ) const ( @@ -38,6 +40,9 @@ const ( HealthCheckInternalError HealthCheckStatus = iota // HealthCheckDefined means the healthcheck was found on the container HealthCheckDefined HealthCheckStatus = iota + // HealthCheckStartup means the healthcheck was unhealthy, but is still + // either within the startup HC or the startup period of the healthcheck + HealthCheckStartup HealthCheckStatus = iota ) // Healthcheck defaults. These are used both in the cli as well in @@ -131,3 +136,12 @@ func ParseHealthCheckOnFailureAction(s string) (HealthCheckOnFailureAction, erro return HealthCheckOnFailureActionInvalid, err } } + +// StartupHealthCheck is the configuration of a startup healthcheck. +type StartupHealthCheck struct { + manifest.Schema2HealthConfig + // Successes are the number of successes required to mark the startup HC + // as passed. + // If set to 0, a single success will mark the HC as passed. + Successes int `json:",omitempty"` +} diff --git a/vendor/github.com/containers/podman/v4/libpod/define/info.go b/vendor/github.com/containers/podman/v4/libpod/define/info.go index c716bec7..28260918 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/info.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/info.go @@ -118,6 +118,7 @@ type StoreInfo struct { ImageStore ImageStore `json:"imageStore"` RunRoot string `json:"runRoot"` VolumePath string `json:"volumePath"` + TransientStore bool `json:"transientStore"` } // ImageStore describes the image store. Right now only the number diff --git a/vendor/github.com/containers/podman/v4/libpod/define/pod_inspect.go b/vendor/github.com/containers/podman/v4/libpod/define/pod_inspect.go index d5607488..dc82af22 100644 --- a/vendor/github.com/containers/podman/v4/libpod/define/pod_inspect.go +++ b/vendor/github.com/containers/podman/v4/libpod/define/pod_inspect.go @@ -120,7 +120,7 @@ type InspectPodInfraConfig struct { // HostAdd adds a number of hosts to the infra container's resolv.conf // which will be shared with the rest of the pod. HostAdd []string - // Networks is a list of CNI networks the pod will join. + // Networks is a list of networks the pod will join. Networks []string // NetworkOptions are additional options for each network NetworkOptions map[string][]string diff --git a/vendor/github.com/containers/podman/v4/libpod/events.go b/vendor/github.com/containers/podman/v4/libpod/events.go index 31a857a7..0ce737e2 100644 --- a/vendor/github.com/containers/podman/v4/libpod/events.go +++ b/vendor/github.com/containers/podman/v4/libpod/events.go @@ -26,6 +26,14 @@ func (r *Runtime) newEventer() (events.Eventer, error) { // newContainerEvent creates a new event based on a container func (c *Container) newContainerEvent(status events.Status) { + if err := c.newContainerEventWithInspectData(status, false); err != nil { + logrus.Errorf("Unable to write container event: %v", err) + } +} + +// newContainerEventWithInspectData creates a new event and sets the +// ContainerInspectData field if inspectData is set. +func (c *Container) newContainerEventWithInspectData(status events.Status, inspectData bool) error { e := events.NewEvent(status) e.ID = c.ID() e.Name = c.Name() @@ -38,6 +46,24 @@ func (c *Container) newContainerEvent(status events.Status) { Attributes: c.Labels(), } + if inspectData { + err := func() error { + data, err := c.inspectLocked(true) + if err != nil { + return err + } + rawData, err := json.Marshal(data) + if err != nil { + return err + } + e.Details.ContainerInspectData = string(rawData) + return nil + }() + if err != nil { + return fmt.Errorf("adding inspect data to container-create event: %v", err) + } + } + // if the current event is a HealthStatus event, we need to get the current // status of the container to pass to the event if status == events.HealthStatus { @@ -48,9 +74,7 @@ func (c *Container) newContainerEvent(status events.Status) { e.HealthStatus = containerHealthStatus } - if err := c.runtime.eventer.Write(e); err != nil { - logrus.Errorf("Unable to write pod event: %q", err) - } + return c.runtime.eventer.Write(e) } // newContainerExitedEvent creates a new event for a container's death diff --git a/vendor/github.com/containers/podman/v4/libpod/events/config.go b/vendor/github.com/containers/podman/v4/libpod/events/config.go index 28bc87a3..058b219a 100644 --- a/vendor/github.com/containers/podman/v4/libpod/events/config.go +++ b/vendor/github.com/containers/podman/v4/libpod/events/config.go @@ -50,6 +50,10 @@ type Event struct { type Details struct { // ID is the event ID ID string + // ContainerInspectData includes the payload of the container's inspect + // data. Only set when events_container_create_inspect_data is set true + // in containers.conf. + ContainerInspectData string `json:",omitempty"` // PodID is the ID of the pod associated with the container. PodID string `json:",omitempty"` // Attributes can be used to describe specifics about the event diff --git a/vendor/github.com/containers/podman/v4/libpod/events/filters.go b/vendor/github.com/containers/podman/v4/libpod/events/filters.go index d5b96e7e..9057c9b7 100644 --- a/vendor/github.com/containers/podman/v4/libpod/events/filters.go +++ b/vendor/github.com/containers/podman/v4/libpod/events/filters.go @@ -21,6 +21,9 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error return strings.HasPrefix(e.ID, filterValue) }, nil case "EVENT", "STATUS": + if filterValue == "die" { // Docker compat + filterValue = "died" + } return func(e *Event) bool { return string(e.Status) == filterValue }, nil diff --git a/vendor/github.com/containers/podman/v4/libpod/events/journal_linux.go b/vendor/github.com/containers/podman/v4/libpod/events/journal_linux.go index e303a205..0f472b8d 100644 --- a/vendor/github.com/containers/podman/v4/libpod/events/journal_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/events/journal_linux.go @@ -11,6 +11,7 @@ import ( "strconv" "time" + "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" "github.com/coreos/go-systemd/v22/journal" "github.com/coreos/go-systemd/v22/sdjournal" @@ -63,6 +64,10 @@ func (e EventJournalD) Write(ee Event) error { m["PODMAN_LABELS"] = string(b) } m["PODMAN_HEALTH_STATUS"] = ee.HealthStatus + + if len(ee.Details.ContainerInspectData) > 0 { + m["PODMAN_CONTAINER_INSPECT_DATA"] = ee.Details.ContainerInspectData + } case Network: m["PODMAN_ID"] = ee.ID m["PODMAN_NETWORK_NAME"] = ee.Network @@ -97,10 +102,20 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { logrus.Errorf("Unable to close journal :%v", err) } }() + err = j.SetDataThreshold(0) + if err != nil { + logrus.Warnf("cannot set data threshold: %v", err) + } // match only podman journal entries podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} if err := j.AddMatch(podmanJournal.String()); err != nil { - return fmt.Errorf("failed to add journal filter for event log: %w", err) + return fmt.Errorf("failed to add SYSLOG_IDENTIFIER journal filter for event log: %w", err) + } + + // make sure we only read events for the current user + uidMatch := sdjournal.Match{Field: "_UID", Value: strconv.Itoa(rootless.GetRootlessUID())} + if err := j.AddMatch(uidMatch.String()); err != nil { + return fmt.Errorf("failed to add _UID journal filter for event log: %w", err) } if len(options.Since) == 0 && len(options.Until) == 0 && options.Stream { @@ -113,10 +128,19 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if _, err := j.Previous(); err != nil { return fmt.Errorf("failed to move journal cursor to previous entry: %w", err) } + } else if len(options.Since) > 0 { + since, err := util.ParseInputTime(options.Since, true) + if err != nil { + return err + } + // seek based on time which helps to reduce unnecessary event reads + if err := j.SeekRealtimeUsec(uint64(since.UnixMicro())); err != nil { + return err + } } for { - entry, err := getNextEntry(ctx, j, options.Stream, untilTime) + entry, err := GetNextEntry(ctx, j, options.Stream, untilTime) if err != nil { return err } @@ -187,6 +211,7 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { } } newEvent.HealthStatus = entry.Fields["PODMAN_HEALTH_STATUS"] + newEvent.Details.ContainerInspectData = entry.Fields["PODMAN_CONTAINER_INSPECT_DATA"] case Network: newEvent.ID = entry.Fields["PODMAN_ID"] newEvent.Network = entry.Fields["PODMAN_NETWORK_NAME"] @@ -201,10 +226,10 @@ func (e EventJournalD) String() string { return Journald.String() } -// getNextEntry returns the next entry in the journal. If the end of the +// GetNextEntry returns the next entry in the journal. If the end of the // journal is reached and stream is not set or the current time is after // the until time this function return nil,nil. -func getNextEntry(ctx context.Context, j *sdjournal.Journal, stream bool, untilTime time.Time) (*sdjournal.JournalEntry, error) { +func GetNextEntry(ctx context.Context, j *sdjournal.Journal, stream bool, untilTime time.Time) (*sdjournal.JournalEntry, error) { for { select { case <-ctx.Done(): diff --git a/vendor/github.com/containers/podman/v4/libpod/events/logfile.go b/vendor/github.com/containers/podman/v4/libpod/events/logfile.go index bb0f461e..b0872bf4 100644 --- a/vendor/github.com/containers/podman/v4/libpod/events/logfile.go +++ b/vendor/github.com/containers/podman/v4/libpod/events/logfile.go @@ -45,7 +45,7 @@ func newLogFileEventer(options EventerOptions) (*EventLogFile, error) { // Writes to the log file func (e EventLogFile) Write(ee Event) error { // We need to lock events file - lock, err := lockfile.GetLockfile(e.options.LogFilePath + ".lock") + lock, err := lockfile.GetLockFile(e.options.LogFilePath + ".lock") if err != nil { return err } diff --git a/vendor/github.com/containers/podman/v4/libpod/healthcheck.go b/vendor/github.com/containers/podman/v4/libpod/healthcheck.go index 1a81360f..63f22ef9 100644 --- a/vendor/github.com/containers/podman/v4/libpod/healthcheck.go +++ b/vendor/github.com/containers/podman/v4/libpod/healthcheck.go @@ -25,7 +25,7 @@ const ( // HealthCheck verifies the state and validity of the healthcheck configuration // on the container and then executes the healthcheck -func (r *Runtime) HealthCheck(name string) (define.HealthCheckStatus, error) { +func (r *Runtime) HealthCheck(ctx context.Context, name string) (define.HealthCheckStatus, error) { container, err := r.LookupContainer(name) if err != nil { return define.HealthCheckContainerNotFound, fmt.Errorf("unable to look up %s to perform a health check: %w", name, err) @@ -36,21 +36,35 @@ func (r *Runtime) HealthCheck(name string) (define.HealthCheckStatus, error) { return hcStatus, err } - hcStatus, logStatus, err := container.runHealthCheck() - if err := container.processHealthCheckStatus(logStatus); err != nil { - return hcStatus, err + isStartupHC := false + if container.config.StartupHealthCheckConfig != nil { + passed, err := container.StartupHCPassed() + if err != nil { + return define.HealthCheckInternalError, err + } + isStartupHC = !passed + } + + hcStatus, logStatus, err := container.runHealthCheck(ctx, isStartupHC) + if !isStartupHC { + if err := container.processHealthCheckStatus(logStatus); err != nil { + return hcStatus, err + } } return hcStatus, err } -// runHealthCheck runs the health check as defined by the container -func (c *Container) runHealthCheck() (define.HealthCheckStatus, string, error) { +func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define.HealthCheckStatus, string, error) { var ( newCommand []string returnCode int inStartPeriod bool ) hcCommand := c.HealthCheckConfig().Test + if isStartup { + logrus.Debugf("Running startup healthcheck for container %s", c.ID()) + hcCommand = c.config.StartupHealthCheckConfig.Test + } if len(hcCommand) < 1 { return define.HealthCheckNotDefined, "", fmt.Errorf("container %s has no defined healthcheck", c.ID()) } @@ -113,6 +127,18 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, string, error) { hcResult = define.HealthCheckFailure returnCode = 1 } + + // Handle startup HC + if isStartup { + inStartPeriod = true + if hcErr != nil || exitCode != 0 { + hcResult = define.HealthCheckStartup + c.incrementStartupHCFailureCounter(ctx) + } else { + c.incrementStartupHCSuccessCounter(ctx) + } + } + timeEnd := time.Now() if c.HealthCheckConfig().StartPeriod > 0 { // there is a start-period we need to honor; we add startPeriod to container start time @@ -188,6 +214,114 @@ func checkHealthCheckCanBeRun(c *Container) (define.HealthCheckStatus, error) { return define.HealthCheckDefined, nil } +// Increment the current startup healthcheck success counter. +// Can stop the startup HC and start the regular HC if the startup HC has enough +// consecutive successes. +func (c *Container) incrementStartupHCSuccessCounter(ctx context.Context) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + logrus.Errorf("Error syncing container %s state: %v", c.ID(), err) + return + } + } + + // We don't have a startup HC, can't do anything + if c.config.StartupHealthCheckConfig == nil { + return + } + + // Race: someone else got here first + if c.state.StartupHCPassed { + return + } + + // Increment the success counter + c.state.StartupHCSuccessCount++ + + logrus.Debugf("Startup healthcheck for container %s succeeded, success counter now %d", c.ID(), c.state.StartupHCSuccessCount) + + // Did we exceed threshold? + recreateTimer := false + if c.config.StartupHealthCheckConfig.Successes == 0 || c.state.StartupHCSuccessCount >= c.config.StartupHealthCheckConfig.Successes { + c.state.StartupHCPassed = true + c.state.StartupHCSuccessCount = 0 + c.state.StartupHCFailureCount = 0 + + recreateTimer = true + } + + if err := c.save(); err != nil { + logrus.Errorf("Error saving container %s state: %v", c.ID(), err) + return + } + + if recreateTimer { + logrus.Infof("Startup healthcheck for container %s passed, recreating timer", c.ID()) + + // Create the new, standard healthcheck timer first. + if err := c.createTimer(c.HealthCheckConfig().Interval.String(), false); err != nil { + logrus.Errorf("Error recreating container %s healthcheck: %v", c.ID(), err) + return + } + if err := c.startTimer(false); err != nil { + logrus.Errorf("Error restarting container %s healthcheck timer: %v", c.ID(), err) + } + + // This kills the process the healthcheck is running. + // Which happens to be us. + // So this has to be last - after this, systemd serves us a + // SIGTERM and we exit. + if err := c.removeTransientFiles(ctx, true); err != nil { + logrus.Errorf("Error removing container %s healthcheck: %v", c.ID(), err) + return + } + } +} + +// Increment the current startup healthcheck failure counter. +// Can restart the container if the HC fails enough times consecutively. +func (c *Container) incrementStartupHCFailureCounter(ctx context.Context) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + logrus.Errorf("Error syncing container %s state: %v", c.ID(), err) + return + } + } + + // We don't have a startup HC, can't do anything + if c.config.StartupHealthCheckConfig == nil { + return + } + + // Race: someone else got here first + if c.state.StartupHCPassed { + return + } + + c.state.StartupHCFailureCount++ + + logrus.Debugf("Startup healthcheck for container %s failed, failure counter now %d", c.ID(), c.state.StartupHCFailureCount) + + if c.config.StartupHealthCheckConfig.Retries != 0 && c.state.StartupHCFailureCount >= c.config.StartupHealthCheckConfig.Retries { + logrus.Infof("Restarting container %s as startup healthcheck failed", c.ID()) + // Restart the container + if err := c.restartWithTimeout(ctx, c.config.StopTimeout); err != nil { + logrus.Errorf("Error restarting container %s after healthcheck failure: %v", c.ID(), err) + } + return + } + + if err := c.save(); err != nil { + logrus.Errorf("Error saving container %s state: %v", c.ID(), err) + } +} + func newHealthCheckLog(start, end time.Time, exitCode int, log string) define.HealthCheckLog { return define.HealthCheckLog{ Start: start.Format(time.RFC3339Nano), @@ -273,10 +407,13 @@ func (c *Container) getHealthCheckLog() (define.HealthCheckResults, error) { return healthCheck, nil } -// HealthCheckStatus returns the current state of a container with a healthcheck +// HealthCheckStatus returns the current state of a container with a healthcheck. +// Returns an empty string if no health check is defined for the container. func (c *Container) HealthCheckStatus() (string, error) { - c.lock.Lock() - defer c.lock.Unlock() + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + } return c.healthCheckStatus() } @@ -284,7 +421,7 @@ func (c *Container) HealthCheckStatus() (string, error) { // This function does not lock the container. func (c *Container) healthCheckStatus() (string, error) { if !c.HasHealthCheck() { - return "", fmt.Errorf("container %s has no defined healthcheck", c.ID()) + return "", nil } if err := c.syncContainer(); err != nil { @@ -298,13 +435,3 @@ func (c *Container) healthCheckStatus() (string, error) { return results.Status, nil } - -func (c *Container) disableHealthCheckSystemd() bool { - if os.Getenv("DISABLE_HC_SYSTEMD") == "true" { - return true - } - if c.config.HealthCheckConfig.Interval == 0 { - return true - } - return false -} diff --git a/vendor/github.com/containers/podman/v4/libpod/healthcheck_linux.go b/vendor/github.com/containers/podman/v4/libpod/healthcheck_linux.go index 6948f976..a0502e57 100644 --- a/vendor/github.com/containers/podman/v4/libpod/healthcheck_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/healthcheck_linux.go @@ -1,3 +1,6 @@ +//go:build systemd +// +build systemd + package libpod import ( @@ -10,12 +13,13 @@ import ( "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/systemd" + "github.com/containers/podman/v4/utils" "github.com/sirupsen/logrus" ) // createTimer systemd timers for healthchecks of a container -func (c *Container) createTimer() error { - if c.disableHealthCheckSystemd() { +func (c *Container) createTimer(interval string, isStartup bool) error { + if c.disableHealthCheckSystemd(isStartup) { return nil } podman, err := os.Executable() @@ -31,7 +35,14 @@ func (c *Container) createTimer() error { if path != "" { cmd = append(cmd, "--setenv=PATH="+path) } - cmd = append(cmd, "--unit", c.ID(), fmt.Sprintf("--on-unit-inactive=%s", c.HealthCheckConfig().Interval.String()), "--timer-property=AccuracySec=1s", podman, "healthcheck", "run", c.ID()) + + cmd = append(cmd, "--unit", c.hcUnitName(isStartup), fmt.Sprintf("--on-unit-inactive=%s", interval), "--timer-property=AccuracySec=1s", podman) + + if logrus.IsLevelEnabled(logrus.DebugLevel) { + cmd = append(cmd, "--log-level=debug", "--syslog") + } + + cmd = append(cmd, "healthcheck", "run", c.ID()) conn, err := systemd.ConnectToDBUS() if err != nil { @@ -58,8 +69,8 @@ func systemdOpSuccessful(c chan string) error { } // startTimer starts a systemd timer for the healthchecks -func (c *Container) startTimer() error { - if c.disableHealthCheckSystemd() { +func (c *Container) startTimer(isStartup bool) error { + if c.disableHealthCheckSystemd(isStartup) { return nil } conn, err := systemd.ConnectToDBUS() @@ -68,7 +79,7 @@ func (c *Container) startTimer() error { } defer conn.Close() - startFile := fmt.Sprintf("%s.service", c.ID()) + startFile := fmt.Sprintf("%s.service", c.hcUnitName(isStartup)) startChan := make(chan string) if _, err := conn.RestartUnitContext(context.Background(), startFile, "fail", startChan); err != nil { return err @@ -82,8 +93,8 @@ func (c *Container) startTimer() error { // removeTransientFiles removes the systemd timer and unit files // for the container -func (c *Container) removeTransientFiles(ctx context.Context) error { - if c.disableHealthCheckSystemd() { +func (c *Container) removeTransientFiles(ctx context.Context, isStartup bool) error { + if c.disableHealthCheckSystemd(isStartup) { return nil } conn, err := systemd.ConnectToDBUS() @@ -99,8 +110,8 @@ func (c *Container) removeTransientFiles(ctx context.Context) error { // Stop the timer before the service to make sure the timer does not // fire after the service is stopped. timerChan := make(chan string) - timerFile := fmt.Sprintf("%s.timer", c.ID()) - if _, err := conn.StopUnitContext(ctx, timerFile, "fail", timerChan); err != nil { + timerFile := fmt.Sprintf("%s.timer", c.hcUnitName(isStartup)) + if _, err := conn.StopUnitContext(ctx, timerFile, "ignore-dependencies", timerChan); err != nil { if !strings.HasSuffix(err.Error(), ".timer not loaded.") { stopErrors = append(stopErrors, fmt.Errorf("removing health-check timer %q: %w", timerFile, err)) } @@ -111,11 +122,11 @@ func (c *Container) removeTransientFiles(ctx context.Context) error { // Reset the service before stopping it to make sure it's being removed // on stop. serviceChan := make(chan string) - serviceFile := fmt.Sprintf("%s.service", c.ID()) + serviceFile := fmt.Sprintf("%s.service", c.hcUnitName(isStartup)) if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil { logrus.Debugf("Failed to reset unit file: %q", err) } - if _, err := conn.StopUnitContext(ctx, serviceFile, "fail", serviceChan); err != nil { + if _, err := conn.StopUnitContext(ctx, serviceFile, "ignore-dependencies", serviceChan); err != nil { if !strings.HasSuffix(err.Error(), ".service not loaded.") { stopErrors = append(stopErrors, fmt.Errorf("removing health-check service %q: %w", serviceFile, err)) } @@ -125,3 +136,27 @@ func (c *Container) removeTransientFiles(ctx context.Context) error { return errorhandling.JoinErrors(stopErrors) } + +func (c *Container) disableHealthCheckSystemd(isStartup bool) bool { + if !utils.RunsOnSystemd() || os.Getenv("DISABLE_HC_SYSTEMD") == "true" { + return true + } + if isStartup { + if c.config.StartupHealthCheckConfig.Interval == 0 { + return true + } + } + if c.config.HealthCheckConfig.Interval == 0 { + return true + } + return false +} + +// Systemd unit name for the healthcheck systemd unit +func (c *Container) hcUnitName(isStartup bool) string { + unitName := c.ID() + if isStartup { + unitName += "-startup" + } + return unitName +} diff --git a/vendor/github.com/containers/podman/v4/libpod/healthcheck_nosystemd_linux.go b/vendor/github.com/containers/podman/v4/libpod/healthcheck_nosystemd_linux.go new file mode 100644 index 00000000..2e3b15dc --- /dev/null +++ b/vendor/github.com/containers/podman/v4/libpod/healthcheck_nosystemd_linux.go @@ -0,0 +1,24 @@ +//go:build !systemd +// +build !systemd + +package libpod + +import ( + "context" +) + +// createTimer systemd timers for healthchecks of a container +func (c *Container) createTimer(interval string, isStartup bool) error { + return nil +} + +// startTimer starts a systemd timer for the healthchecks +func (c *Container) startTimer(isStartup bool) error { + return nil +} + +// removeTransientFiles removes the systemd timer and unit files +// for the container +func (c *Container) removeTransientFiles(ctx context.Context, isStartup bool) error { + return nil +} diff --git a/vendor/github.com/containers/podman/v4/libpod/healthcheck_unsupported.go b/vendor/github.com/containers/podman/v4/libpod/healthcheck_unsupported.go index 92cd5d0a..e8a8b77d 100644 --- a/vendor/github.com/containers/podman/v4/libpod/healthcheck_unsupported.go +++ b/vendor/github.com/containers/podman/v4/libpod/healthcheck_unsupported.go @@ -9,17 +9,17 @@ import ( ) // createTimer systemd timers for healthchecks of a container -func (c *Container) createTimer() error { +func (c *Container) createTimer(interval string, isStartup bool) error { return errors.New("not implemented (*Container) createTimer") } // startTimer starts a systemd timer for the healthchecks -func (c *Container) startTimer() error { +func (c *Container) startTimer(isStartup bool) error { return errors.New("not implemented (*Container) startTimer") } // removeTransientFiles removes the systemd timer and unit files // for the container -func (c *Container) removeTransientFiles(ctx context.Context) error { +func (c *Container) removeTransientFiles(ctx context.Context, isStartup bool) error { return errors.New("not implemented (*Container) removeTransientFiles") } diff --git a/vendor/github.com/containers/podman/v4/libpod/info.go b/vendor/github.com/containers/podman/v4/libpod/info.go index ad8f6543..953ca053 100644 --- a/vendor/github.com/containers/podman/v4/libpod/info.go +++ b/vendor/github.com/containers/podman/v4/libpod/info.go @@ -226,6 +226,7 @@ func (r *Runtime) storeInfo() (*define.StoreInfo, error) { GraphOptions: nil, VolumePath: r.config.Engine.VolumePath, ConfigFile: configFile, + TransientStore: r.store.TransientStore(), } graphOptions := map[string]interface{}{} diff --git a/vendor/github.com/containers/podman/v4/libpod/kube.go b/vendor/github.com/containers/podman/v4/libpod/kube.go index b8047a84..0552973f 100644 --- a/vendor/github.com/containers/podman/v4/libpod/kube.go +++ b/vendor/github.com/containers/podman/v4/libpod/kube.go @@ -17,6 +17,7 @@ import ( "github.com/containers/common/pkg/config" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/annotations" "github.com/containers/podman/v4/pkg/env" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/api/resource" @@ -27,7 +28,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" ) @@ -366,6 +366,9 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po for _, ctr := range containers { if !ctr.IsInfra() { for k, v := range ctr.config.Spec.Annotations { + if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) { + continue + } podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) } // Convert auto-update labels into kube annotations @@ -435,7 +438,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po } } } - podVolumes := make([]v1.Volume, 0, len(deDupPodVolumes)) + podVolumes := []v1.Volume{} for _, vol := range deDupPodVolumes { podVolumes = append(podVolumes, *vol) } @@ -471,18 +474,12 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta CreationTimestamp: v12.Now(), Annotations: annotations, } - // Set enableServiceLinks to false as podman doesn't use the service port environment variables - enableServiceLinks := false - // Set automountServiceAccountToken to false as podman doesn't use service account tokens - automountServiceAccountToken := false ps := v1.PodSpec{ - Containers: containers, - Hostname: hostname, - HostNetwork: hostNetwork, - InitContainers: initCtrs, - Volumes: volumes, - EnableServiceLinks: &enableServiceLinks, - AutomountServiceAccountToken: &automountServiceAccountToken, + Containers: containers, + Hostname: hostname, + HostNetwork: hostNetwork, + InitContainers: initCtrs, + Volumes: volumes, } if !hostUsers { ps.HostUsers = &hostUsers @@ -513,6 +510,9 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, for _, ctr := range ctrs { ctrNames = append(ctrNames, removeUnderscores(ctr.Name())) for k, v := range ctr.config.Spec.Annotations { + if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) { + continue + } kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) } @@ -608,7 +608,7 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer := v1.Container{} kubeVolumes := []v1.Volume{} annotations := make(map[string]string) - kubeSec, err := generateKubeSecurityContext(c) + kubeSec, hasSecData, err := generateKubeSecurityContext(c) if err != nil { return kubeContainer, kubeVolumes, nil, annotations, err } @@ -683,9 +683,16 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer.WorkingDir = c.WorkingDir() } - if imgData.User == c.User() { + if imgData.User == c.User() && hasSecData { kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil } + // If the image has user set as a positive integer value, then set runAsNonRoot to true + // in the kube yaml + imgUserID, err := strconv.Atoi(imgData.User) + if err == nil && imgUserID > 0 { + trueBool := true + kubeSec.RunAsNonRoot = &trueBool + } envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env, imgData.Config.Env) if err != nil { @@ -696,14 +703,16 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer.Ports = ports // This should not be applicable // container.EnvFromSource = - kubeContainer.SecurityContext = kubeSec + if hasSecData { + kubeContainer.SecurityContext = kubeSec + } kubeContainer.StdinOnce = false kubeContainer.TTY = c.Terminal() - if c.config.Spec.Linux != nil && - c.config.Spec.Linux.Resources != nil { - if c.config.Spec.Linux.Resources.Memory != nil && - c.config.Spec.Linux.Resources.Memory.Limit != nil { + resources := c.LinuxResources() + if resources != nil { + if resources.Memory != nil && + resources.Memory.Limit != nil { if kubeContainer.Resources.Limits == nil { kubeContainer.Resources.Limits = v1.ResourceList{} } @@ -713,11 +722,11 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer.Resources.Limits[v1.ResourceMemory] = *qty } - if c.config.Spec.Linux.Resources.CPU != nil && - c.config.Spec.Linux.Resources.CPU.Quota != nil && - c.config.Spec.Linux.Resources.CPU.Period != nil { - quota := *c.config.Spec.Linux.Resources.CPU.Quota - period := *c.config.Spec.Linux.Resources.CPU.Period + if resources.CPU != nil && + resources.CPU.Quota != nil && + resources.CPU.Period != nil { + quota := *resources.CPU.Quota + period := *resources.CPU.Period if quota > 0 && period > 0 { cpuLimitMilli := int64(1000 * util.PeriodAndQuotaToCores(period, quota)) @@ -894,35 +903,46 @@ func generateKubeVolumeMount(m specs.Mount) (v1.VolumeMount, v1.Volume, error) { vm := v1.VolumeMount{} vo := v1.Volume{} - name, err := convertVolumePathToName(m.Source) - if err != nil { - return vm, vo, err + var ( + name string + err error + ) + if m.Type == define.TypeTmpfs { + name = "tmp" + vo.EmptyDir = &v1.EmptyDirVolumeSource{ + Medium: v1.StorageMediumMemory, + } + vo.Name = name + } else { + name, err = convertVolumePathToName(m.Source) + if err != nil { + return vm, vo, err + } + // To avoid naming conflicts with any persistent volume mounts, add a unique suffix to the volume's name. + name += "-host" + vo.Name = name + vo.HostPath = &v1.HostPathVolumeSource{} + vo.HostPath.Path = m.Source + isDir, err := isHostPathDirectory(m.Source) + // neither a directory or a file lives here, default to creating a directory + // TODO should this be an error instead? + var hostPathType v1.HostPathType + switch { + case err != nil: + hostPathType = v1.HostPathDirectoryOrCreate + case isDir: + hostPathType = v1.HostPathDirectory + default: + hostPathType = v1.HostPathFile + } + vo.HostPath.Type = &hostPathType } - // To avoid naming conflicts with any persistent volume mounts, add a unique suffix to the volume's name. - name += "-host" vm.Name = name vm.MountPath = m.Destination if cutil.StringInSlice("ro", m.Options) { vm.ReadOnly = true } - vo.Name = name - vo.HostPath = &v1.HostPathVolumeSource{} - vo.HostPath.Path = m.Source - isDir, err := isHostPathDirectory(m.Source) - // neither a directory or a file lives here, default to creating a directory - // TODO should this be an error instead? - var hostPathType v1.HostPathType - switch { - case err != nil: - hostPathType = v1.HostPathDirectoryOrCreate - case isDir: - hostPathType = v1.HostPathDirectory - default: - hostPathType = v1.HostPathFile - } - vo.HostPath.Type = &hostPathType - return vm, vo, nil } @@ -978,27 +998,16 @@ func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v } } - return &v1.Capabilities{ - Add: add, - Drop: drop, + if len(add) > 0 || len(drop) > 0 { + return &v1.Capabilities{ + Add: add, + Drop: drop, + } } + return nil } -func capAddDrop(caps *specs.LinuxCapabilities) (*v1.Capabilities, error) { - g, err := generate.New("linux") - if err != nil { - return nil, err - } - - defCaps := g.Config.Process.Capabilities - // Combine all the default capabilities into a slice - defaultCaps := make([]string, 0, len(defCaps.Ambient)+len(defCaps.Bounding)+len(defCaps.Effective)+len(defCaps.Inheritable)+len(defCaps.Permitted)) - defaultCaps = append(defaultCaps, defCaps.Ambient...) - defaultCaps = append(defaultCaps, defCaps.Bounding...) - defaultCaps = append(defaultCaps, defCaps.Effective...) - defaultCaps = append(defaultCaps, defCaps.Inheritable...) - defaultCaps = append(defaultCaps, defCaps.Permitted...) - +func (c *Container) capAddDrop(caps *specs.LinuxCapabilities) *v1.Capabilities { // Combine all the container's capabilities into a slice containerCaps := make([]string, 0, len(caps.Ambient)+len(caps.Bounding)+len(caps.Effective)+len(caps.Inheritable)+len(caps.Permitted)) containerCaps = append(containerCaps, caps.Ambient...) @@ -1007,12 +1016,12 @@ func capAddDrop(caps *specs.LinuxCapabilities) (*v1.Capabilities, error) { containerCaps = append(containerCaps, caps.Inheritable...) containerCaps = append(containerCaps, caps.Permitted...) - calculatedCaps := determineCapAddDropFromCapabilities(defaultCaps, containerCaps) - return calculatedCaps, nil + calculatedCaps := determineCapAddDropFromCapabilities(c.runtime.config.Containers.DefaultCapabilities, containerCaps) + return calculatedCaps } // generateKubeSecurityContext generates a securityContext based on the existing container -func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { +func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, bool, error) { privileged := c.Privileged() ro := c.IsReadOnly() allowPrivEscalation := !c.config.Spec.Process.NoNewPrivileges @@ -1020,19 +1029,17 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { var capabilities *v1.Capabilities if !privileged { // Running privileged adds all caps. - newCaps, err := capAddDrop(c.config.Spec.Process.Capabilities) - if err != nil { - return nil, err - } - capabilities = newCaps + capabilities = c.capAddDrop(c.config.Spec.Process.Capabilities) } + scHasData := false sc := v1.SecurityContext{ // RunAsNonRoot is an optional parameter; our first implementations should be root only; however // I'm leaving this as a bread-crumb for later //RunAsNonRoot: &nonRoot, } if capabilities != nil { + scHasData = true sc.Capabilities = capabilities } var selinuxOpts v1.SELinuxOptions @@ -1043,24 +1050,30 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { case "type": selinuxOpts.Type = opts[1] sc.SELinuxOptions = &selinuxOpts + scHasData = true case "level": selinuxOpts.Level = opts[1] sc.SELinuxOptions = &selinuxOpts + scHasData = true } case 1: if opts[0] == "disable" { selinuxOpts.Type = "spc_t" sc.SELinuxOptions = &selinuxOpts + scHasData = true } } if !allowPrivEscalation { + scHasData = true sc.AllowPrivilegeEscalation = &allowPrivEscalation } if privileged { + scHasData = true sc.Privileged = &privileged } if ro { + scHasData = true sc.ReadOnlyRootFilesystem = &ro } if c.User() != "" { @@ -1069,7 +1082,7 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { defer c.lock.Unlock() } if err := c.syncContainer(); err != nil { - return nil, fmt.Errorf("unable to sync container during YAML generation: %w", err) + return nil, false, fmt.Errorf("unable to sync container during YAML generation: %w", err) } mountpoint := c.state.Mountpoint @@ -1077,7 +1090,7 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { var err error mountpoint, err = c.mount() if err != nil { - return nil, fmt.Errorf("failed to mount %s mountpoint: %w", c.ID(), err) + return nil, false, fmt.Errorf("failed to mount %s mountpoint: %w", c.ID(), err) } defer func() { if err := c.unmount(false); err != nil { @@ -1089,14 +1102,16 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { execUser, err := lookup.GetUserGroupInfo(mountpoint, c.User(), nil) if err != nil { - return nil, err + return nil, false, err } uid := int64(execUser.Uid) gid := int64(execUser.Gid) + scHasData = true sc.RunAsUser = &uid sc.RunAsGroup = &gid } - return &sc, nil + + return &sc, scHasData, nil } // generateKubeVolumeDeviceFromLinuxDevice takes a list of devices and makes a VolumeDevice struct for kube diff --git a/vendor/github.com/containers/podman/v4/libpod/lock/file/file_lock.go b/vendor/github.com/containers/podman/v4/libpod/lock/file/file_lock.go index bcbaea5e..e26ca24a 100644 --- a/vendor/github.com/containers/podman/v4/libpod/lock/file/file_lock.go +++ b/vendor/github.com/containers/podman/v4/libpod/lock/file/file_lock.go @@ -7,7 +7,7 @@ import ( "strconv" "syscall" - "github.com/containers/storage" + "github.com/containers/storage/pkg/lockfile" "github.com/sirupsen/logrus" ) @@ -150,7 +150,7 @@ func (locks *FileLocks) LockFileLock(lck uint32) error { return fmt.Errorf("locks have already been closed: %w", syscall.EINVAL) } - l, err := storage.GetLockfile(locks.getLockPath(lck)) + l, err := lockfile.GetLockFile(locks.getLockPath(lck)) if err != nil { return fmt.Errorf("acquiring lock: %w", err) } @@ -164,7 +164,7 @@ func (locks *FileLocks) UnlockFileLock(lck uint32) error { if !locks.valid { return fmt.Errorf("locks have already been closed: %w", syscall.EINVAL) } - l, err := storage.GetLockfile(locks.getLockPath(lck)) + l, err := lockfile.GetLockFile(locks.getLockPath(lck)) if err != nil { return fmt.Errorf("acquiring lock: %w", err) } diff --git a/vendor/github.com/containers/podman/v4/libpod/lock/shm/shm_lock.go b/vendor/github.com/containers/podman/v4/libpod/lock/shm/shm_lock.go index c8ae17a1..3934883a 100644 --- a/vendor/github.com/containers/podman/v4/libpod/lock/shm/shm_lock.go +++ b/vendor/github.com/containers/podman/v4/libpod/lock/shm/shm_lock.go @@ -1,11 +1,15 @@ -//go:build linux && cgo -// +build linux,cgo +//go:build (linux || freebsd) && cgo +// +build linux freebsd +// +build cgo package shm // #cgo LDFLAGS: -lrt -lpthread // #cgo CFLAGS: -Wall -Werror // #include +// #include +// #include +// #include // #include "shm_lock.h" // const uint32_t bitmap_size_c = BITMAP_SIZE; import "C" @@ -261,3 +265,13 @@ func (locks *SHMLocks) UnlockSemaphore(sem uint32) error { return nil } + +func unlinkSHMLock(path string) error { + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + + if _, err := C.shm_unlink(cPath); err != nil { + return fmt.Errorf("failed to unlink SHM locks: %w", err) + } + return nil +} diff --git a/vendor/github.com/containers/podman/v4/libpod/logs/reversereader/reversereader.go b/vendor/github.com/containers/podman/v4/libpod/logs/reversereader/reversereader.go index f2e71fb6..d5d7ac17 100644 --- a/vendor/github.com/containers/podman/v4/libpod/logs/reversereader/reversereader.go +++ b/vendor/github.com/containers/podman/v4/libpod/logs/reversereader/reversereader.go @@ -16,7 +16,7 @@ type ReverseReader struct { // NewReverseReader returns a reader that reads from the end of a file // rather than the beginning. It sets the readsize to pagesize and determines -// the first offset using using modulus. +// the first offset using modulus. func NewReverseReader(reader *os.File) (*ReverseReader, error) { // pagesize should be safe for memory use and file reads should be on page // boundaries as well diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_common.go b/vendor/github.com/containers/podman/v4/libpod/networking_common.go index fa444e26..816e1d1c 100644 --- a/vendor/github.com/containers/podman/v4/libpod/networking_common.go +++ b/vendor/github.com/containers/podman/v4/libpod/networking_common.go @@ -39,9 +39,15 @@ func (c *Container) convertPortMappings() []types.PortMapping { } func (c *Container) getNetworkOptions(networkOpts map[string]types.PerNetworkOptions) types.NetworkOptions { + nameservers := make([]string, 0, len(c.runtime.config.Containers.DNSServers)+len(c.config.DNSServer)) + nameservers = append(nameservers, c.runtime.config.Containers.DNSServers...) + for _, ip := range c.config.DNSServer { + nameservers = append(nameservers, ip.String()) + } opts := types.NetworkOptions{ ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), + ContainerName: getNetworkPodName(c), + DNSServers: nameservers, } opts.PortMappings = c.convertPortMappings() @@ -78,9 +84,9 @@ func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string return results, err } -// getCNIPodName return the pod name (hostname) used by CNI and the dnsname plugin. +// getNetworkPodName return the pod name (hostname) used by dns backend. // If we are in the pod network namespace use the pod name otherwise the container name -func getCNIPodName(c *Container) string { +func getNetworkPodName(c *Container) string { if c.config.NetMode.IsPod() || c.IsInfra() { pod, err := c.runtime.state.Pod(c.PodID()) if err == nil { @@ -92,7 +98,7 @@ func getCNIPodName(c *Container) string { // Tear down a container's network configuration and joins the // rootless net ns as rootless user -func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error { +func (r *Runtime) teardownNetworkBackend(ns string, opts types.NetworkOptions) error { rootlessNetNS, err := r.GetRootlessNetNs(false) if err != nil { return err @@ -106,7 +112,7 @@ func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error { // rootlessNetNS is nil if we are root if rootlessNetNS != nil { - // execute the cni setup in the rootless net ns + // execute the network setup in the rootless net ns err = rootlessNetNS.Do(tearDownPod) if cerr := rootlessNetNS.Cleanup(r); cerr != nil { logrus.WithError(err).Error("failed to clean up rootless netns") @@ -118,24 +124,25 @@ func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error { return err } -// Tear down a container's CNI network configuration, but do not tear down the +// Tear down a container's network backend configuration, but do not tear down the // namespace itself. -func (r *Runtime) teardownCNI(ctr *Container) error { - if ctr.state.NetNS == nil { +func (r *Runtime) teardownNetwork(ctr *Container) error { + if ctr.state.NetNS == "" { // The container has no network namespace, we're set return nil } - logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) + logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS, ctr.ID()) networks, err := ctr.networks() if err != nil { return err } - if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 { + if !ctr.config.NetMode.IsSlirp4netns() && + !ctr.config.NetMode.IsPasta() && len(networks) > 0 { netOpts := ctr.getNetworkOptions(networks) - return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts) + return r.teardownNetworkBackend(ctr.state.NetNS, netOpts) } return nil } @@ -153,13 +160,11 @@ func isBridgeNetMode(n namespaces.NetworkMode) error { // It will tear down, and then reconfigure, the network of the container. // This is mainly used when a reload of firewall rules wipes out existing // firewall configuration. -// Efforts will be made to preserve MAC and IP addresses, but this only works if -// the container only joined a single CNI network, and was only assigned a -// single MAC or IP. -// Only works on root containers at present, though in the future we could +// Efforts will be made to preserve MAC and IP addresses. +// Only works on containers with bridge networking at present, though in the future we could // extend this to stop + restart slirp4netns func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.StatusBlock, error) { - if ctr.state.NetNS == nil { + if ctr.state.NetNS == "" { return nil, fmt.Errorf("container %s network is not configured, refusing to reload: %w", ctr.ID(), define.ErrCtrStateInvalid) } if err := isBridgeNetMode(ctr.config.NetMode); err != nil { @@ -167,9 +172,9 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu } logrus.Infof("Going to reload container %s network", ctr.ID()) - err := r.teardownCNI(ctr) + err := r.teardownNetwork(ctr) if err != nil { - // teardownCNI will error if the iptables rules do not exists and this is the case after + // teardownNetwork will error if the iptables rules do not exists and this is the case after // a firewall reload. The purpose of network reload is to recreate the rules if they do // not exists so we should not log this specific error as error. This would confuse users otherwise. // iptables-legacy and iptables-nft will create different errors make sure to match both. @@ -228,14 +233,14 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } settings := new(define.InspectNetworkSettings) - settings.Ports = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts) + settings.Ports = makeInspectPorts(c.config.PortMappings, c.config.ExposedPorts) networks, err := c.networks() if err != nil { return nil, err } - if c.state.NetNS == nil { + if c.state.NetNS == "" { if networkNSPath := c.joinedNetworkNSPath(); networkNSPath != "" { if result, err := c.inspectJoinedNetworkNS(networkNSPath); err == nil { // fallback to dummy configuration @@ -247,7 +252,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // We can't do more if the network is down. - // We still want to make dummy configurations for each CNI net + // We still want to make dummy configurations for each network // the container joined. if len(networks) > 0 { settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks)) @@ -263,7 +268,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // Set network namespace path - settings.SandboxKey = c.state.NetNS.Path() + settings.SandboxKey = c.state.NetNS netStatus := c.getNetworkStatus() // If this is empty, we're probably slirp4netns @@ -369,7 +374,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro } // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name + // CNI and netavark and the libpod db only uses names so it is important that we only use the name netName, err = c.runtime.normalizeNetworkName(netName) if err != nil { return err @@ -395,20 +400,20 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return nil } - if c.state.NetNS == nil { + if c.state.NetNS == "" { return fmt.Errorf("unable to disconnect %s from %s: %w", nameOrID, netName, define.ErrNoNetwork) } opts := types.NetworkOptions{ ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), + ContainerName: getNetworkPodName(c), } opts.PortMappings = c.convertPortMappings() opts.Networks = map[string]types.PerNetworkOptions{ netName: networks[netName], } - if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil { + if err := c.runtime.teardownNetworkBackend(c.state.NetNS, opts); err != nil { return err } @@ -450,7 +455,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro if len(rm) > 0 { // make sure to lock this file to prevent concurrent writes when // this is used a net dependency container - lock, err := lockfile.GetLockfile(file) + lock, err := lockfile.GetLockFile(file) if err != nil { return fmt.Errorf("failed to lock hosts file: %w", err) } @@ -469,7 +474,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro // ConnectNetwork connects a container to a given network func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNetworkOptions) error { - // only the bridge mode supports cni networks + // only the bridge mode supports networks if err := isBridgeNetMode(c.config.NetMode); err != nil { return err } @@ -483,7 +488,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe } // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name + // CNI and netavark and the libpod db only uses names so it is important that we only use the name netName, err = c.runtime.normalizeNetworkName(netName) if err != nil { return err @@ -518,20 +523,20 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { return nil } - if c.state.NetNS == nil { + if c.state.NetNS == "" { return fmt.Errorf("unable to connect %s to %s: %w", nameOrID, netName, define.ErrNoNetwork) } opts := types.NetworkOptions{ ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), + ContainerName: getNetworkPodName(c), } opts.PortMappings = c.convertPortMappings() opts.Networks = map[string]types.PerNetworkOptions{ netName: netOpts, } - results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) + results, err := c.runtime.setUpNetwork(c.state.NetNS, opts) if err != nil { return err } @@ -590,7 +595,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { // make sure to lock this file to prevent concurrent writes when // this is used a net dependency container - lock, err := lockfile.GetLockfile(file) + lock, err := lockfile.GetLockFile(file) if err != nil { return fmt.Errorf("failed to lock hosts file: %w", err) } @@ -625,7 +630,7 @@ func getFreeInterfaceName(networks map[string]types.PerNetworkOptions) string { return "" } -// DisconnectContainerFromNetwork removes a container from its CNI network +// DisconnectContainerFromNetwork removes a container from its network func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error { ctr, err := r.LookupContainer(nameOrID) if err != nil { @@ -634,7 +639,7 @@ func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force return ctr.NetworkDisconnect(nameOrID, netName, force) } -// ConnectContainerToNetwork connects a container to a CNI network +// ConnectContainerToNetwork connects a container to a network func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error { ctr, err := r.LookupContainer(nameOrID) if err != nil { @@ -644,7 +649,7 @@ func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts ty } // normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. -// If the network is not found a errors is returned. +// If the network is not found an error is returned. func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { net, err := r.network.NetworkInspect(nameOrID) if err != nil { diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_freebsd.go b/vendor/github.com/containers/podman/v4/libpod/networking_freebsd.go index 230efc99..26899faf 100644 --- a/vendor/github.com/containers/podman/v4/libpod/networking_freebsd.go +++ b/vendor/github.com/containers/podman/v4/libpod/networking_freebsd.go @@ -75,7 +75,7 @@ type LinkStatistics64 struct { type RootlessNetNS struct { dir string - Lock lockfile.Locker + Lock *lockfile.LockFile } // getPath will join the given path to the rootless netns dir @@ -85,7 +85,7 @@ func (r *RootlessNetNS) getPath(path string) string { // Do - run the given function in the rootless netns. // It does not lock the rootlessCNI lock, the caller -// should only lock when needed, e.g. for cni operations. +// should only lock when needed, e.g. for network operations. func (r *RootlessNetNS) Do(toRun func() error) error { return errors.New("not supported on freebsd") } @@ -116,7 +116,7 @@ func (r *Runtime) setupNetNS(ctr *Container) error { } // Create and configure a new network namespace for a container -func (r *Runtime) configureNetNS(ctr *Container, ctrNS *jailNetNS) (status map[string]types.StatusBlock, rerr error) { +func (r *Runtime) configureNetNS(ctr *Container, ctrNS string) (status map[string]types.StatusBlock, rerr error) { if err := r.exposeMachinePorts(ctr.config.PortMappings); err != nil { return nil, err } @@ -139,7 +139,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS *jailNetNS) (status map[s } netOpts := ctr.getNetworkOptions(networks) - netStatus, err := r.setUpNetwork(ctrNS.Name, netOpts) + netStatus, err := r.setUpNetwork(ctrNS, netOpts) if err != nil { return nil, err } @@ -148,16 +148,16 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS *jailNetNS) (status map[s } // Create and configure a new network namespace for a container -func (r *Runtime) createNetNS(ctr *Container) (n *jailNetNS, q map[string]types.StatusBlock, retErr error) { +func (r *Runtime) createNetNS(ctr *Container) (n string, q map[string]types.StatusBlock, retErr error) { b := make([]byte, 16) _, err := rand.Reader.Read(b) if err != nil { - return nil, nil, fmt.Errorf("failed to generate random vnet name: %v", err) + return "", nil, fmt.Errorf("failed to generate random vnet name: %v", err) } - ctrNS := &jailNetNS{Name: fmt.Sprintf("vnet-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])} + netns := fmt.Sprintf("vnet-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) jconf := jail.NewConfig() - jconf.Set("name", ctrNS.Name) + jconf.Set("name", netns) jconf.Set("vnet", jail.NEW) jconf.Set("children.max", 1) jconf.Set("persist", true) @@ -166,15 +166,24 @@ func (r *Runtime) createNetNS(ctr *Container) (n *jailNetNS, q map[string]types. jconf.Set("allow.raw_sockets", true) jconf.Set("allow.chflags", true) jconf.Set("securelevel", -1) - if _, err := jail.Create(jconf); err != nil { - logrus.Debugf("Failed to create vnet jail %s for container %s", ctrNS.Name, ctr.ID()) + j, err := jail.Create(jconf) + if err != nil { + return "", nil, fmt.Errorf("Failed to create vnet jail %s for container %s: %w", netns, ctr.ID(), err) } - logrus.Debugf("Created vnet jail %s for container %s", ctrNS.Name, ctr.ID()) + logrus.Debugf("Created vnet jail %s for container %s", netns, ctr.ID()) var networkStatus map[string]types.StatusBlock - networkStatus, err = r.configureNetNS(ctr, ctrNS) - return ctrNS, networkStatus, err + networkStatus, err = r.configureNetNS(ctr, netns) + if err != nil { + jconf := jail.NewConfig() + jconf.Set("persist", false) + if err := j.Set(jconf); err != nil { + // Log this error and return the error from configureNetNS + logrus.Errorf("failed to destroy vnet jail %s: %w", netns, err) + } + } + return netns, networkStatus, err } // Tear down a network namespace, undoing all state associated with it. @@ -183,32 +192,32 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { // do not return an error otherwise we would prevent network cleanup logrus.Errorf("failed to free gvproxy machine ports: %v", err) } - if err := r.teardownCNI(ctr); err != nil { + if err := r.teardownNetwork(ctr); err != nil { return err } - if ctr.state.NetNS != nil { + if ctr.state.NetNS != "" { // Rather than destroying the jail immediately, reset the // persist flag so that it will live until the container is // done. - netjail, err := jail.FindByName(ctr.state.NetNS.Name) + netjail, err := jail.FindByName(ctr.state.NetNS) if err != nil { - return fmt.Errorf("finding network jail %s: %w", ctr.state.NetNS.Name, err) + return fmt.Errorf("finding network jail %s: %w", ctr.state.NetNS, err) } jconf := jail.NewConfig() jconf.Set("persist", false) if err := netjail.Set(jconf); err != nil { - return fmt.Errorf("releasing network jail %s: %w", ctr.state.NetNS.Name, err) + return fmt.Errorf("releasing network jail %s: %w", ctr.state.NetNS, err) } - ctr.state.NetNS = nil + ctr.state.NetNS = "" } return nil } func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) { - if ctr.state.NetNS == nil { + if ctr.state.NetNS == "" { // If NetNS is nil, it was set as none, and no netNS // was set up this is a valid state and thus return no // error, nor any statistics @@ -216,7 +225,7 @@ func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) { } // FIXME get the interface from the container netstatus - cmd := exec.Command("jexec", ctr.state.NetNS.Name, "netstat", "-bI", "eth0", "--libxo", "json") + cmd := exec.Command("jexec", ctr.state.NetNS, "netstat", "-bI", "eth0", "--libxo", "json") out, err := cmd.Output() if err != nil { return nil, err @@ -246,11 +255,7 @@ func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) { } func (c *Container) joinedNetworkNSPath() string { - if c.state.NetNS != nil { - return c.state.NetNS.Name - } else { - return "" - } + return c.state.NetNS } func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBlock, retErr error) { diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_linux.go b/vendor/github.com/containers/podman/v4/libpod/networking_linux.go index 5376ff8a..31c66246 100644 --- a/vendor/github.com/containers/podman/v4/libpod/networking_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/networking_linux.go @@ -53,7 +53,7 @@ const ( type RootlessNetNS struct { ns ns.NetNS dir string - Lock lockfile.Locker + Lock *lockfile.LockFile } // getPath will join the given path to the rootless netns dir @@ -63,7 +63,7 @@ func (r *RootlessNetNS) getPath(path string) string { // Do - run the given function in the rootless netns. // It does not lock the rootlessCNI lock, the caller -// should only lock when needed, e.g. for cni operations. +// should only lock when needed, e.g. for network operations. func (r *RootlessNetNS) Do(toRun func() error) error { err := r.ns.Do(func(_ ns.NetNS) error { // Before we can run the given function, @@ -269,7 +269,7 @@ func (r *RootlessNetNS) Cleanup(runtime *Runtime) error { // at this stage the container is already locked. // also do not try to lock only containers which are not currently in net // teardown because this will result in an ABBA deadlock between the rootless - // cni lock and the container lock + // rootless netns lock and the container lock // because we need to get the state we have to sync otherwise this will not // work because the state is empty by default // I do not like this but I do not see a better way at moment @@ -281,9 +281,9 @@ func (r *RootlessNetNS) Cleanup(runtime *Runtime) error { // only check for an active netns, we cannot use the container state // because not running does not mean that the netns does not need cleanup // only if the netns is empty we know that we do not need cleanup - return c.state.NetNS != nil + return c.state.NetNS != "" } - ctrs, err := runtime.GetContainers(activeNetns) + ctrs, err := runtime.GetContainers(false, activeNetns) if err != nil { return err } @@ -293,7 +293,7 @@ func (r *RootlessNetNS) Cleanup(runtime *Runtime) error { return nil } logrus.Debug("Cleaning up rootless network namespace") - err = netns.UnmountNS(r.ns) + err = netns.UnmountNS(r.ns.Path()) if err != nil { return err } @@ -333,7 +333,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { runDir := r.config.Engine.TmpDir lfile := filepath.Join(runDir, "rootless-netns.lock") - lock, err := lockfile.GetLockfile(lfile) + lock, err := lockfile.GetLockFile(lfile) if err != nil { return nil, fmt.Errorf("failed to get rootless-netns lockfile: %w", err) } @@ -368,7 +368,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { ns, err := ns.GetNS(path) if err != nil { if !new { - // return a error if we could not get the namespace and should no create one + // return an error if we could not get the namespace and should no create one return nil, fmt.Errorf("getting rootless network namespace: %w", err) } // create a new namespace @@ -498,7 +498,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, fmt.Errorf("failed to create rootless netns resolv.conf: %w", err) } // create cni directories to store files - // they will be bind mounted to the correct location in a extra mount ns + // they will be bind mounted to the correct location in an extra mount ns err = os.MkdirAll(filepath.Join(rootlessNetNsDir, persistentCNIDir), 0700) if err != nil { return nil, fmt.Errorf("could not create rootless-netns var directory: %w", err) @@ -548,7 +548,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { } // Create and configure a new network namespace for a container -func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[string]types.StatusBlock, rerr error) { +func (r *Runtime) configureNetNS(ctr *Container, ctrNS string) (status map[string]types.StatusBlock, rerr error) { if err := r.exposeMachinePorts(ctr.config.PortMappings); err != nil { return nil, err } @@ -563,6 +563,9 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str if ctr.config.NetMode.IsSlirp4netns() { return nil, r.setupSlirp4netns(ctr, ctrNS) } + if ctr.config.NetMode.IsPasta() { + return nil, r.setupPasta(ctr, ctrNS) + } networks, err := ctr.networks() if err != nil { return nil, err @@ -574,7 +577,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str } netOpts := ctr.getNetworkOptions(networks) - netStatus, err := r.setUpNetwork(ctrNS.Path(), netOpts) + netStatus, err := r.setUpNetwork(ctrNS, netOpts) if err != nil { return nil, err } @@ -584,25 +587,24 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str // not set up port because they are still active if rootless.IsRootless() && len(ctr.config.PortMappings) > 0 && ctr.getNetworkStatus() == nil { // set up port forwarder for rootless netns - netnsPath := ctrNS.Path() // TODO: support slirp4netns port forwarder as well // make sure to fix this in container.handleRestartPolicy() as well // Important we have to call this after r.setUpNetwork() so that // we can use the proper netStatus - err = r.setupRootlessPortMappingViaRLK(ctr, netnsPath, netStatus) + err = r.setupRootlessPortMappingViaRLK(ctr, ctrNS, netStatus) } return netStatus, err } // Create and configure a new network namespace for a container -func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.StatusBlock, retErr error) { +func (r *Runtime) createNetNS(ctr *Container) (n string, q map[string]types.StatusBlock, retErr error) { ctrNS, err := netns.NewNS() if err != nil { - return nil, nil, fmt.Errorf("creating network namespace for container %s: %w", ctr.ID(), err) + return "", nil, fmt.Errorf("creating network namespace for container %s: %w", ctr.ID(), err) } defer func() { if retErr != nil { - if err := netns.UnmountNS(ctrNS); err != nil { + if err := netns.UnmountNS(ctrNS.Path()); err != nil { logrus.Errorf("Unmounting partially created network namespace for container %s: %v", ctr.ID(), err) } if err := ctrNS.Close(); err != nil { @@ -614,8 +616,8 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.St logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) var networkStatus map[string]types.StatusBlock - networkStatus, err = r.configureNetNS(ctr, ctrNS) - return ctrNS, networkStatus, err + networkStatus, err = r.configureNetNS(ctr, ctrNS.Path()) + return ctrNS.Path(), networkStatus, err } // Configure the network namespace using the container process @@ -649,46 +651,14 @@ func (r *Runtime) setupNetNS(ctr *Container) error { return fmt.Errorf("cannot mount %s: %w", nsPath, err) } - netNS, err := ns.GetNS(nsPath) - if err != nil { - return err - } - networkStatus, err := r.configureNetNS(ctr, netNS) + networkStatus, err := r.configureNetNS(ctr, nsPath) // Assign NetNS attributes to container - ctr.state.NetNS = netNS + ctr.state.NetNS = nsPath ctr.state.NetworkStatus = networkStatus return err } -// Join an existing network namespace -func joinNetNS(path string) (ns.NetNS, error) { - netNS, err := ns.GetNS(path) - if err != nil { - return nil, fmt.Errorf("retrieving network namespace at %s: %w", path, err) - } - - return netNS, nil -} - -// Close a network namespace. -// Differs from teardownNetNS() in that it will not attempt to undo the setup of -// the namespace, but will instead only close the open file descriptor -func (r *Runtime) closeNetNS(ctr *Container) error { - if ctr.state.NetNS == nil { - // The container has no network namespace, we're set - return nil - } - - if err := ctr.state.NetNS.Close(); err != nil { - return fmt.Errorf("closing network namespace for container %s: %w", ctr.ID(), err) - } - - ctr.state.NetNS = nil - - return nil -} - // Tear down a network namespace, undoing all state associated with it. func (r *Runtime) teardownNetNS(ctr *Container) error { if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil { @@ -699,7 +669,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { // Do not check the error here, we want to always umount the netns // This will ensure that the container interface will be deleted // even when there is a CNI or netavark bug. - prevErr := r.teardownCNI(ctr) + prevErr := r.teardownNetwork(ctr) // First unmount the namespace if err := netns.UnmountNS(ctr.state.NetNS); err != nil { @@ -709,22 +679,14 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { return fmt.Errorf("unmounting network namespace for container %s: %w", ctr.ID(), err) } - // Now close the open file descriptor - if err := ctr.state.NetNS.Close(); err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - return fmt.Errorf("closing network namespace for container %s: %w", ctr.ID(), err) - } - - ctr.state.NetNS = nil + ctr.state.NetNS = "" return prevErr } func getContainerNetNS(ctr *Container) (string, *Container, error) { - if ctr.state.NetNS != nil { - return ctr.state.NetNS.Path(), nil, nil + if ctr.state.NetNS != "" { + return ctr.state.NetNS, nil, nil } if ctr.config.NetNsCtr != "" { c, err := ctr.runtime.GetContainer(ctr.config.NetNsCtr) diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_pasta_linux.go b/vendor/github.com/containers/podman/v4/libpod/networking_pasta_linux.go new file mode 100644 index 00000000..bf28a117 --- /dev/null +++ b/vendor/github.com/containers/podman/v4/libpod/networking_pasta_linux.go @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// networking_pasta_linux.go - Start pasta(1) for user-mode connectivity +// +// Copyright (c) 2022 Red Hat GmbH +// Author: Stefano Brivio + +package libpod + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/sirupsen/logrus" +) + +func (r *Runtime) setupPasta(ctr *Container, netns string) error { + var NoTCPInitPorts = true + var NoUDPInitPorts = true + var NoTCPNamespacePorts = true + var NoUDPNamespacePorts = true + var NoMapGW = true + + path, err := r.config.FindHelperBinary("pasta", true) + if err != nil { + return fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err) + } + + cmdArgs := []string{} + cmdArgs = append(cmdArgs, "--config-net") + + for _, i := range ctr.convertPortMappings() { + protocols := strings.Split(i.Protocol, ",") + for _, protocol := range protocols { + var addr string + + if i.HostIP != "" { + addr = fmt.Sprintf("%s/", i.HostIP) + } + + switch protocol { + case "tcp": + cmdArgs = append(cmdArgs, "-t") + case "udp": + cmdArgs = append(cmdArgs, "-u") + case "default": + return fmt.Errorf("can't forward protocol: %s", protocol) + } + + arg := fmt.Sprintf("%s%d-%d:%d-%d", addr, + i.HostPort, + i.HostPort+i.Range-1, + i.ContainerPort, + i.ContainerPort+i.Range-1) + cmdArgs = append(cmdArgs, arg) + } + } + + cmdArgs = append(cmdArgs, ctr.config.NetworkOptions["pasta"]...) + + for i, opt := range cmdArgs { + switch opt { + case "-t", "--tcp-ports": + NoTCPInitPorts = false + case "-u", "--udp-ports": + NoUDPInitPorts = false + case "-T", "--tcp-ns": + NoTCPNamespacePorts = false + case "-U", "--udp-ns": + NoUDPNamespacePorts = false + case "--map-gw": + NoMapGW = false + // not an actual pasta(1) option + cmdArgs = append(cmdArgs[:i], cmdArgs[i+1:]...) + } + } + + if NoTCPInitPorts { + cmdArgs = append(cmdArgs, "-t", "none") + } + if NoUDPInitPorts { + cmdArgs = append(cmdArgs, "-u", "none") + } + if NoTCPNamespacePorts { + cmdArgs = append(cmdArgs, "-T", "none") + } + if NoUDPNamespacePorts { + cmdArgs = append(cmdArgs, "-U", "none") + } + if NoMapGW { + cmdArgs = append(cmdArgs, "--no-map-gw") + } + + cmdArgs = append(cmdArgs, "--netns", netns) + + logrus.Debugf("pasta arguments: %s", strings.Join(cmdArgs, " ")) + + // pasta forks once ready, and quits once we delete the target namespace + _, err = exec.Command(path, cmdArgs...).Output() + if err != nil { + return fmt.Errorf("failed to start pasta:\n%s", + err.(*exec.ExitError).Stderr) + } + + return nil +} diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_slirp4netns.go b/vendor/github.com/containers/podman/v4/libpod/networking_slirp4netns.go index 4026b6b4..48e1aae0 100644 --- a/vendor/github.com/containers/podman/v4/libpod/networking_slirp4netns.go +++ b/vendor/github.com/containers/podman/v4/libpod/networking_slirp4netns.go @@ -212,7 +212,7 @@ func createBasicSlirp4netnsCmdArgs(options *slirp4netnsNetworkOptions, features } // setupSlirp4netns can be called in rootful as well as in rootless -func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error { +func (r *Runtime) setupSlirp4netns(ctr *Container, netns string) error { path := r.config.Engine.NetworkCmdPath if path == "" { var err error @@ -267,7 +267,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error { if err != nil { return fmt.Errorf("failed to create rootless network sync pipe: %w", err) } - netnsPath = netns.Path() + netnsPath = netns cmdArgs = append(cmdArgs, "--netns-type=path", netnsPath, "tap0") } else { defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR) diff --git a/vendor/github.com/containers/podman/v4/libpod/networking_unsupported.go b/vendor/github.com/containers/podman/v4/libpod/networking_unsupported.go index e5a6d145..d1cec1c4 100644 --- a/vendor/github.com/containers/podman/v4/libpod/networking_unsupported.go +++ b/vendor/github.com/containers/podman/v4/libpod/networking_unsupported.go @@ -15,7 +15,7 @@ import ( type RootlessNetNS struct { dir string - Lock lockfile.Locker + Lock *lockfile.LockFile } // ocicniPortsToNetTypesPorts convert the old port format to the new one @@ -37,17 +37,17 @@ func (r *Runtime) setupNetNS(ctr *Container) error { } // normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. -// If the network is not found a errors is returned. +// If the network is not found an error is returned. func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { return "", errors.New("not implemented (*Runtime) normalizeNetworkName") } -// DisconnectContainerFromNetwork removes a container from its CNI network +// DisconnectContainerFromNetwork removes a container from its network func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error { return errors.New("not implemented (*Runtime) DisconnectContainerFromNetwork") } -// ConnectContainerToNetwork connects a container to a CNI network +// ConnectContainerToNetwork connects a container to a network func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error { return errors.New("not implemented (*Runtime) ConnectContainerToNetwork") } @@ -59,7 +59,7 @@ func (r *RootlessNetNS) getPath(path string) string { // Do - run the given function in the rootless netns. // It does not lock the rootlessCNI lock, the caller -// should only lock when needed, e.g. for cni operations. +// should only lock when needed, e.g. for network operations. func (r *RootlessNetNS) Do(toRun func() error) error { return errors.New("not implemented (*RootlessNetNS) Do") } diff --git a/vendor/github.com/containers/podman/v4/libpod/oci_conmon_common.go b/vendor/github.com/containers/podman/v4/libpod/oci_conmon_common.go index f5acf079..6dd54a8b 100644 --- a/vendor/github.com/containers/podman/v4/libpod/oci_conmon_common.go +++ b/vendor/github.com/containers/podman/v4/libpod/oci_conmon_common.go @@ -400,12 +400,11 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) return nil } - stopSignal := ctr.config.StopSignal - if stopSignal == 0 { - stopSignal = uint(syscall.SIGTERM) - } - if timeout > 0 { + stopSignal := ctr.config.StopSignal + if stopSignal == 0 { + stopSignal = uint(syscall.SIGTERM) + } if err := r.KillContainer(ctr, stopSignal, all); err != nil { // Is the container gone? // If so, it probably died between the first check and @@ -428,7 +427,21 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) } } + // If the timeout was set to 0 or if stopping the container with the + // specified signal did not work, use the big hammer with SIGKILL. if err := r.KillContainer(ctr, uint(unix.SIGKILL), all); err != nil { + // There's an inherent race with the cleanup process (see + // #16142, #17142). If the container has already been marked as + // stopped or exited by the cleanup process, we can return + // immediately. + if errors.Is(err, define.ErrCtrStateInvalid) && ctr.ensureState(define.ContainerStateStopped, define.ContainerStateExited) { + return nil + } + + // If the PID is 0, then the container is already stopped. + if ctr.state.PID == 0 { + return nil + } // Again, check if the container is gone. If it is, exit cleanly. if aliveErr := unix.Kill(ctr.state.PID, 0); errors.Is(aliveErr, unix.ESRCH) { return nil @@ -594,6 +607,10 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. device := logLine.Device var header []byte headerLen := uint32(len(logLine.Msg)) + if !logLine.Partial() { + // we append an extra newline in this case so we need to increment the len as well + headerLen++ + } logSize += len(logLine.Msg) switch strings.ToLower(device) { case "stdin": @@ -936,31 +953,20 @@ func waitContainerStop(ctr *Container, timeout time.Duration) error { // Wait for a given PID to stop func waitPidStop(pid int, timeout time.Duration) error { - done := make(chan struct{}) - chControl := make(chan struct{}) - go func() { - for { - select { - case <-chControl: - return - default: - if err := unix.Kill(pid, 0); err != nil { - if err == unix.ESRCH { - close(done) - return - } - logrus.Errorf("Pinging PID %d with signal 0: %v", pid, err) + timer := time.NewTimer(timeout) + for { + select { + case <-timer.C: + return fmt.Errorf("given PID did not die within timeout") + default: + if err := unix.Kill(pid, 0); err != nil { + if err == unix.ESRCH { + return nil } - time.Sleep(100 * time.Millisecond) + logrus.Errorf("Pinging PID %d with signal 0: %v", pid, err) } + time.Sleep(10 * time.Millisecond) } - }() - select { - case <-done: - return nil - case <-time.After(timeout): - close(chControl) - return fmt.Errorf("given PIDs did not die within timeout") } } @@ -1438,7 +1444,7 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int, ch <- syncStruct{si: si} }() - data := -1 //nolint: wastedassign + data := -1 select { case ss := <-ch: if ss.err != nil { diff --git a/vendor/github.com/containers/podman/v4/libpod/oci_conmon_linux.go b/vendor/github.com/containers/podman/v4/libpod/oci_conmon_linux.go index 0964d4ea..5740d706 100644 --- a/vendor/github.com/containers/podman/v4/libpod/oci_conmon_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/oci_conmon_linux.go @@ -133,8 +133,7 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec // there are only 2 valid cgroup managers cgroupParent := ctr.CgroupParent() cgroupPath := filepath.Join(ctr.config.CgroupParent, "conmon") - Resource := ctr.Spec().Linux.Resources - cgroupResources, err := GetLimits(Resource) + cgroupResources, err := GetLimits(ctr.LinuxResources()) if err != nil { logrus.StandardLogger().Log(logLevel, "Could not get ctr resources") } diff --git a/vendor/github.com/containers/podman/v4/libpod/options.go b/vendor/github.com/containers/podman/v4/libpod/options.go index 71ad3d11..d9a6f997 100644 --- a/vendor/github.com/containers/podman/v4/libpod/options.go +++ b/vendor/github.com/containers/podman/v4/libpod/options.go @@ -6,6 +6,7 @@ import ( "net" "os" "path/filepath" + "strings" "syscall" "github.com/containers/buildah/pkg/parse" @@ -22,11 +23,14 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/regexp" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/sirupsen/logrus" ) +var umaskRegex = regexp.Delayed(`^[0-7]{1,4}$`) + // WithStorageConfig uses the given configuration to set up container storage. // If this is not specified, the system default configuration will be used // instead. @@ -109,6 +113,18 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption { } } +func WithTransientStore(transientStore bool) RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return define.ErrRuntimeFinalized + } + + rt.storageConfig.TransientStore = transientStore + + return nil + } +} + // WithSignaturePolicy specifies the path of a file which decides how trust is // managed for images we've pulled. // If this is not specified, the system default configuration will be used @@ -142,6 +158,17 @@ func WithOCIRuntime(runtime string) RuntimeOption { } } +// WithCtrOCIRuntime specifies an OCI runtime in container's config. +func WithCtrOCIRuntime(runtime string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + ctr.config.OCIRuntime = runtime + return nil + } +} + // WithConmonPath specifies the path to the conmon binary which manages the // runtime. func WithConmonPath(path string) RuntimeOption { @@ -273,7 +300,7 @@ func WithHooksDir(hooksDirs ...string) RuntimeOption { } } -// WithCDI sets the devices to check for for CDI configuration. +// WithCDI sets the devices to check for CDI configuration. func WithCDI(devices []string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { @@ -284,7 +311,7 @@ func WithCDI(devices []string) CtrCreateOption { } } -// WithStorageOpts sets the devices to check for for CDI configuration. +// WithStorageOpts sets the devices to check for CDI configuration. func WithStorageOpts(storageOpts map[string]string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { @@ -351,8 +378,8 @@ func WithNoPivotRoot() RuntimeOption { } } -// WithCNIConfigDir sets the CNI configuration directory. -func WithCNIConfigDir(dir string) RuntimeOption { +// WithNetworkConfigDir sets the network configuration directory. +func WithNetworkConfigDir(dir string) RuntimeOption { return func(rt *Runtime) error { if rt.valid { return define.ErrRuntimeFinalized @@ -753,6 +780,7 @@ func WithName(name string) CtrCreateOption { return define.ErrCtrFinalized } + name = strings.TrimPrefix(name, "/") // Check the name against a regex if !define.NameRegex.MatchString(name) { return define.RegexError @@ -1417,6 +1445,7 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { Dest: vol.Dest, Options: mountOpts, IsAnonymous: vol.IsAnonymous, + SubPath: vol.SubPath, }) } @@ -1551,6 +1580,17 @@ func WithCreateWorkingDir() CtrCreateOption { // Volume Creation Options +func WithVolumeIgnoreIfExist() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + volume.ignoreIfExists = true + + return nil + } +} + // WithVolumeName sets the name of the volume. func WithVolumeName(name string) VolumeCreateOption { return func(volume *Volume) error { @@ -1757,7 +1797,7 @@ func WithUmask(umask string) CtrCreateOption { if ctr.valid { return define.ErrCtrFinalized } - if !define.UmaskRegex.MatchString(umask) { + if !umaskRegex.MatchString(umask) { return fmt.Errorf("invalid umask string %s: %w", umask, define.ErrInvalidArg) } ctr.config.Umask = umask @@ -1820,7 +1860,7 @@ func WithHostUsers(hostUsers []string) CtrCreateOption { } } -// WithInitCtrType indicates the container is a initcontainer +// WithInitCtrType indicates the container is an initcontainer func WithInitCtrType(containerType string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { @@ -1876,6 +1916,21 @@ func WithInfraConfig(compatibleOptions InfraInherit) CtrCreateOption { } } +// WithStartupHealthcheck sets a startup healthcheck for the container. +// Requires that a healthcheck must be set. +func WithStartupHealthcheck(startupHC *define.StartupHealthCheck) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + ctr.config.StartupHealthCheckConfig = new(define.StartupHealthCheck) + if err := JSONDeepCopy(startupHC, ctr.config.StartupHealthCheckConfig); err != nil { + return fmt.Errorf("error copying startup healthcheck into container: %w", err) + } + return nil + } +} + // Pod Creation Options // WithPodCreateCommand adds the full command plus arguments of the current diff --git a/vendor/github.com/containers/podman/v4/libpod/pod_api.go b/vendor/github.com/containers/podman/v4/libpod/pod_api.go index 7b143b47..b5b676b4 100644 --- a/vendor/github.com/containers/podman/v4/libpod/pod_api.go +++ b/vendor/github.com/containers/podman/v4/libpod/pod_api.go @@ -44,10 +44,6 @@ func (p *Pod) startInitContainers(ctx context.Context) error { icLock.Unlock() return fmt.Errorf("failed to remove once init container %s: %w", initCon.ID(), err) } - // Removing a container this way requires an explicit call to clean up the db - if err := p.runtime.state.RemoveContainerFromPod(p, initCon); err != nil { - logrus.Errorf("Removing container %s from database: %v", initCon.ID(), err) - } icLock.Unlock() } } @@ -709,7 +705,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { infraConfig.Networks = netNames } infraConfig.NetworkOptions = infra.config.ContainerNetworkConfig.NetworkOptions - infraConfig.PortBindings = makeInspectPortBindings(infra.config.ContainerNetworkConfig.PortMappings, nil) + infraConfig.PortBindings = makeInspectPortBindings(infra.config.ContainerNetworkConfig.PortMappings) } inspectData := define.InspectPodData{ diff --git a/vendor/github.com/containers/podman/v4/libpod/pod_internal.go b/vendor/github.com/containers/podman/v4/libpod/pod_internal.go index 743280f4..48d83393 100644 --- a/vendor/github.com/containers/podman/v4/libpod/pod_internal.go +++ b/vendor/github.com/containers/podman/v4/libpod/pod_internal.go @@ -44,7 +44,7 @@ func (p *Pod) save() error { // This cannot lock any other pod, but may lock individual containers, as those // will have refreshed by the time pod refresh runs. func (p *Pod) refresh() error { - // Need to to an update from the DB to pull potentially-missing state + // Need to do an update from the DB to pull potentially-missing state if err := p.runtime.state.UpdatePod(p); err != nil { return err } diff --git a/vendor/github.com/containers/podman/v4/libpod/reset.go b/vendor/github.com/containers/podman/v4/libpod/reset.go index b3ece03b..fb157c4c 100644 --- a/vendor/github.com/containers/podman/v4/libpod/reset.go +++ b/vendor/github.com/containers/podman/v4/libpod/reset.go @@ -14,6 +14,8 @@ import ( "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" + "github.com/containers/storage/pkg/lockfile" + stypes "github.com/containers/storage/types" "github.com/sirupsen/logrus" ) @@ -34,7 +36,7 @@ func (r *Runtime) removeAllDirs() error { // TODO: maybe want a helper for getting the path? This is duped from // runtime.go runtimeAliveLock := filepath.Join(r.config.Engine.TmpDir, "alive.lck") - aliveLock, err := storage.GetLockfile(runtimeAliveLock) + aliveLock, err := lockfile.GetLockFile(runtimeAliveLock) if err != nil { logrus.Errorf("Lock runtime alive lock %s: %v", runtimeAliveLock, err) } else { @@ -157,7 +159,13 @@ func (r *Runtime) reset(ctx context.Context) error { } } - xdgRuntimeDir := filepath.Clean(os.Getenv("XDG_RUNTIME_DIR")) + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + if xdgRuntimeDir != "" { + xdgRuntimeDir, err = filepath.EvalSymlinks(xdgRuntimeDir) + if err != nil { + return err + } + } _, prevError := r.store.Shutdown(true) graphRoot := filepath.Clean(r.store.GraphRoot()) if graphRoot == xdgRuntimeDir { @@ -209,9 +217,14 @@ func (r *Runtime) reset(ctx context.Context) error { } } if storageConfPath, err := storage.DefaultConfigFile(rootless.IsRootless()); err == nil { - if _, err = os.Stat(storageConfPath); err == nil { - fmt.Printf("A storage.conf file exists at %s\n", storageConfPath) - fmt.Println("You should remove this file if you did not modify the configuration.") + switch storageConfPath { + case stypes.SystemConfigFile: + break + default: + if _, err = os.Stat(storageConfPath); err == nil { + fmt.Printf(" A %q config file exists.\n", storageConfPath) + fmt.Println("Remove this file if you did not modify the configuration.") + } } } else { if prevError != nil { diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime.go b/vendor/github.com/containers/podman/v4/libpod/runtime.go index 0828c19d..ece290f7 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime.go @@ -35,6 +35,7 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/utils" "github.com/containers/storage" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/unshare" "github.com/docker/docker/pkg/namesgenerator" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -165,7 +166,7 @@ func SetXdgDirs() error { // NewRuntime creates a new container runtime // Options can be passed to override the default configuration for the runtime func NewRuntime(ctx context.Context, options ...RuntimeOption) (*Runtime, error) { - conf, err := config.NewConfig("") + conf, err := config.Default() if err != nil { return nil, err } @@ -322,6 +323,11 @@ func makeRuntime(runtime *Runtime) (retErr error) { } } + // Create the TmpDir if needed + if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil { + return fmt.Errorf("creating runtime temporary files directory: %w", err) + } + // Set up the state. // // TODO - if we further break out the state implementation into @@ -334,7 +340,11 @@ func makeRuntime(runtime *Runtime) (retErr error) { case config.SQLiteStateStore: return fmt.Errorf("SQLite state is currently disabled: %w", define.ErrInvalidArg) case config.BoltDBStateStore: - dbPath := filepath.Join(runtime.config.Engine.StaticDir, "bolt_state.db") + baseDir := runtime.config.Engine.StaticDir + if runtime.storageConfig.TransientStore { + baseDir = runtime.config.Engine.TmpDir + } + dbPath := filepath.Join(baseDir, "bolt_state.db") state, err := NewBoltState(dbPath, runtime) if err != nil { @@ -391,6 +401,7 @@ func makeRuntime(runtime *Runtime) (retErr error) { logrus.Debugf("Using static dir %s", runtime.config.Engine.StaticDir) logrus.Debugf("Using tmp dir %s", runtime.config.Engine.TmpDir) logrus.Debugf("Using volume path %s", runtime.config.Engine.VolumePath) + logrus.Debugf("Using transient store: %v", runtime.storageConfig.TransientStore) // Validate our config against the database, now that we've set our // final storage configuration @@ -409,13 +420,14 @@ func makeRuntime(runtime *Runtime) (retErr error) { } logrus.Debugf("Set libpod namespace to %q", runtime.config.Engine.Namespace) - hasCapSysAdmin, err := unshare.HasCapSysAdmin() - if err != nil { - return err + needsUserns := os.Geteuid() != 0 + if !needsUserns { + hasCapSysAdmin, err := unshare.HasCapSysAdmin() + if err != nil { + return err + } + needsUserns = !hasCapSysAdmin } - - needsUserns := !hasCapSysAdmin - // Set up containers/storage var store storage.Store if needsUserns { @@ -458,14 +470,6 @@ func makeRuntime(runtime *Runtime) (retErr error) { } runtime.imageContext.SignaturePolicyPath = runtime.config.Engine.SignaturePolicyPath - // Create the tmpDir - if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil { - // The directory is allowed to exist - if !errors.Is(err, os.ErrExist) { - return fmt.Errorf("creating tmpdir: %w", err) - } - } - // Get us at least one working OCI runtime. runtime.ociRuntimes = make(map[string]OCIRuntime) @@ -476,7 +480,7 @@ func makeRuntime(runtime *Runtime) (retErr error) { // Don't fatally error. // This will allow us to ship configs including optional // runtimes that might not be installed (crun, kata). - // Only a infof so default configs don't spec errors. + // Only an infof so default configs don't spec errors. logrus.Debugf("Configured OCI runtime %s initialization failed: %v", name, err) continue } @@ -516,14 +520,6 @@ func makeRuntime(runtime *Runtime) (retErr error) { return fmt.Errorf("no default OCI runtime was configured: %w", define.ErrInvalidArg) } - // Make the per-boot files directory if it does not exist - if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0755); err != nil { - // The directory is allowed to exist - if !errors.Is(err, os.ErrExist) { - return fmt.Errorf("creating runtime temporary files directory: %w", err) - } - } - // the store is only set up when we are in the userns so we do the same for the network interface if !needsUserns { netBackend, netInterface, err := network.NetworkBackend(runtime.store, runtime.config, runtime.syslog) @@ -539,7 +535,7 @@ func makeRuntime(runtime *Runtime) (retErr error) { // This check must be locked to prevent races runtimeAliveLock := filepath.Join(runtime.config.Engine.TmpDir, "alive.lck") runtimeAliveFile := filepath.Join(runtime.config.Engine.TmpDir, "alive") - aliveLock, err := storage.GetLockfile(runtimeAliveLock) + aliveLock, err := lockfile.GetLockFile(runtimeAliveLock) if err != nil { return fmt.Errorf("acquiring runtime init lock: %w", err) } @@ -547,9 +543,10 @@ func makeRuntime(runtime *Runtime) (retErr error) { // This ensures that no two processes will be in runtime.refresh at once aliveLock.Lock() doRefresh := false + unLockFunc := aliveLock.Unlock defer func() { - if aliveLock.Locked() { - aliveLock.Unlock() + if unLockFunc != nil { + unLockFunc() } }() @@ -568,7 +565,8 @@ func makeRuntime(runtime *Runtime) (retErr error) { logrus.Debug("Invalid systemd user session for current user") } } - aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec. + unLockFunc() + unLockFunc = nil pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir) if err != nil { return fmt.Errorf("could not get pause process pid file path: %w", err) @@ -779,7 +777,7 @@ func (r *Runtime) Shutdown(force bool) error { // Shutdown all containers if --force is given if force { - ctrs, err := r.state.AllContainers() + ctrs, err := r.state.AllContainers(false) if err != nil { logrus.Errorf("Retrieving containers from database: %v", err) } else { @@ -835,7 +833,7 @@ func (r *Runtime) refresh(alivePath string) error { // Next refresh the state of all containers to recreate dirs and // namespaces, and all the pods to recreate cgroups. // Containers, pods, and volumes must also reacquire their locks. - ctrs, err := r.state.AllContainers() + ctrs, err := r.state.AllContainers(false) if err != nil { return fmt.Errorf("retrieving all containers from state: %w", err) } @@ -963,6 +961,10 @@ func (r *Runtime) StorageConfig() storage.StoreOptions { return r.storageConfig } +func (r *Runtime) GarbageCollect() error { + return r.store.GarbageCollect() +} + // RunRoot retrieves the current c/storage temporary directory in use by Libpod. func (r *Runtime) RunRoot() string { if r.store == nil { @@ -1019,8 +1021,8 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) { if !r.storageSet.GraphDriverNameSet && dbConfig.GraphDriver != "" { if r.storageConfig.GraphDriverName != dbConfig.GraphDriver && r.storageConfig.GraphDriverName != "" { - logrus.Errorf("User-selected graph driver %q overwritten by graph driver %q from database - delete libpod local files to resolve. May prevent use of images created by other tools", - r.storageConfig.GraphDriverName, dbConfig.GraphDriver) + logrus.Errorf("User-selected graph driver %q overwritten by graph driver %q from database - delete libpod local files (%q) to resolve. May prevent use of images created by other tools", + r.storageConfig.GraphDriverName, dbConfig.GraphDriver, r.storageConfig.GraphRoot) } r.storageConfig.GraphDriverName = dbConfig.GraphDriver } diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime_ctr.go b/vendor/github.com/containers/podman/v4/libpod/runtime_ctr.go index bb30078c..33e31c63 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime_ctr.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime_ctr.go @@ -118,6 +118,7 @@ func (r *Runtime) RenameContainer(ctx context.Context, ctr *Container, newName s return nil, err } + newName = strings.TrimPrefix(newName, "/") if newName == "" || !define.NameRegex.MatchString(newName) { return nil, define.RegexError } @@ -382,6 +383,13 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if err != nil { return nil, fmt.Errorf("retrieving pod %s cgroup: %w", pod.ID(), err) } + expectPodCgroup, err := ctr.expectPodCgroup() + if err != nil { + return nil, err + } + if expectPodCgroup && podCgroup == "" { + return nil, fmt.Errorf("pod %s cgroup is not set: %w", pod.ID(), define.ErrInternal) + } ctr.config.CgroupParent = podCgroup case rootless.IsRootless() && ctr.config.CgroupsMode != cgroupSplit: ctr.config.CgroupParent = SystemdDefaultRootlessCgroupParent @@ -488,16 +496,21 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai logrus.Debugf("Creating new volume %s for container", vol.Name) // The volume does not exist, so we need to create it. - volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())} + volOptions := []VolumeCreateOption{WithVolumeName(vol.Name)} if isAnonymous { volOptions = append(volOptions, withSetAnon()) } + needsChown := true + // If volume-opts are set parse and add driver opts. if len(vol.Options) > 0 { isDriverOpts := false driverOpts := make(map[string]string) for _, opts := range vol.Options { + if opts == "idmap" { + needsChown = false + } if strings.HasPrefix(opts, "volume-opt") { isDriverOpts = true driverOptKey, driverOptValue, err := util.ParseDriverOpts(opts) @@ -512,6 +525,13 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai volOptions = append(volOptions, parsedOptions...) } } + + if needsChown { + volOptions = append(volOptions, WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())) + } else { + volOptions = append(volOptions, WithVolumeNoChown()) + } + newVol, err := r.newVolume(ctx, false, volOptions...) if err != nil { return nil, fmt.Errorf("creating named volume %q: %w", vol.Name, err) @@ -571,7 +591,14 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai } else if err := r.state.AddContainer(ctr); err != nil { return nil, err } - ctr.newContainerEvent(events.Create) + + if ctr.runtime.config.Engine.EventsContainerCreateInspectData { + if err := ctr.newContainerEventWithInspectData(events.Create, true); err != nil { + return nil, err + } + } else { + ctr.newContainerEvent(events.Create) + } return ctr, nil } @@ -581,6 +608,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai // be removed also if and only if the container is the sole user // Otherwise, RemoveContainer will return an error if the container is running func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool, timeout *uint) error { + // NOTE: container will be locked down the road. return r.removeContainer(ctx, c, force, removeVolume, false, false, timeout) } @@ -660,11 +688,13 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo } if c.IsService() { - canStop, err := c.canStopServiceContainer() - if err != nil { - return err - } - if !canStop { + for _, id := range c.state.Service.Pods { + if _, err := c.runtime.LookupPod(id); err != nil { + if errors.Is(err, define.ErrNoSuchPod) { + continue + } + return err + } return fmt.Errorf("container %s is the service container of pod(s) %s and cannot be removed without removing the pod(s)", c.ID(), strings.Join(c.state.Service.Pods, ",")) } } @@ -712,7 +742,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo } // Check that the container's in a good state to be removed. - if c.state.State == define.ContainerStateRunning { + if c.ensureState(define.ContainerStateRunning, define.ContainerStateStopping) { time := c.StopTimeout() if timeout != nil { time = *timeout @@ -777,6 +807,16 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo } } + // Remove the container's CID file on container removal. + if cidFile, ok := c.config.Spec.Annotations[define.InspectAnnotationCIDFile]; ok { + if err := os.Remove(cidFile); err != nil { + if cleanupErr == nil { + cleanupErr = err + } else { + logrus.Errorf("Cleaning up CID file: %v", err) + } + } + } // Remove the container from the state if c.config.Pod != "" { // If we're removing the pod, the container will be evicted @@ -1007,7 +1047,7 @@ func (r *Runtime) RemoveDepend(ctx context.Context, rmCtr *Container, force bool return nil, err } for _, cID := range podContainerIDS { - rmReports = append(rmReports, &reports.RmReport{Id: cID, RawInput: cID}) + rmReports = append(rmReports, &reports.RmReport{Id: cID}) } return rmReports, nil } @@ -1035,7 +1075,7 @@ func (r *Runtime) RemoveDepend(ctx context.Context, rmCtr *Container, force bool rmReports = append(rmReports, reports...) } - report := reports.RmReport{Id: rmCtr.ID(), RawInput: rmCtr.ID()} + report := reports.RmReport{Id: rmCtr.ID()} report.Err = r.removeContainer(ctx, rmCtr, force, removeVolume, false, false, timeout) return append(rmReports, &report), nil } @@ -1076,16 +1116,17 @@ func (r *Runtime) LookupContainerID(idOrName string) (string, error) { return r.state.LookupContainerID(idOrName) } -// GetContainers retrieves all containers from the state +// GetContainers retrieves all containers from the state. +// If `loadState` is set, the containers' state will be loaded as well. // Filters can be provided which will determine what containers are included in // the output. Multiple filters are handled by ANDing their output, so only // containers matching all filters are returned -func (r *Runtime) GetContainers(filters ...ContainerFilter) ([]*Container, error) { +func (r *Runtime) GetContainers(loadState bool, filters ...ContainerFilter) ([]*Container, error) { if !r.valid { return nil, define.ErrRuntimeStopped } - ctrs, err := r.GetAllContainers() + ctrs, err := r.state.AllContainers(loadState) if err != nil { return nil, err } @@ -1108,7 +1149,7 @@ func (r *Runtime) GetContainers(filters ...ContainerFilter) ([]*Container, error // GetAllContainers is a helper function for GetContainers func (r *Runtime) GetAllContainers() ([]*Container, error) { - return r.state.AllContainers() + return r.state.AllContainers(false) } // GetRunningContainers is a helper function for GetContainers @@ -1117,7 +1158,7 @@ func (r *Runtime) GetRunningContainers() ([]*Container, error) { state, _ := c.State() return state == define.ContainerStateRunning } - return r.GetContainers(running) + return r.GetContainers(false, running) } // GetContainersByList is a helper function for GetContainers @@ -1191,7 +1232,7 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) ([]*reports.Pru return false } filterFuncs = append(filterFuncs, containerStateFilter) - delContainers, err := r.GetContainers(filterFuncs...) + delContainers, err := r.GetContainers(false, filterFuncs...) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime_img.go b/vendor/github.com/containers/podman/v4/libpod/runtime_img.go index d8e88ca5..fc1fc62f 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime_img.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime_img.go @@ -28,7 +28,7 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage if !r.valid { return define.ErrRuntimeStopped } - ctrs, err := r.state.AllContainers() + ctrs, err := r.state.AllContainers(false) if err != nil { return err } diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime_migrate.go b/vendor/github.com/containers/podman/v4/libpod/runtime_migrate.go index df1a1f1c..f279eaf9 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime_migrate.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime_migrate.go @@ -49,7 +49,7 @@ func (r *Runtime) migrate() error { return err } - allCtrs, err := r.state.AllContainers() + allCtrs, err := r.state.AllContainers(false) if err != nil { return err } diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime_renumber.go b/vendor/github.com/containers/podman/v4/libpod/runtime_renumber.go index ff70081d..f75e0e3e 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime_renumber.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime_renumber.go @@ -20,7 +20,7 @@ func (r *Runtime) renumberLocks() error { return err } - allCtrs, err := r.state.AllContainers() + allCtrs, err := r.state.AllContainers(false) if err != nil { return err } diff --git a/vendor/github.com/containers/podman/v4/libpod/runtime_volume_common.go b/vendor/github.com/containers/podman/v4/libpod/runtime_volume_common.go index b1de2be8..5c225803 100644 --- a/vendor/github.com/containers/podman/v4/libpod/runtime_volume_common.go +++ b/vendor/github.com/containers/podman/v4/libpod/runtime_volume_common.go @@ -53,12 +53,14 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti volume.config.CreatedTime = time.Now() // Check if volume with given name exists. - exists, err := r.state.HasVolume(volume.config.Name) - if err != nil { - return nil, fmt.Errorf("checking if volume with name %s exists: %w", volume.config.Name, err) - } - if exists { - return nil, fmt.Errorf("volume with name %s already exists: %w", volume.config.Name, define.ErrVolumeExists) + if !volume.ignoreIfExists { + exists, err := r.state.HasVolume(volume.config.Name) + if err != nil { + return nil, fmt.Errorf("checking if volume with name %s exists: %w", volume.config.Name, err) + } + if exists { + return nil, fmt.Errorf("volume with name %s already exists: %w", volume.config.Name, define.ErrVolumeExists) + } } // Plugin can be nil if driver is local, but that's OK - superfluous @@ -209,6 +211,13 @@ func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, opti // Add the volume to state if err := r.state.AddVolume(volume); err != nil { + if volume.ignoreIfExists && errors.Is(err, define.ErrVolumeExists) { + existingVolume, err := r.state.Volume(volume.config.Name) + if err != nil { + return nil, fmt.Errorf("reading volume from state: %w", err) + } + return existingVolume, nil + } return nil, fmt.Errorf("adding volume to state: %w", err) } defer volume.newVolumeEvent(events.Create) diff --git a/vendor/github.com/containers/podman/v4/libpod/service.go b/vendor/github.com/containers/podman/v4/libpod/service.go index a8928277..3304c73e 100644 --- a/vendor/github.com/containers/podman/v4/libpod/service.go +++ b/vendor/github.com/containers/podman/v4/libpod/service.go @@ -89,6 +89,9 @@ func (c *Container) canStopServiceContainer() (bool, error) { status, err := pod.GetPodStatus() if err != nil { + if errors.Is(err, define.ErrNoSuchPod) { + continue + } return false, err } @@ -112,6 +115,9 @@ func (p *Pod) maybeStopServiceContainer() error { serviceCtr, err := p.serviceContainer() if err != nil { + if errors.Is(err, define.ErrNoSuchCtr) { + return nil + } return fmt.Errorf("getting pod's service container: %w", err) } // Checking whether the service can be stopped must be done in @@ -129,7 +135,9 @@ func (p *Pod) maybeStopServiceContainer() error { } logrus.Debugf("Stopping service container %s", serviceCtr.ID()) if err := serviceCtr.Stop(); err != nil { - logrus.Errorf("Stopping service container %s: %v", serviceCtr.ID(), err) + if !errors.Is(err, define.ErrCtrStopped) { + logrus.Errorf("Stopping service container %s: %v", serviceCtr.ID(), err) + } } }) return nil @@ -163,13 +171,7 @@ func (p *Pod) maybeStartServiceContainer(ctx context.Context) error { // canRemoveServiceContainer returns true if all pods of the service are removed. // Note that the method acquires the container lock. -func (c *Container) canRemoveServiceContainerLocked() (bool, error) { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return false, err - } - +func (c *Container) canRemoveServiceContainer() (bool, error) { if !c.IsService() { return false, fmt.Errorf("internal error: checking service: container %s is not a service container", c.ID()) } @@ -188,6 +190,7 @@ func (c *Container) canRemoveServiceContainerLocked() (bool, error) { } // Checks whether the service container can be removed and does so. +// It also unlinks the pod from the service container. func (p *Pod) maybeRemoveServiceContainer() error { if !p.hasServiceContainer() { return nil @@ -195,6 +198,9 @@ func (p *Pod) maybeRemoveServiceContainer() error { serviceCtr, err := p.serviceContainer() if err != nil { + if errors.Is(err, define.ErrNoSuchCtr) { + return nil + } return fmt.Errorf("getting pod's service container: %w", err) } // Checking whether the service can be stopped must be done in @@ -202,9 +208,31 @@ func (p *Pod) maybeRemoveServiceContainer() error { // pod->container->servicePods hierarchy. p.runtime.queueWork(func() { logrus.Debugf("Pod %s has a service %s: checking if it can be removed", p.ID(), serviceCtr.ID()) - canRemove, err := serviceCtr.canRemoveServiceContainerLocked() + canRemove, err := func() (bool, error) { // Anonymous func for easy locking + serviceCtr.lock.Lock() + defer serviceCtr.lock.Unlock() + if err := serviceCtr.syncContainer(); err != nil { + return false, err + } + + // Unlink the pod from the service container. + servicePods := make([]string, 0, len(serviceCtr.state.Service.Pods)-1) + for _, id := range serviceCtr.state.Service.Pods { + if id != p.ID() { + servicePods = append(servicePods, id) + } + } + serviceCtr.state.Service.Pods = servicePods + if err := serviceCtr.save(); err != nil { + return false, err + } + + return serviceCtr.canRemoveServiceContainer() + }() if err != nil { - logrus.Errorf("Checking whether service of container %s can be removed: %v", serviceCtr.ID(), err) + if !errors.Is(err, define.ErrNoSuchCtr) { + logrus.Errorf("Checking whether service container %s can be removed: %v", serviceCtr.ID(), err) + } return } if !canRemove { @@ -213,7 +241,9 @@ func (p *Pod) maybeRemoveServiceContainer() error { timeout := uint(0) logrus.Debugf("Removing service container %s", serviceCtr.ID()) if err := p.runtime.RemoveContainer(context.Background(), serviceCtr, true, false, &timeout); err != nil { - logrus.Errorf("Removing service container %s: %v", serviceCtr.ID(), err) + if !errors.Is(err, define.ErrNoSuchCtr) { + logrus.Errorf("Removing service container %s: %v", serviceCtr.ID(), err) + } } }) return nil diff --git a/vendor/github.com/containers/podman/v4/libpod/state.go b/vendor/github.com/containers/podman/v4/libpod/state.go index 5fa8dc25..0499525e 100644 --- a/vendor/github.com/containers/podman/v4/libpod/state.go +++ b/vendor/github.com/containers/podman/v4/libpod/state.go @@ -96,14 +96,17 @@ type State interface { //nolint:interfacebloat // The container being checked must be part of the set namespace. ContainerInUse(ctr *Container) ([]string, error) // Retrieves all containers presently in state. + // If `loadState` is set, the containers' state will be loaded as well. // If a namespace is set, only containers within the namespace will be // returned. - AllContainers() ([]*Container, error) + AllContainers(loadState bool) ([]*Container, error) // Get networks the container is currently connected to. GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error) // Add the container to the given network with the given options NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error + // Modify the container network with the given options. + NetworkModify(ctr *Container, network string, opts types.PerNetworkOptions) error // Remove the container from the given network, removing all aliases for // the container in that network in the process. NetworkDisconnect(ctr *Container, network string) error diff --git a/vendor/github.com/containers/podman/v4/libpod/stats_freebsd.go b/vendor/github.com/containers/podman/v4/libpod/stats_freebsd.go index 6c036c78..e2ec9516 100644 --- a/vendor/github.com/containers/podman/v4/libpod/stats_freebsd.go +++ b/vendor/github.com/containers/podman/v4/libpod/stats_freebsd.go @@ -82,8 +82,8 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev // Handle case where the container is not in a network namespace if netStats != nil { - stats.NetInput = netStats.TxBytes - stats.NetOutput = netStats.RxBytes + stats.NetInput = netStats.RxBytes + stats.NetOutput = netStats.TxBytes } else { stats.NetInput = 0 stats.NetOutput = 0 @@ -96,9 +96,9 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev func (c *Container) getMemLimit() uint64 { memLimit := uint64(math.MaxUint64) - if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && - c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { - memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + resources := c.LinuxResources() + if resources != nil && resources.Memory != nil && resources.Memory.Limit != nil { + memLimit = uint64(*resources.Memory.Limit) } mi, err := system.ReadMemInfo() diff --git a/vendor/github.com/containers/podman/v4/libpod/stats_linux.go b/vendor/github.com/containers/podman/v4/libpod/stats_linux.go index ad8f33c9..5eb04493 100644 --- a/vendor/github.com/containers/podman/v4/libpod/stats_linux.go +++ b/vendor/github.com/containers/podman/v4/libpod/stats_linux.go @@ -72,8 +72,8 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev stats.PerCPU = cgroupStats.CpuStats.CpuUsage.PercpuUsage // Handle case where the container is not in a network namespace if netStats != nil { - stats.NetInput = netStats.TxBytes - stats.NetOutput = netStats.RxBytes + stats.NetInput = netStats.RxBytes + stats.NetOutput = netStats.TxBytes } else { stats.NetInput = 0 stats.NetOutput = 0 @@ -86,9 +86,9 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev func (c *Container) getMemLimit() uint64 { memLimit := uint64(math.MaxUint64) - if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && - c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { - memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + resources := c.LinuxResources() + if resources != nil && resources.Memory != nil && resources.Memory.Limit != nil { + memLimit = uint64(*resources.Memory.Limit) } si := &syscall.Sysinfo_t{} diff --git a/vendor/github.com/containers/podman/v4/libpod/util.go b/vendor/github.com/containers/podman/v4/libpod/util.go index c5a2b81b..1047a523 100644 --- a/vendor/github.com/containers/podman/v4/libpod/util.go +++ b/vendor/github.com/containers/podman/v4/libpod/util.go @@ -171,11 +171,12 @@ func queryPackageVersion(cmdArg ...string) string { func packageVersion(program string) string { // program is full path packagers := [][]string{ {"/usr/bin/rpm", "-q", "-f"}, - {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu - {"/usr/bin/pacman", "-Qo"}, // Arch - {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) - {"/usr/bin/equery", "b"}, // Gentoo (slow) - {"/sbin/apk", "info", "-W"}, // Alpine + {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu + {"/usr/bin/pacman", "-Qo"}, // Arch + {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) + {"/usr/bin/equery", "b"}, // Gentoo (slow) + {"/sbin/apk", "info", "-W"}, // Alpine + {"/usr/local/sbin/pkg", "which", "-q"}, // FreeBSD } for _, cmd := range packagers { @@ -311,7 +312,12 @@ func writeHijackHeader(r *http.Request, conn io.Writer) { } // Convert OCICNI port bindings into Inspect-formatted port bindings. -func makeInspectPortBindings(bindings []types.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort { +func makeInspectPortBindings(bindings []types.PortMapping) map[string][]define.InspectHostPort { + return makeInspectPorts(bindings, nil) +} + +// Convert OCICNI port bindings into Inspect-formatted port bindings with exposed, but not bound ports set to nil. +func makeInspectPorts(bindings []types.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort { portBindings := make(map[string][]define.InspectHostPort) for _, port := range bindings { protocols := strings.Split(port.Protocol, ",") diff --git a/vendor/github.com/containers/podman/v4/libpod/volume.go b/vendor/github.com/containers/podman/v4/libpod/volume.go index 2d4ea428..e7606201 100644 --- a/vendor/github.com/containers/podman/v4/libpod/volume.go +++ b/vendor/github.com/containers/podman/v4/libpod/volume.go @@ -17,10 +17,11 @@ type Volume struct { config *VolumeConfig state *VolumeState - valid bool - plugin *plugin.VolumePlugin - runtime *Runtime - lock lock.Locker + ignoreIfExists bool + valid bool + plugin *plugin.VolumePlugin + runtime *Runtime + lock lock.Locker } // VolumeConfig holds the volume's immutable configuration. @@ -280,3 +281,7 @@ func (v *Volume) Unmount() error { defer v.lock.Unlock() return v.unmount(false) } + +func (v *Volume) NeedsMount() bool { + return v.needsMount() +} diff --git a/vendor/github.com/containers/podman/v4/pkg/annotations/annotations.go b/vendor/github.com/containers/podman/v4/pkg/annotations/annotations.go index a22222f1..5d3cb992 100644 --- a/vendor/github.com/containers/podman/v4/pkg/annotations/annotations.go +++ b/vendor/github.com/containers/podman/v4/pkg/annotations/annotations.go @@ -120,3 +120,15 @@ const ( // ContainerManagerLibpod indicates that libpod created and manages the // container. const ContainerManagerLibpod = "libpod" + +// IsReservedAnnotation returns true if the specified value corresponds to an +// already reserved annotation that Podman sets during container creation. +func IsReservedAnnotation(value string) bool { + switch value { + case Annotations, ContainerID, ContainerName, ContainerType, Created, HostName, CgroupParent, IP, NamespaceOptions, SeccompProfilePath, Image, ImageName, ImageRef, KubeName, PortMappings, Labels, LogPath, Metadata, Name, Namespace, PrivilegedRuntime, ResolvPath, HostnamePath, SandboxID, SandboxName, ShmPath, MountPoint, RuntimeHandler, TTY, Stdin, StdinOnce, Volumes, HostNetwork, CNIResult, ContainerManager: + return true + + default: + return false + } +} diff --git a/vendor/github.com/containers/podman/v4/pkg/auth/auth.go b/vendor/github.com/containers/podman/v4/pkg/auth/auth.go index 52a632b3..2a675e2a 100644 --- a/vendor/github.com/containers/podman/v4/pkg/auth/auth.go +++ b/vendor/github.com/containers/podman/v4/pkg/auth/auth.go @@ -227,7 +227,7 @@ func encodeMultiAuthConfigs(authConfigs map[string]types.DockerAuthConfig) (stri } // authConfigsToAuthFile stores the specified auth configs in a temporary files -// and returns its path. The file can later be used an auth file for contacting +// and returns its path. The file can later be used as an auth file for contacting // one or more container registries. If tmpDir is empty, the system's default // TMPDIR will be used. func authConfigsToAuthFile(authConfigs map[string]types.DockerAuthConfig) (string, error) { @@ -284,7 +284,7 @@ func normalizeAuthFileKey(authFileKey string) string { // dockerAuthToImageAuth converts a docker auth config to one we're using // internally from c/image. Note that the Docker types look slightly // different, so we need to convert to be extra sure we're not running into -// undesired side-effects when unmarhalling directly to our types. +// undesired side-effects when unmarshalling directly to our types. func dockerAuthToImageAuth(authConfig dockerAPITypes.AuthConfig) types.DockerAuthConfig { return types.DockerAuthConfig{ Username: authConfig.Username, diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/connection.go b/vendor/github.com/containers/podman/v4/pkg/bindings/connection.go index a3677d39..7bd1ff48 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/connection.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/connection.go @@ -17,6 +17,7 @@ import ( "github.com/containers/common/pkg/ssh" "github.com/containers/podman/v4/version" "github.com/sirupsen/logrus" + "golang.org/x/net/proxy" ) type APIResponse struct { @@ -91,9 +92,12 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string, var connection Connection switch _url.Scheme { case "ssh": - port, err := strconv.Atoi(_url.Port()) - if err != nil { - return nil, err + port := 22 + if _url.Port() != "" { + port, err = strconv.Atoi(_url.Port()) + if err != nil { + return nil, err + } } conn, err := ssh.Dial(&ssh.ConnectionDialOptions{ Host: uri, @@ -123,7 +127,11 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string, if !strings.HasPrefix(uri, "tcp://") { return nil, errors.New("tcp URIs should begin with tcp://") } - connection = tcpClient(_url) + conn, err := tcpClient(_url) + if err != nil { + return nil, err + } + connection = conn default: return nil, fmt.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) } @@ -140,19 +148,45 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string, return ctx, nil } -func tcpClient(_url *url.URL) Connection { +func tcpClient(_url *url.URL) (Connection, error) { connection := Connection{ URI: _url, } + dialContext := func(ctx context.Context, _, _ string) (net.Conn, error) { + return net.Dial("tcp", _url.Host) + } + // use proxy if env `CONTAINER_PROXY` set + if proxyURI, found := os.LookupEnv("CONTAINER_PROXY"); found { + proxyURL, err := url.Parse(proxyURI) + if err != nil { + return connection, fmt.Errorf("value of CONTAINER_PROXY is not a valid url: %s: %w", proxyURI, err) + } + proxyDialer, err := proxy.FromURL(proxyURL, proxy.Direct) + if err != nil { + return connection, fmt.Errorf("unable to dial to proxy %s, %w", proxyURI, err) + } + dialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { + logrus.Debugf("use proxy %s, but proxy dialer does not support dial timeout", proxyURI) + return proxyDialer.Dial("tcp", _url.Host) + } + if f, ok := proxyDialer.(proxy.ContextDialer); ok { + dialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { + // the default tcp dial timeout seems to be 75s, podman-remote will retry 3 times before exit. + // here we change proxy dial timeout to 3s + logrus.Debugf("use proxy %s with dial timeout 3s", proxyURI) + ctx, cancel := context.WithTimeout(ctx, time.Second*3) + defer cancel() // It's safe to cancel, `f.DialContext` only use ctx for returning the Conn, not the lifetime of the Conn. + return f.DialContext(ctx, "tcp", _url.Host) + } + } + } connection.Client = &http.Client{ Transport: &http.Transport{ - DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { - return net.Dial("tcp", _url.Host) - }, + DialContext: dialContext, DisableCompression: true, }, } - return connection + return connection, nil } // pingNewConnection pings to make sure the RESTFUL service is up diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/images/build.go b/vendor/github.com/containers/podman/v4/pkg/bindings/images/build.go index f8552cdd..b58153a5 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/images/build.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/images/build.go @@ -13,7 +13,6 @@ import ( "net/url" "os" "path/filepath" - "regexp" "runtime" "strconv" "strings" @@ -25,6 +24,7 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/storage/pkg/fileutils" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/regexp" "github.com/docker/go-units" "github.com/hashicorp/go-multierror" jsoniter "github.com/json-iterator/go" @@ -36,9 +36,7 @@ type devino struct { Ino uint64 } -var ( - iidRegex = regexp.MustCompile(`^[0-9a-f]{12}`) -) +var iidRegex = regexp.Delayed(`^[0-9a-f]{12}`) // Build creates an image using a containerfile reference func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) { @@ -231,7 +229,15 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Set("manifest", options.Manifest) } if options.CacheFrom != nil { - params.Set("cachefrom", options.CacheFrom.String()) + cacheFrom := []string{} + for _, cacheSrc := range options.CacheFrom { + cacheFrom = append(cacheFrom, cacheSrc.String()) + } + cacheFromJSON, err := jsoniter.MarshalToString(cacheFrom) + if err != nil { + return nil, err + } + params.Set("cachefrom", cacheFromJSON) } switch options.SkipUnusedStages { @@ -242,7 +248,15 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } if options.CacheTo != nil { - params.Set("cacheto", options.CacheTo.String()) + cacheTo := []string{} + for _, cacheSrc := range options.CacheTo { + cacheTo = append(cacheTo, cacheSrc.String()) + } + cacheToJSON, err := jsoniter.MarshalToString(cacheTo) + if err != nil { + return nil, err + } + params.Set("cacheto", cacheToJSON) } if int64(options.CacheTTL) != 0 { params.Set("cachettl", options.CacheTTL.String()) @@ -290,6 +304,15 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Add("platform", platform) } } + + for _, volume := range options.CommonBuildOpts.Volumes { + params.Add("volume", volume) + } + + for _, group := range options.GroupAdd { + params.Add("groupadd", group) + } + var err error var contextDir string if contextDir, err = filepath.EvalSymlinks(options.ContextDirectory); err == nil { @@ -626,23 +649,27 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { return err } + separator := string(filepath.Separator) // check if what we are given is an empty dir, if so then continue w/ it. Else return. // if we are given a file or a symlink, we do not want to exclude it. - if d.IsDir() && s == path { - var p *os.File - p, err = os.Open(path) - if err != nil { - return err - } - defer p.Close() - _, err = p.Readdir(1) - if err != io.EOF { - return nil // non empty root dir, need to return - } else if err != nil { - logrus.Errorf("While reading directory %v: %v", path, err) + if s == path { + separator = "" + if d.IsDir() { + var p *os.File + p, err = os.Open(path) + if err != nil { + return err + } + defer p.Close() + _, err = p.Readdir(1) + if err != io.EOF { + return nil // non empty root dir, need to return + } else if err != nil { + logrus.Errorf("While reading directory %v: %v", path, err) + } } } - name := filepath.ToSlash(strings.TrimPrefix(path, s+string(filepath.Separator))) + name := filepath.ToSlash(strings.TrimPrefix(path, s+separator)) excluded, err := pm.Matches(name) //nolint:staticcheck if err != nil { diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/kube.go b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/kube.go index 1b9f888e..731a35ed 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/kube.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/kube.go @@ -67,7 +67,7 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e return &report, nil } -func Down(ctx context.Context, path string) (*entities.KubePlayReport, error) { +func Down(ctx context.Context, path string, options DownOptions) (*entities.KubePlayReport, error) { f, err := os.Open(path) if err != nil { return nil, err @@ -78,17 +78,22 @@ func Down(ctx context.Context, path string) (*entities.KubePlayReport, error) { } }() - return DownWithBody(ctx, f) + return DownWithBody(ctx, f, options) } -func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport, error) { +func DownWithBody(ctx context.Context, body io.Reader, options DownOptions) (*entities.KubePlayReport, error) { var report entities.KubePlayReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", nil, nil) + params, err := options.ToParams() + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", params, nil) if err != nil { return nil, err } @@ -102,3 +107,41 @@ func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport func Generate(ctx context.Context, nameOrIDs []string, options generate.KubeOptions) (*entities.GenerateKubeReport, error) { return generate.Kube(ctx, nameOrIDs, &options) } + +func Apply(ctx context.Context, path string, options *ApplyOptions) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer func() { + if err := f.Close(); err != nil { + logrus.Warn(err) + } + }() + + return ApplyWithBody(ctx, f, options) +} + +func ApplyWithBody(ctx context.Context, body io.Reader, options *ApplyOptions) error { + if options == nil { + options = new(ApplyOptions) + } + + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + + params, err := options.ToParams() + if err != nil { + return err + } + + response, err := conn.DoRequest(ctx, body, http.MethodPost, "/kube/apply", params, nil) + if err != nil { + return err + } + defer response.Body.Close() + + return nil +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types.go b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types.go index 93057972..8cd7c792 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types.go @@ -46,4 +46,32 @@ type PlayOptions struct { Start *bool // Userns - define the user namespace to use. Userns *string + // Force - remove volumes on --down + Force *bool + // PublishPorts - configure how to expose ports configured inside the K8S YAML file + PublishPorts []string +} + +// ApplyOptions are optional options for applying kube YAML files to a k8s cluster +// +//go:generate go run ../generator/generator.go ApplyOptions +type ApplyOptions struct { + // Kubeconfig - path to the cluster's kubeconfig file. + Kubeconfig *string + // Namespace - namespace to deploy the workload in on the cluster. + Namespace *string + // CACertFile - the path to the CA cert file for the Kubernetes cluster. + CACertFile *string + // File - the path to the Kubernetes yaml to deploy. + File *string + // Service - creates a service for the container being deployed. + Service *bool +} + +// DownOptions are optional options for tearing down kube YAML files to a k8s cluster +// +//go:generate go run ../generator/generator.go DownOptions +type DownOptions struct { + // Force - remove volumes on --down + Force *bool } diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_apply_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_apply_options.go new file mode 100644 index 00000000..9fb49594 --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_apply_options.go @@ -0,0 +1,93 @@ +// Code generated by go generate; DO NOT EDIT. +package kube + +import ( + "net/url" + + "github.com/containers/podman/v4/pkg/bindings/internal/util" +) + +// Changed returns true if named field has been set +func (o *ApplyOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams formats struct fields to be passed to API service +func (o *ApplyOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithKubeconfig set field Kubeconfig to given value +func (o *ApplyOptions) WithKubeconfig(value string) *ApplyOptions { + o.Kubeconfig = &value + return o +} + +// GetKubeconfig returns value of field Kubeconfig +func (o *ApplyOptions) GetKubeconfig() string { + if o.Kubeconfig == nil { + var z string + return z + } + return *o.Kubeconfig +} + +// WithNamespace set field Namespace to given value +func (o *ApplyOptions) WithNamespace(value string) *ApplyOptions { + o.Namespace = &value + return o +} + +// GetNamespace returns value of field Namespace +func (o *ApplyOptions) GetNamespace() string { + if o.Namespace == nil { + var z string + return z + } + return *o.Namespace +} + +// WithCACertFile set field CACertFile to given value +func (o *ApplyOptions) WithCACertFile(value string) *ApplyOptions { + o.CACertFile = &value + return o +} + +// GetCACertFile returns value of field CACertFile +func (o *ApplyOptions) GetCACertFile() string { + if o.CACertFile == nil { + var z string + return z + } + return *o.CACertFile +} + +// WithFile set field File to given value +func (o *ApplyOptions) WithFile(value string) *ApplyOptions { + o.File = &value + return o +} + +// GetFile returns value of field File +func (o *ApplyOptions) GetFile() string { + if o.File == nil { + var z string + return z + } + return *o.File +} + +// WithService set field Service to given value +func (o *ApplyOptions) WithService(value bool) *ApplyOptions { + o.Service = &value + return o +} + +// GetService returns value of field Service +func (o *ApplyOptions) GetService() bool { + if o.Service == nil { + var z bool + return z + } + return *o.Service +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_down_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_down_options.go new file mode 100644 index 00000000..99ce3abe --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_down_options.go @@ -0,0 +1,33 @@ +// Code generated by go generate; DO NOT EDIT. +package kube + +import ( + "net/url" + + "github.com/containers/podman/v4/pkg/bindings/internal/util" +) + +// Changed returns true if named field has been set +func (o *DownOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams formats struct fields to be passed to API service +func (o *DownOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithForce set field Force to given value +func (o *DownOptions) WithForce(value bool) *DownOptions { + o.Force = &value + return o +} + +// GetForce returns value of field Force +func (o *DownOptions) GetForce() bool { + if o.Force == nil { + var z bool + return z + } + return *o.Force +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_play_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_play_options.go index cdc2e9dd..522fac85 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_play_options.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/kube/types_play_options.go @@ -287,3 +287,33 @@ func (o *PlayOptions) GetUserns() string { } return *o.Userns } + +// WithForce set field Force to given value +func (o *PlayOptions) WithForce(value bool) *PlayOptions { + o.Force = &value + return o +} + +// GetForce returns value of field Force +func (o *PlayOptions) GetForce() bool { + if o.Force == nil { + var z bool + return z + } + return *o.Force +} + +// WithPublishPorts set field PublishPorts to given value +func (o *PlayOptions) WithPublishPorts(value []string) *PlayOptions { + o.PublishPorts = value + return o +} + +// GetPublishPorts returns value of field PublishPorts +func (o *PlayOptions) GetPublishPorts() []string { + if o.PublishPorts == nil { + var z []string + return z + } + return o.PublishPorts +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/manifests.go b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/manifests.go index ed605bd7..4c40b2dc 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/manifests.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/manifests.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "github.com/containers/common/libimage" "github.com/containers/image/v5/manifest" imageTypes "github.com/containers/image/v5/types" "github.com/containers/podman/v4/pkg/auth" @@ -71,13 +72,27 @@ func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, err } // Inspect returns a manifest list for a given name. -func Inspect(ctx context.Context, name string, _ *InspectOptions) (*manifest.Schema2List, error) { +func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } + if options == nil { + options = new(InspectOptions) + } - response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", nil, nil, name) + params, err := options.ToParams() + if err != nil { + return nil, err + } + // SkipTLSVerify is special. We need to delete the param added by + // ToParams() and change the key and flip the bool + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) + } + + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", params, nil, name) if err != nil { return nil, err } @@ -87,6 +102,39 @@ func Inspect(ctx context.Context, name string, _ *InspectOptions) (*manifest.Sch return &list, response.Process(&list) } +// InspectListData returns a manifest list for a given name. +// Contains exclusive field like `annotations` which is only +// present in OCI spec and not in docker image spec. +func InspectListData(ctx context.Context, name string, options *InspectOptions) (*libimage.ManifestListData, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + if options == nil { + options = new(InspectOptions) + } + + params, err := options.ToParams() + if err != nil { + return nil, err + } + // SkipTLSVerify is special. We need to delete the param added by + // ToParams() and change the key and flip the bool + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) + } + + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", params, nil, name) + if err != nil { + return nil, err + } + defer response.Body.Close() + + var list libimage.ManifestListData + return &list, response.Process(&list) +} + // Add adds a manifest to a given manifest list. Additional options for the manifest // can also be specified. The ID of the new manifest list is returned as a string func Add(ctx context.Context, name string, options *AddOptions) (string, error) { diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types.go b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types.go index 45c15f95..d0485e93 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types.go @@ -4,6 +4,7 @@ package manifests // //go:generate go run ../generator/generator.go InspectOptions type InspectOptions struct { + SkipTLSVerify *bool } // CreateOptions are optional options for creating manifests diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types_inspect_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types_inspect_options.go index f5a2df4d..fb0c397a 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types_inspect_options.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/manifests/types_inspect_options.go @@ -16,3 +16,18 @@ func (o *InspectOptions) Changed(fieldName string) bool { func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } + +// WithSkipTLSVerify set field SkipTLSVerify to given value +func (o *InspectOptions) WithSkipTLSVerify(value bool) *InspectOptions { + o.SkipTLSVerify = &value + return o +} + +// GetSkipTLSVerify returns value of field SkipTLSVerify +func (o *InspectOptions) GetSkipTLSVerify() bool { + if o.SkipTLSVerify == nil { + var z bool + return z + } + return *o.SkipTLSVerify +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/network/network.go b/vendor/github.com/containers/podman/v4/pkg/bindings/network/network.go index 83641f67..79d26cbf 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/network/network.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/network/network.go @@ -3,6 +3,7 @@ package network import ( "context" "net/http" + "net/url" "strings" "github.com/containers/common/libnetwork/types" @@ -11,13 +12,26 @@ import ( jsoniter "github.com/json-iterator/go" ) -// Create makes a new CNI network configuration +// Create makes a new network configuration func Create(ctx context.Context, network *types.Network) (types.Network, error) { + return CreateWithOptions(ctx, network, nil) +} + +func CreateWithOptions(ctx context.Context, network *types.Network, extraCreateOptions *ExtraCreateOptions) (types.Network, error) { var report types.Network conn, err := bindings.GetClient(ctx) if err != nil { return report, err } + + var params url.Values + if extraCreateOptions != nil { + params, err = extraCreateOptions.ToParams() + if err != nil { + return report, err + } + } + // create empty network if the caller did not provide one if network == nil { network = &types.Network{} @@ -27,7 +41,7 @@ func Create(ctx context.Context, network *types.Network) (types.Network, error) return report, err } reader := strings.NewReader(networkConfig) - response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/create", nil, nil) + response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/create", params, nil) if err != nil { return report, err } @@ -36,7 +50,26 @@ func Create(ctx context.Context, network *types.Network) (types.Network, error) return report, response.Process(&report) } -// Inspect returns low level information about a CNI network configuration +// Updates an existing netavark network config +func Update(ctx context.Context, netNameOrID string, options *UpdateOptions) error { + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + networkConfig, err := jsoniter.MarshalToString(options) + if err != nil { + return err + } + reader := strings.NewReader(networkConfig) + response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/%s/update", nil, nil, netNameOrID) + if err != nil { + return err + } + defer response.Body.Close() + return response.Process(nil) +} + +// Inspect returns information about a network configuration func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Network, error) { var net types.Network conn, err := bindings.GetClient(ctx) @@ -52,7 +85,7 @@ func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Net return net, response.Process(&net) } -// Remove deletes a defined CNI network configuration by name. The optional force boolean +// Remove deletes a defined network configuration by name. The optional force boolean // will remove all containers associated with the network when set to true. A slice // of NetworkRemoveReports are returned. func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*entities.NetworkRmReport, error) { @@ -77,7 +110,7 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*en return reports, response.Process(&reports) } -// List returns a summary of all CNI network configurations +// List returns a summary of all network configurations func List(ctx context.Context, options *ListOptions) ([]types.Network, error) { var netList []types.Network if options == nil { @@ -178,7 +211,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, return response.IsSuccess(), nil } -// Prune removes unused CNI networks +// Prune removes unused networks func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPruneReport, error) { if options == nil { options = new(PruneOptions) diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/network/types.go b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types.go index a3d69426..d99f7841 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/network/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types.go @@ -58,6 +58,14 @@ type ListOptions struct { Filters map[string][]string } +// NetworkUpdateOptions describes options to update a network +// +//go:generate go run ../generator/generator.go UpdateOptions +type UpdateOptions struct { + AddDNSServers []string `json:"adddnsservers"` + RemoveDNSServers []string `json:"removednsservers"` +} + // DisconnectOptions are optional options for disconnecting // containers from a network // @@ -76,7 +84,7 @@ type ExistsOptions struct { } // PruneOptions are optional options for removing unused -// CNI networks +// networks // //go:generate go run ../generator/generator.go PruneOptions type PruneOptions struct { @@ -84,3 +92,12 @@ type PruneOptions struct { // specific on choosing Filters map[string][]string } + +// ExtraCreateOptions are optional additional configuration flags for creating Networks +// that are not part of the network configuration +// +//go:generate go run ../generator/generator.go ExtraCreateOptions +type ExtraCreateOptions struct { + // IgnoreIfExists if true, do not fail if the network already exists + IgnoreIfExists *bool `schema:"ignoreIfExists"` +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_extracreate_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_extracreate_options.go new file mode 100644 index 00000000..f12adf6b --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_extracreate_options.go @@ -0,0 +1,33 @@ +// Code generated by go generate; DO NOT EDIT. +package network + +import ( + "net/url" + + "github.com/containers/podman/v4/pkg/bindings/internal/util" +) + +// Changed returns true if named field has been set +func (o *ExtraCreateOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams formats struct fields to be passed to API service +func (o *ExtraCreateOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithIgnoreIfExists set field IgnoreIfExists to given value +func (o *ExtraCreateOptions) WithIgnoreIfExists(value bool) *ExtraCreateOptions { + o.IgnoreIfExists = &value + return o +} + +// GetIgnoreIfExists returns value of field IgnoreIfExists +func (o *ExtraCreateOptions) GetIgnoreIfExists() bool { + if o.IgnoreIfExists == nil { + var z bool + return z + } + return *o.IgnoreIfExists +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_update_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_update_options.go new file mode 100644 index 00000000..2cc9d50d --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/network/types_update_options.go @@ -0,0 +1,48 @@ +// Code generated by go generate; DO NOT EDIT. +package network + +import ( + "net/url" + + "github.com/containers/podman/v4/pkg/bindings/internal/util" +) + +// Changed returns true if named field has been set +func (o *UpdateOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams formats struct fields to be passed to API service +func (o *UpdateOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithAddDNSServers set field AddDNSServers to given value +func (o *UpdateOptions) WithAddDNSServers(value []string) *UpdateOptions { + o.AddDNSServers = value + return o +} + +// GetAddDNSServers returns value of field AddDNSServers +func (o *UpdateOptions) GetAddDNSServers() []string { + if o.AddDNSServers == nil { + var z []string + return z + } + return o.AddDNSServers +} + +// WithRemoveDNSServers set field RemoveDNSServers to given value +func (o *UpdateOptions) WithRemoveDNSServers(value []string) *UpdateOptions { + o.RemoveDNSServers = value + return o +} + +// GetRemoveDNSServers returns value of field RemoveDNSServers +func (o *UpdateOptions) GetRemoveDNSServers() []string { + if o.RemoveDNSServers == nil { + var z []string + return z + } + return o.RemoveDNSServers +} diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/play/play.go b/vendor/github.com/containers/podman/v4/pkg/bindings/play/play.go index d5d64913..803349b8 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/play/play.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/play/play.go @@ -18,10 +18,10 @@ func KubeWithBody(ctx context.Context, body io.Reader, options *KubeOptions) (*e return kube.PlayWithBody(ctx, body, options) } -func Down(ctx context.Context, path string) (*entities.PlayKubeReport, error) { - return kube.Down(ctx, path) +func Down(ctx context.Context, path string, options kube.DownOptions) (*entities.PlayKubeReport, error) { + return kube.Down(ctx, path, options) } -func DownWithBody(ctx context.Context, body io.Reader) (*entities.PlayKubeReport, error) { - return kube.DownWithBody(ctx, body) +func DownWithBody(ctx context.Context, body io.Reader, options kube.DownOptions) (*entities.PlayKubeReport, error) { + return kube.DownWithBody(ctx, body, options) } diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/pods/pods.go b/vendor/github.com/containers/podman/v4/pkg/bindings/pods/pods.go index 6b454635..47befb09 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/pods/pods.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/pods/pods.go @@ -188,7 +188,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*en return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } -// Remove deletes a Pod from from local storage. The optional force parameter denotes +// Remove deletes a Pod from local storage. The optional force parameter denotes // that the Pod can be removed even if in a running state. func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.PodRmReport, error) { var report entities.PodRmReport diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/system/types.go b/vendor/github.com/containers/podman/v4/pkg/bindings/system/types.go index 9efd2800..89e093f6 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/system/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/system/types.go @@ -14,9 +14,10 @@ type EventsOptions struct { // //go:generate go run ../generator/generator.go PruneOptions type PruneOptions struct { - All *bool - Filters map[string][]string - Volumes *bool + All *bool + Filters map[string][]string + Volumes *bool + External *bool } // VersionOptions are optional options for getting version info diff --git a/vendor/github.com/containers/podman/v4/pkg/bindings/system/types_prune_options.go b/vendor/github.com/containers/podman/v4/pkg/bindings/system/types_prune_options.go index 47805feb..d0049852 100644 --- a/vendor/github.com/containers/podman/v4/pkg/bindings/system/types_prune_options.go +++ b/vendor/github.com/containers/podman/v4/pkg/bindings/system/types_prune_options.go @@ -61,3 +61,18 @@ func (o *PruneOptions) GetVolumes() bool { } return *o.Volumes } + +// WithExternal set field External to given value +func (o *PruneOptions) WithExternal(value bool) *PruneOptions { + o.External = &value + return o +} + +// GetExternal returns value of field External +func (o *PruneOptions) GetExternal() bool { + if o.External == nil { + var z bool + return z + } + return *o.External +} diff --git a/vendor/github.com/containers/podman/v4/pkg/checkpoint/checkpoint_restore.go b/vendor/github.com/containers/podman/v4/pkg/checkpoint/checkpoint_restore.go index 248b9cdb..f4613635 100644 --- a/vendor/github.com/containers/podman/v4/pkg/checkpoint/checkpoint_restore.go +++ b/vendor/github.com/containers/podman/v4/pkg/checkpoint/checkpoint_restore.go @@ -81,18 +81,6 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt } } - if restoreOptions.IgnoreStaticIP || restoreOptions.IgnoreStaticMAC { - for net, opts := range ctrConfig.Networks { - if restoreOptions.IgnoreStaticIP { - opts.StaticIPs = nil - } - if restoreOptions.IgnoreStaticMAC { - opts.StaticMAC = nil - } - ctrConfig.Networks[net] = opts - } - } - ctrID := ctrConfig.ID newName := false @@ -117,7 +105,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt // According to podman pod create a pod can share the following namespaces: // cgroup, ipc, net, pid, uts - // Let's make sure we a restoring into a pod with the same shared namespaces. + // Let's make sure we are restoring into a pod with the same shared namespaces. pod, err := runtime.LookupPod(ctrConfig.Pod) if err != nil { return nil, fmt.Errorf("pod %q cannot be retrieved: %w", ctrConfig.Pod, err) @@ -128,7 +116,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt return nil, fmt.Errorf("cannot retrieve infra container from pod %q: %w", ctrConfig.Pod, err) } - // If a namespaces was shared (!= "") it needs to be set to the new infrastructure container + // If a namespace was shared (!= "") it needs to be set to the new infrastructure container. // If the infrastructure container does not share the same namespaces as the to be restored // container we abort. if ctrConfig.IPCNsCtr != "" { diff --git a/vendor/github.com/containers/podman/v4/pkg/checkpoint/crutils/checkpoint_restore_utils.go b/vendor/github.com/containers/podman/v4/pkg/checkpoint/crutils/checkpoint_restore_utils.go index b9db9562..4e97d35a 100644 --- a/vendor/github.com/containers/podman/v4/pkg/checkpoint/crutils/checkpoint_restore_utils.go +++ b/vendor/github.com/containers/podman/v4/pkg/checkpoint/crutils/checkpoint_restore_utils.go @@ -10,12 +10,12 @@ import ( "path/filepath" metadata "github.com/checkpoint-restore/checkpointctl/lib" - "github.com/checkpoint-restore/go-criu/v5/stats" + "github.com/checkpoint-restore/go-criu/v6/stats" "github.com/containers/storage/pkg/archive" "github.com/opencontainers/selinux/go-selinux/label" ) -// This file mainly exist to make the checkpoint/restore functions +// This file mainly exists to make the checkpoint/restore functions // available for other users. One possible candidate would be CRI-O. // CRImportCheckpointWithoutConfig imports the checkpoint archive (input) @@ -207,7 +207,7 @@ func CRCreateFileWithLabel(directory, fileName, fileLabel string) error { // CRRuntimeSupportsCheckpointRestore tests if the given runtime at 'runtimePath' // supports checkpointing. The checkpoint restore interface has no definition -// but crun implements all commands just as runc does. Whathh runc does it the +// but crun implements all commands just as runc does. What runc does is the // official definition of the checkpoint/restore interface. func CRRuntimeSupportsCheckpointRestore(runtimePath string) bool { // Check if the runtime implements checkpointing. Currently only diff --git a/vendor/github.com/containers/podman/v4/pkg/criu/criu_linux.go b/vendor/github.com/containers/podman/v4/pkg/criu/criu_linux.go index c28e23fd..ee1de859 100644 --- a/vendor/github.com/containers/podman/v4/pkg/criu/criu_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/criu/criu_linux.go @@ -4,8 +4,8 @@ package criu import ( - "github.com/checkpoint-restore/go-criu/v5" - "github.com/checkpoint-restore/go-criu/v5/rpc" + "github.com/checkpoint-restore/go-criu/v6" + "github.com/checkpoint-restore/go-criu/v6/rpc" "google.golang.org/protobuf/proto" ) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/apply.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/apply.go new file mode 100644 index 00000000..e6d0775e --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/apply.go @@ -0,0 +1,21 @@ +package entities + +var ( + TypePVC = "PersistentVolumeClaim" + TypePod = "Pod" + TypeService = "Service" +) + +// ApplyOptions controls the deployment of kube yaml files to a Kubernetes Cluster +type ApplyOptions struct { + // Kubeconfig - path to the cluster's kubeconfig file. + Kubeconfig string + // Namespace - namespace to deploy the workload in on the cluster. + Namespace string + // CACertFile - the path to the CA cert file for the Kubernetes cluster. + CACertFile string + // File - the path to the Kubernetes yaml to deploy. + File string + // Service - creates a service for the container being deployed. + Service bool +} diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/containers.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/containers.go index 47225f25..940e6b08 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/containers.go @@ -52,11 +52,11 @@ type ContainerRunlabelReport struct{} type WaitOptions struct { Condition []define.ContainerStatus Interval time.Duration + Ignore bool Latest bool } type WaitReport struct { - Id string //nolint:revive,stylecheck Error error ExitCode int32 } @@ -181,7 +181,7 @@ type CommitReport struct { } type ContainerExportOptions struct { - Output string + Output io.Writer } type CheckpointOptions struct { @@ -204,7 +204,7 @@ type CheckpointOptions struct { type CheckpointReport struct { Err error `json:"-"` Id string `json:"Id"` //nolint:revive,stylecheck - RawInput string `json:"RawInput"` + RawInput string `json:"-"` RuntimeDuration int64 `json:"runtime_checkpoint_duration"` CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } @@ -231,7 +231,7 @@ type RestoreOptions struct { type RestoreReport struct { Err error `json:"-"` Id string `json:"Id"` //nolint:revive,stylecheck - RawInput string `json:"RawInput"` + RawInput string `json:"-"` RuntimeDuration int64 `json:"runtime_restore_duration"` CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine.go index d0d439a1..aa5ad692 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine.go @@ -30,29 +30,30 @@ func (m EngineMode) String() string { // PodmanConfig combines the defaults and settings from the file system with the // flags given in os.Args. Some runtime state is also stored here. type PodmanConfig struct { - *config.Config *pflag.FlagSet - DockerConfig string // Used for Docker compatibility - CgroupUsage string // rootless code determines Usage message - ConmonPath string // --conmon flag will set Engine.ConmonPath - CPUProfile string // Hidden: Should CPU profile be taken - EngineMode EngineMode // ABI or Tunneling mode - Identity string // ssh identity for connecting to server - MaxWorks int // maximum number of parallel threads - MemoryProfile string // Hidden: Should memory profile be taken - NoOut bool // Don't output to stdout - RegistriesConf string // allows for specifying a custom registries.conf - Remote bool // Connection to Podman API Service will use RESTful API - RuntimePath string // --runtime flag will set Engine.RuntimePath - RuntimeFlags []string // global flags for the container runtime - Syslog bool // write to StdOut and Syslog, not supported when tunneling - Trace bool // Hidden: Trace execution - URI string // URI to RESTful API Service - - Runroot string - StorageDriver string - StorageOpts []string - SSHMode string - MachineMode bool + ContainersConf *config.Config + ContainersConfDefaultsRO *config.Config // The read-only! defaults from containers.conf. + DockerConfig string // Used for Docker compatibility + CgroupUsage string // rootless code determines Usage message + ConmonPath string // --conmon flag will set Engine.ConmonPath + CPUProfile string // Hidden: Should CPU profile be taken + EngineMode EngineMode // ABI or Tunneling mode + Identity string // ssh identity for connecting to server + MaxWorks int // maximum number of parallel threads + MemoryProfile string // Hidden: Should memory profile be taken + RegistriesConf string // allows for specifying a custom registries.conf + Remote bool // Connection to Podman API Service will use RESTful API + RuntimePath string // --runtime flag will set Engine.RuntimePath + RuntimeFlags []string // global flags for the container runtime + Syslog bool // write to StdOut and Syslog, not supported when tunneling + Trace bool // Hidden: Trace execution + URI string // URI to RESTful API Service + + Runroot string + StorageDriver string + StorageOpts []string + SSHMode string + MachineMode bool + TransientStore bool } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_container.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_container.go index 51058e31..06a6372f 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_container.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_container.go @@ -61,8 +61,10 @@ type ContainerEngine interface { //nolint:interfacebloat SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error) HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) + KubeApply(ctx context.Context, body io.Reader, opts ApplyOptions) error NetworkConnect(ctx context.Context, networkname string, options NetworkConnectOptions) error - NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) + NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) + NetworkUpdate(ctx context.Context, networkname string, options NetworkUpdateOptions) error NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error NetworkExists(ctx context.Context, networkname string) (*BoolReport, error) NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]types.Network, []error, error) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_image.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_image.go index 43de27a7..0de0a889 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_image.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/engine_image.go @@ -34,7 +34,7 @@ type ImageEngine interface { //nolint:interfacebloat Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error) ManifestExists(ctx context.Context, name string) (*BoolReport, error) - ManifestInspect(ctx context.Context, name string) ([]byte, error) + ManifestInspect(ctx context.Context, name string, opts ManifestInspectOptions) ([]byte, error) ManifestAdd(ctx context.Context, listName string, imageNames []string, opts ManifestAddOptions) (string, error) ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (string, error) ManifestRemoveDigest(ctx context.Context, names, image string) (string, error) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/images.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/images.go index b1eb3b00..46c4a22e 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/images.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/images.go @@ -7,7 +7,9 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/signature/signer" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/podman/v4/pkg/inspect" "github.com/containers/podman/v4/pkg/trust" "github.com/docker/docker/api/types/container" @@ -158,6 +160,9 @@ type ImagePullOptions struct { PullPolicy config.PullPolicy // Writer is used to display copy information including progress bars. Writer io.Writer + // OciDecryptConfig contains the config that can be used to decrypt an image if it is + // encrypted if non-nil. If nil, it does not attempt to decrypt an image. + OciDecryptConfig *encconfig.DecryptConfig } // ImagePullReport is the response from pulling one or more images. @@ -206,6 +211,10 @@ type ImagePushOptions struct { RemoveSignatures bool // SignaturePolicy to use when pulling. Ignored for remote calls. SignaturePolicy string + // Signers, if non-empty, asks for signatures to be added during the copy + // using the provided signers. + // Rejected for remote calls. + Signers []*signer.Signer // SignBy adds a signature at the destination using the specified key. // Ignored for remote calls. SignBy string @@ -227,6 +236,15 @@ type ImagePushOptions struct { CompressionFormat string // Writer is used to display copy information including progress bars. Writer io.Writer + // OciEncryptConfig when non-nil indicates that an image should be encrypted. + // The encryption options is derived from the construction of EncryptConfig object. + OciEncryptConfig *encconfig.EncryptConfig + // OciEncryptLayers represents the list of layers to encrypt. + // If nil, don't encrypt any layers. + // If non-nil and len==0, denotes encrypt all layers. + // integers in the slice represent 0-indexed layer indices, with support for negative + // indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer. + OciEncryptLayers *[]int } // ImagePushReport is the response from pushing an image. diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/manifest.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/manifest.go index f1707927..030dc4b6 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/manifest.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/manifest.go @@ -12,6 +12,12 @@ type ManifestCreateOptions struct { SkipTLSVerify types.OptionalBool `json:"-" schema:"-"` } +// ManifestInspectOptions provides model for inspecting manifest +type ManifestInspectOptions struct { + // Should TLS registry certificate be verified? + SkipTLSVerify types.OptionalBool `json:"-" schema:"-"` +} + // ManifestAddOptions provides model for adding digests to manifest list // // swagger:model @@ -37,6 +43,8 @@ type ManifestAddOptions struct { type ManifestAnnotateOptions struct { // Annotation to add to manifest list Annotation []string `json:"annotation" schema:"annotation"` + // Annotations to add to manifest list by a map which is prefferred over Annotation + Annotations map[string]string `json:"annotations" schema:"annotations"` // Arch overrides the architecture for the image Arch string `json:"arch" schema:"arch"` // Feature list for the image diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/network.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/network.go index 9e59953c..0ac3d5bf 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/network.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/network.go @@ -41,17 +41,26 @@ type NetworkRmReport struct { // NetworkCreateOptions describes options to create a network type NetworkCreateOptions struct { - DisableDNS bool - Driver string - Gateways []net.IP - Internal bool - Labels map[string]string - MacVLAN string - Ranges []string - Subnets []string - IPv6 bool + DisableDNS bool + Driver string + Gateways []net.IP + Internal bool + Labels map[string]string + MacVLAN string + NetworkDNSServers []string + Ranges []string + Subnets []string + IPv6 bool // Mapping of driver options and values. Options map[string]string + // IgnoreIfExists if true, do not fail if the network already exists + IgnoreIfExists bool +} + +// NetworkUpdateOptions describes options to update a network +type NetworkUpdateOptions struct { + AddDNSServers []string `json:"adddnsservers"` + RemoveDNSServers []string `json:"removednsservers"` } // NetworkCreateReport describes a created network for the cli diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/play.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/play.go index 5bb438d7..bd14b296 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/play.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/play.go @@ -58,6 +58,12 @@ type PlayKubeOptions struct { ServiceContainer bool // Userns - define the user namespace to use. Userns string + // IsRemote - was the request triggered by running podman-remote + IsRemote bool + // Force - remove volumes on --down + Force bool + // PublishPorts - configure how to expose ports configured inside the K8S YAML file + PublishPorts []string } // PlayKubePod represents a single pod and associated containers created by play kube @@ -94,12 +100,16 @@ type PlayKubeReport struct { type KubePlayReport = PlayKubeReport // PlayKubeDownOptions are options for tearing down pods -type PlayKubeDownOptions struct{} +type PlayKubeDownOptions struct { + // Force - remove volumes if passed + Force bool +} // PlayKubeDownReport contains the results of tearing down play kube type PlayKubeTeardown struct { - StopReport []*PodStopReport - RmReport []*PodRmReport + StopReport []*PodStopReport + RmReport []*PodRmReport + VolumeRmReport []*VolumeRmReport } type PlaySecret struct { diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/pods.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/pods.go index a059cd7b..36676d56 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/pods.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/pods.go @@ -174,124 +174,129 @@ const ( ) type ContainerCreateOptions struct { - Annotation []string - Attach []string - Authfile string - BlkIOWeight string - BlkIOWeightDevice []string - CapAdd []string - CapDrop []string - CgroupNS string - CgroupsMode string - CgroupParent string `json:"cgroup_parent,omitempty"` - CIDFile string - ConmonPIDFile string `json:"container_conmon_pidfile,omitempty"` - CPUPeriod uint64 - CPUQuota int64 - CPURTPeriod uint64 - CPURTRuntime int64 - CPUShares uint64 - CPUS float64 `json:"cpus,omitempty"` - CPUSetCPUs string `json:"cpuset_cpus,omitempty"` - CPUSetMems string - Devices []string `json:"devices,omitempty"` - DeviceCgroupRule []string - DeviceReadBPs []string `json:"device_read_bps,omitempty"` - DeviceReadIOPs []string - DeviceWriteBPs []string - DeviceWriteIOPs []string - Entrypoint *string `json:"container_command,omitempty"` - Env []string - EnvHost bool - EnvFile []string - Expose []string - GIDMap []string - GroupAdd []string - HealthCmd string - HealthInterval string - HealthRetries uint - HealthStartPeriod string - HealthTimeout string - HealthOnFailure string - Hostname string `json:"hostname,omitempty"` - HTTPProxy bool - HostUsers []string - ImageVolume string - Init bool - InitContainerType string - InitPath string - Interactive bool - IPC string - Label []string - LabelFile []string - LogDriver string - LogOptions []string - Memory string - MemoryReservation string - MemorySwap string - MemorySwappiness int64 - Name string `json:"container_name"` - NoHealthCheck bool - OOMKillDisable bool - OOMScoreAdj *int - Arch string - OS string - Variant string - PID string `json:"pid,omitempty"` - PIDsLimit *int64 - Platform string - Pod string - PodIDFile string - Personality string - PreserveFDs uint - Privileged bool - PublishAll bool - Pull string - Quiet bool - ReadOnly bool - ReadOnlyTmpFS bool - Restart string - Replace bool - Requires []string - Rm bool - RootFS bool - Secrets []string - SecurityOpt []string `json:"security_opt,omitempty"` - SdNotifyMode string - ShmSize string - SignaturePolicy string - StopSignal string - StopTimeout uint - StorageOpts []string - SubUIDName string - SubGIDName string - Sysctl []string `json:"sysctl,omitempty"` - Systemd string - Timeout uint - TLSVerify commonFlag.OptionalBool - TmpFS []string - TTY bool - Timezone string - Umask string - EnvMerge []string - UnsetEnv []string - UnsetEnvAll bool - UIDMap []string - Ulimit []string - User string - UserNS string `json:"-"` - UTS string - Mount []string - Volume []string `json:"volume,omitempty"` - VolumesFrom []string `json:"volumes_from,omitempty"` - Workdir string - SeccompPolicy string - PidFile string - ChrootDirs []string - IsInfra bool - IsClone bool - - Net *NetOptions `json:"net,omitempty"` + Annotation []string + Attach []string + Authfile string + BlkIOWeight string + BlkIOWeightDevice []string + CapAdd []string + CapDrop []string + CgroupNS string + CgroupsMode string + CgroupParent string `json:"cgroup_parent,omitempty"` + CIDFile string + ConmonPIDFile string `json:"container_conmon_pidfile,omitempty"` + CPUPeriod uint64 + CPUQuota int64 + CPURTPeriod uint64 + CPURTRuntime int64 + CPUShares uint64 + CPUS float64 `json:"cpus,omitempty"` + CPUSetCPUs string `json:"cpuset_cpus,omitempty"` + CPUSetMems string + Devices []string `json:"devices,omitempty"` + DeviceCgroupRule []string + DeviceReadBPs []string `json:"device_read_bps,omitempty"` + DeviceReadIOPs []string + DeviceWriteBPs []string + DeviceWriteIOPs []string + Entrypoint *string `json:"container_command,omitempty"` + Env []string + EnvHost bool + EnvFile []string + Expose []string + GIDMap []string + GroupAdd []string + HealthCmd string + HealthInterval string + HealthRetries uint + HealthStartPeriod string + HealthTimeout string + HealthOnFailure string + Hostname string `json:"hostname,omitempty"` + HTTPProxy bool + HostUsers []string + ImageVolume string + Init bool + InitContainerType string + InitPath string + Interactive bool + IPC string + Label []string + LabelFile []string + LogDriver string + LogOptions []string + Memory string + MemoryReservation string + MemorySwap string + MemorySwappiness int64 + Name string `json:"container_name"` + NoHealthCheck bool + OOMKillDisable bool + OOMScoreAdj *int + Arch string + OS string + Variant string + PID string `json:"pid,omitempty"` + PIDsLimit *int64 + Platform string + Pod string + PodIDFile string + Personality string + PreserveFDs uint + Privileged bool + PublishAll bool + Pull string + Quiet bool + ReadOnly bool + ReadWriteTmpFS bool + Restart string + Replace bool + Requires []string + Rm bool + RootFS bool + Secrets []string + SecurityOpt []string `json:"security_opt,omitempty"` + SdNotifyMode string + ShmSize string + SignaturePolicy string + StartupHCCmd string + StartupHCInterval string + StartupHCRetries uint + StartupHCSuccesses uint + StartupHCTimeout string + StopSignal string + StopTimeout uint + StorageOpts []string + SubUIDName string + SubGIDName string + Sysctl []string `json:"sysctl,omitempty"` + Systemd string + Timeout uint + TLSVerify commonFlag.OptionalBool + TmpFS []string + TTY bool + Timezone string + Umask string + EnvMerge []string + UnsetEnv []string + UnsetEnvAll bool + UIDMap []string + Ulimit []string + User string + UserNS string `json:"-"` + UTS string + Mount []string + Volume []string `json:"volume,omitempty"` + VolumesFrom []string `json:"volumes_from,omitempty"` + Workdir string + SeccompPolicy string + PidFile string + ChrootDirs []string + IsInfra bool + IsClone bool + DecryptionKeys []string + Net *NetOptions `json:"net,omitempty"` CgroupConf []string diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/reports/containers.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/reports/containers.go index 6759fc40..6ccbc9a6 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/reports/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/reports/containers.go @@ -3,7 +3,7 @@ package reports type RmReport struct { Id string `json:"Id"` //nolint:revive,stylecheck Err error `json:"Err,omitempty"` - RawInput string + RawInput string `json:"-"` } func RmReportsIds(r []*RmReport) []string { diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/system.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/system.go index 8dd0a61b..5d7ef92d 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/system.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/system.go @@ -18,9 +18,10 @@ type ServiceOptions struct { // SystemPruneOptions provides options to prune system. type SystemPruneOptions struct { - All bool - Volume bool - Filters map[string][]string `json:"filters" schema:"filters"` + All bool + Volume bool + Filters map[string][]string `json:"filters" schema:"filters"` + External bool } // SystemPruneReport provides report after system prune is executed. @@ -47,6 +48,7 @@ type SystemDfOptions struct { // SystemDfReport describes the response for df information type SystemDfReport struct { + ImagesSize int64 Images []*SystemDfImageReport Containers []*SystemDfContainerReport Volumes []*SystemDfVolumeReport @@ -85,12 +87,6 @@ type SystemDfVolumeReport struct { ReclaimableSize int64 } -// SystemResetOptions describes the options for resetting your -// container runtime storage, etc -type SystemResetOptions struct { - Force bool -} - // SystemVersionReport describes version information about the running Podman service type SystemVersionReport struct { // Always populated diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/entities/volumes.go b/vendor/github.com/containers/podman/v4/pkg/domain/entities/volumes.go index 9a06b223..dad09e07 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/entities/volumes.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/entities/volumes.go @@ -19,6 +19,8 @@ type VolumeCreateOptions struct { Labels map[string]string `schema:"labels"` // Mapping of driver options and values. Options map[string]string `schema:"opts"` + // Ignore existing volumes + IgnoreIfExists bool `schema:"ignoreIfExist"` } type VolumeConfigResponse struct { diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/filters/containers.go b/vendor/github.com/containers/podman/v4/pkg/domain/filters/containers.go index de62b658..99b11f04 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/filters/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/filters/containers.go @@ -30,7 +30,11 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo case "name": // we only have to match one name return func(c *libpod.Container) bool { - return util.StringMatchRegexSlice(c.Name(), filterValues) + var filters []string + for _, f := range filterValues { + filters = append(filters, strings.ReplaceAll(f, "/", "")) + } + return util.StringMatchRegexSlice(c.Name(), filters) }, nil case "exited": var exitCodes []int32 @@ -84,20 +88,20 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo // - ancestor=([:tag]|| ⟨image@digest⟩) - containers created from an image or a descendant. return func(c *libpod.Container) bool { for _, filterValue := range filterValues { - containerConfig := c.Config() + rootfsImageID, rootfsImageName := c.Image() var imageTag string var imageNameWithoutTag string // Compare with ImageID, ImageName // Will match ImageName if running image has tag latest for other tags exact complete filter must be given - imageNameSlice := strings.SplitN(containerConfig.RootfsImageName, ":", 2) + imageNameSlice := strings.SplitN(rootfsImageName, ":", 2) if len(imageNameSlice) == 2 { imageNameWithoutTag = imageNameSlice[0] imageTag = imageNameSlice[1] } - if (containerConfig.RootfsImageID == filterValue) || - (containerConfig.RootfsImageName == filterValue) || - (imageNameWithoutTag == filterValue && imageTag == "latest") { + if (rootfsImageID == filterValue) || + util.StringMatchRegexSlice(rootfsImageName, filterValues) || + (util.StringMatchRegexSlice(imageNameWithoutTag, filterValues) && imageTag == "latest") { return true } } @@ -110,14 +114,12 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo if err != nil { return nil, err } - containerConfig := ctr.Config() - if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { - createTime = containerConfig.CreatedTime + if createTime.IsZero() || createTime.After(ctr.CreatedTime()) { + createTime = ctr.CreatedTime() } } return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.After(cc.CreatedTime) + return createTime.After(c.CreatedTime()) }, nil case "since": var createTime time.Time @@ -126,19 +128,17 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo if err != nil { return nil, err } - containerConfig := ctr.Config() - if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { - createTime = containerConfig.CreatedTime + if createTime.IsZero() || createTime.After(ctr.CreatedTime()) { + createTime = ctr.CreatedTime() } } return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.Before(cc.CreatedTime) + return createTime.Before(c.CreatedTime()) }, nil case "volume": //- volume=(|) return func(c *libpod.Container) bool { - containerConfig := c.Config() + containerConfig := c.ConfigNoCopy() var dest string for _, filterValue := range filterValues { arr := strings.SplitN(filterValue, ":", 2) @@ -150,7 +150,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo if dest != "" && (mount.Source == source && mount.Destination == dest) { return true } - if dest == "" && mount.Source == source { + if dest == "" && mount.Destination == source { return true } } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/apply.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/apply.go new file mode 100644 index 00000000..0e63d829 --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/apply.go @@ -0,0 +1,194 @@ +package abi + +import ( + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/containers/podman/v4/pkg/domain/entities" + k8sAPI "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" + "github.com/ghodss/yaml" +) + +func (ic *ContainerEngine) KubeApply(ctx context.Context, body io.Reader, options entities.ApplyOptions) error { + // Read the yaml file + content, err := io.ReadAll(body) + if err != nil { + return err + } + if len(content) == 0 { + return errors.New("yaml file provided is empty, cannot apply to a cluster") + } + + // Split the yaml file + documentList, err := splitMultiDocYAML(content) + if err != nil { + return err + } + + // Sort the kube kinds + documentList, err = sortKubeKinds(documentList) + if err != nil { + return fmt.Errorf("unable to sort kube kinds: %w", err) + } + + // Get the namespace to deploy the workload to + namespace := options.Namespace + if namespace == "" { + namespace = "default" + } + + // Parse the given kubeconfig + kconfig, err := getClusterInfo(options.Kubeconfig) + if err != nil { + return err + } + + // Set up the client to connect to the cluster endpoints + client, err := setUpClusterClient(kconfig, options) + if err != nil { + return err + } + + for _, document := range documentList { + kind, err := getKubeKind(document) + if err != nil { + return fmt.Errorf("unable to read kube YAML: %w", err) + } + + switch kind { + case entities.TypeService: + url := kconfig.Clusters[0].Cluster.Server + "/api/v1/namespaces/" + namespace + "/services" + if err := createObject(client, url, document); err != nil { + return err + } + case entities.TypePVC: + url := kconfig.Clusters[0].Cluster.Server + "/api/v1/namespaces/" + namespace + "/persistentvolumeclaims" + if err := createObject(client, url, document); err != nil { + return err + } + case entities.TypePod: + url := kconfig.Clusters[0].Cluster.Server + "/api/v1/namespaces/" + namespace + "/pods" + if err := createObject(client, url, document); err != nil { + return err + } + default: + return fmt.Errorf("unsupported Kubernetes kind found: %q", kind) + } + } + + return nil +} + +// setUpClusterClient sets up the client to use when connecting to the cluster. It sets up the CA Certs and +// client certs and keys based on the information given in the kubeconfig +func setUpClusterClient(kconfig k8sAPI.Config, applyOptions entities.ApplyOptions) (*http.Client, error) { + var ( + clientCert tls.Certificate + err error + ) + + // Load client certificate and key + // This information will always be in the kubeconfig + if kconfig.AuthInfos[0].AuthInfo.ClientCertificate != "" && kconfig.AuthInfos[0].AuthInfo.ClientKey != "" { + clientCert, err = tls.LoadX509KeyPair(kconfig.AuthInfos[0].AuthInfo.ClientCertificate, kconfig.AuthInfos[0].AuthInfo.ClientKey) + if err != nil { + return nil, err + } + } else if len(kconfig.AuthInfos[0].AuthInfo.ClientCertificateData) > 0 && len(kconfig.AuthInfos[0].AuthInfo.ClientKeyData) > 0 { + clientCert, err = tls.X509KeyPair(kconfig.AuthInfos[0].AuthInfo.ClientCertificateData, kconfig.AuthInfos[0].AuthInfo.ClientKeyData) + if err != nil { + return nil, err + } + } + + // Load CA cert + // The CA cert may not always be in the kubeconfig and could be in a separate file. + // The CA cert file can be passed on here by setting the --ca-cert-file flag. If that is not set + // check the kubeconfig to see if it has the CA cert data. + var caCert []byte + insecureSkipVerify := false + caCertFile := applyOptions.CACertFile + caCertPool := x509.NewCertPool() + + // Be insecure if user sets ca-cert-file flag to insecure + if strings.ToLower(caCertFile) == "insecure" { + insecureSkipVerify = true + } else if caCertFile == "" { + caCertFile = kconfig.Clusters[0].Cluster.CertificateAuthority + } + + // Get the caCert data if we are running secure + if caCertFile != "" && !insecureSkipVerify { + caCert, err = os.ReadFile(caCertFile) + if err != nil { + return nil, err + } + } else if len(kconfig.Clusters[0].Cluster.CertificateAuthorityData) > 0 && !insecureSkipVerify { + caCert = kconfig.Clusters[0].Cluster.CertificateAuthorityData + } + if len(caCert) > 0 { + caCertPool.AppendCertsFromPEM(caCert) + } + + // Create transport with ca and client certs + tr := &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caCertPool, Certificates: []tls.Certificate{clientCert}, InsecureSkipVerify: insecureSkipVerify}, + } + return &http.Client{Transport: tr}, nil +} + +// createObject connects to the given url and creates the yaml given in objectData +func createObject(client *http.Client, url string, objectData []byte) error { + req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(objectData))) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/yaml") + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + // Log the response body as fatal if we get a non-success status code + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusAccepted { + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + return errors.New(string(body)) + } + return nil +} + +// getClusterInfo returns the kubeconfig in struct form so that the server +// and certificates data can be accessed and used to connect to the k8s cluster +func getClusterInfo(kubeconfig string) (k8sAPI.Config, error) { + var config k8sAPI.Config + + configData, err := os.ReadFile(kubeconfig) + if err != nil { + return config, err + } + + // Convert yaml kubeconfig to json so we can unmarshal it + jsonData, err := yaml.YAMLToJSON(configData) + if err != nil { + return config, err + } + + if err := json.Unmarshal(jsonData, &config); err != nil { + return config, err + } + + return config, nil +} diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers.go index 48457495..5674f675 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "reflect" "strconv" "sync" "time" @@ -34,90 +35,105 @@ import ( "github.com/sirupsen/logrus" ) -// getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids -// is specified. It also returns a list of the corresponding input name used to lookup each container. -func getContainersAndInputByContext(all, latest, isPod bool, names []string, filters map[string][]string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) { - var ctr *libpod.Container - var filteredCtrs []*libpod.Container - ctrs = []*libpod.Container{} - filterFuncs := make([]libpod.ContainerFilter, 0, len(filters)) +type getContainersOptions struct { + all bool + isPod bool + ignore bool + latest bool + running bool + filters map[string][]string + names []string +} + +type containerWrapper struct { + *libpod.Container + rawInput string + doesNotExist bool +} + +func getContainers(runtime *libpod.Runtime, options getContainersOptions) ([]containerWrapper, error) { + var libpodContainers []*libpod.Container switch { - case len(filters) > 0: - for k, v := range filters { + case len(options.filters) > 0: + filterFuncs := make([]libpod.ContainerFilter, 0, len(options.filters)) + for k, v := range options.filters { generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, runtime) if err != nil { - return nil, nil, err + return nil, err } filterFuncs = append(filterFuncs, generatedFunc) } - ctrs, err = runtime.GetContainers(filterFuncs...) + ctrs, err := runtime.GetContainers(false, filterFuncs...) if err != nil { - return nil, nil, err + return nil, err } - rawInput = []string{} - for _, candidate := range ctrs { - if len(names) > 0 { - for _, name := range names { + libpodContainers = ctrs + if len(options.names) > 0 { + var filteredCtrs []*libpod.Container + for _, candidate := range ctrs { + for _, name := range options.names { if candidate.ID() == name || candidate.Name() == name { - rawInput = append(rawInput, candidate.ID()) filteredCtrs = append(filteredCtrs, candidate) } } - ctrs = filteredCtrs - } else { - rawInput = append(rawInput, candidate.ID()) + libpodContainers = filteredCtrs } } - case all: - ctrs, err = runtime.GetAllContainers() - if err == nil { - for _, ctr := range ctrs { - rawInput = append(rawInput, ctr.ID()) - } + case options.running: + // Process `running` before `all`. podman-restart allows both + // but will narrow it down to `running`. + containers, err := runtime.GetRunningContainers() + if err != nil { + return nil, err + } + libpodContainers = containers + case options.all: + containers, err := runtime.GetAllContainers() + if err != nil { + return nil, err } - case latest: - if isPod { + libpodContainers = containers + case options.latest: + if options.isPod { pod, err := runtime.GetLatestPod() - if err == nil { - podCtrs, err := pod.AllContainers() - if err == nil { - for _, c := range podCtrs { - rawInput = append(rawInput, c.ID()) - } - ctrs = podCtrs - } + if err != nil { + return nil, err } + podCtrs, err := pod.AllContainers() + if err != nil { + return nil, err + } + libpodContainers = podCtrs } else { - ctr, err = runtime.GetLatestContainer() - if err == nil { - rawInput = append(rawInput, ctr.ID()) - ctrs = append(ctrs, ctr) + ctr, err := runtime.GetLatestContainer() + if err != nil { + return nil, err } + libpodContainers = append(libpodContainers, ctr) } default: - for _, n := range names { - ctr, e := runtime.LookupContainer(n) - if e != nil { - // Log all errors here, so callers don't need to. - logrus.Debugf("Error looking up container %q: %v", n, e) - if err == nil { - err = e + containers := make([]containerWrapper, 0, len(options.names)) + for _, n := range options.names { + ctr, err := runtime.LookupContainer(n) + if err != nil { + if options.ignore && errors.Is(err, define.ErrNoSuchCtr) { + containers = append(containers, containerWrapper{rawInput: n, doesNotExist: true}) + continue } - } else { - rawInput = append(rawInput, n) - ctrs = append(ctrs, ctr) + return nil, err } + containers = append(containers, containerWrapper{Container: ctr, rawInput: n}) } + return containers, nil + } + + containers := make([]containerWrapper, len(libpodContainers)) + for i := range libpodContainers { + containers[i] = containerWrapper{Container: libpodContainers[i], rawInput: libpodContainers[i].ID()} } - return ctrs, rawInput, err -} -// getContainersByContext gets containers whether all, latest, or a slice of names/ids -// is specified. -func getContainersByContext(all, latest, isPod bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { - ctrs, _, err = getContainersAndInputByContext(all, latest, isPod, names, nil, runtime) - return + return containers, nil } // ContainerExists returns whether the container exists in container storage @@ -138,13 +154,18 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { - ctrs, err := getContainersByContext(false, options.Latest, false, namesOrIds, ic.Libpod) + responses := make([]entities.WaitReport, 0, len(namesOrIds)) + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, ignore: options.Ignore, names: namesOrIds}) if err != nil { return nil, err } - responses := make([]entities.WaitReport, 0, len(ctrs)) - for _, c := range ctrs { - response := entities.WaitReport{Id: c.ID()} + for _, c := range containers { + if c.doesNotExist { // Only set when `options.Ignore == true` + responses = append(responses, entities.WaitReport{ExitCode: -1}) + continue + } + + response := entities.WaitReport{} if options.Condition == nil { options.Condition = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited} } @@ -160,18 +181,12 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, options.Filters, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds, filters: options.Filters}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } - reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) - for _, c := range ctrs { + reports := make([]*entities.PauseUnpauseReport, 0, len(containers)) + for _, c := range containers { err := c.Pause() if err != nil && options.All && errors.Is(err, define.ErrCtrStateInvalid) { logrus.Debugf("Container %s is not running", c.ID()) @@ -180,25 +195,19 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri reports = append(reports, &entities.PauseUnpauseReport{ Id: c.ID(), Err: err, - RawInput: idToRawInput[c.ID()], + RawInput: c.rawInput, }) } return reports, nil } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, options.Filters, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds, filters: options.Filters}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } - reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) - for _, c := range ctrs { + reports := make([]*entities.PauseUnpauseReport, 0, len(containers)) + for _, c := range containers { err := c.Unpause() if err != nil && options.All && errors.Is(err, define.ErrCtrStateInvalid) { logrus.Debugf("Container %s is not paused", c.ID()) @@ -207,24 +216,35 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st reports = append(reports, &entities.PauseUnpauseReport{ Id: c.ID(), Err: err, - RawInput: idToRawInput[c.ID()], + RawInput: c.rawInput, }) } return reports, nil } func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { - names := namesOrIds - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, names, options.Filters, ic.Libpod) - if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) { + containers, err := getContainers(ic.Libpod, + getContainersOptions{ + all: options.All, + latest: options.Latest, + names: namesOrIds, + filters: options.Filters, + ignore: options.Ignore, + }) + if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] + + idToRawInput := make(map[string]string, len(containers)) + libpodContainers := make([]*libpod.Container, 0, len(containers)) + for i, c := range containers { + if c.doesNotExist { + continue } + idToRawInput[c.ID()] = c.rawInput + libpodContainers = append(libpodContainers, containers[i].Container) } - errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { + + errMap, err := parallelctr.ContainerOp(ctx, libpodContainers, func(c *libpod.Container) error { var err error if options.Timeout != nil { err = c.StopWithTimeout(*options.Timeout) @@ -240,7 +260,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin // container never created in OCI runtime // docker parity: do nothing just return container id case errors.Is(err, define.ErrCtrStateInvalid): - logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID()) + logrus.Debugf("Container %s is either not created on runtime or is in an invalid state", c.ID()) default: return err } @@ -264,11 +284,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin for ctr, err := range errMap { report := new(entities.StopReport) report.Id = ctr.ID() - if options.All { - report.RawInput = ctr.ID() - } else { - report.RawInput = idToRawInput[ctr.ID()] - } + report.RawInput = idToRawInput[ctr.ID()] report.Err = err reports = append(reports, report) } @@ -293,18 +309,13 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin if err != nil { return nil, err } - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, nil, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } - reports := make([]*entities.KillReport, 0, len(ctrs)) - for _, con := range ctrs { + + reports := make([]*entities.KillReport, 0, len(containers)) + for _, con := range containers { err := con.Kill(uint(sig)) if options.All && errors.Is(err, define.ErrCtrStateInvalid) { logrus.Debugf("Container %s is not running", con.ID()) @@ -313,42 +324,26 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin reports = append(reports, &entities.KillReport{ Id: con.ID(), Err: err, - RawInput: idToRawInput[con.ID()], + RawInput: con.rawInput, }) } return reports, nil } func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { - var ( - ctrs []*libpod.Container - err error - rawInputs = []string{} - ) - - if options.Running { - ctrs, err = ic.Libpod.GetRunningContainers() - for _, candidate := range ctrs { - rawInputs = append(rawInputs, candidate.ID()) - } - - if err != nil { - return nil, err - } - } else { - ctrs, rawInputs, err = getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, options.Filters, ic.Libpod) - if err != nil { - return nil, err - } - } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } + containers, err := getContainers(ic.Libpod, getContainersOptions{ + all: options.All, + filters: options.Filters, + latest: options.Latest, + running: options.Running, + names: namesOrIds, + }) + if err != nil { + return nil, err } - reports := make([]*entities.RestartReport, 0, len(ctrs)) - for _, c := range ctrs { + + reports := make([]*entities.RestartReport, 0, len(containers)) + for _, c := range containers { timeout := c.StopTimeout() if options.Timeout != nil { timeout = *options.Timeout @@ -356,7 +351,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st reports = append(reports, &entities.RestartReport{ Id: c.ID(), Err: c.RestartWithTimeout(ctx, timeout), - RawInput: idToRawInput[c.ID()], + RawInput: c.rawInput, }) } return reports, nil @@ -388,69 +383,37 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) { rmReports := []*reports.RmReport{} - names := namesOrIds - // Attempt to remove named containers directly from storage, if container is defined in libpod - // this will fail and code will fall through to removing the container from libpod.` - tmpNames := []string{} - for _, ctr := range names { - report := reports.RmReport{Id: ctr} - report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force) - //nolint:gocritic - if report.Err == nil { - // remove container names that we successfully deleted - rmReports = append(rmReports, &report) - } else if errors.Is(report.Err, define.ErrNoSuchCtr) || errors.Is(report.Err, define.ErrCtrExists) { - // There is still a potential this is a libpod container - tmpNames = append(tmpNames, ctr) - } else { - if _, err := ic.Libpod.LookupContainer(ctr); errors.Is(err, define.ErrNoSuchCtr) { - // remove container failed, but not a libpod container - rmReports = append(rmReports, &report) - continue - } - // attempt to remove as a libpod container - tmpNames = append(tmpNames, ctr) - } - } - names = tmpNames - - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, names, options.Filters, ic.Libpod) - if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) { + containers, err := getContainers(ic.Libpod, getContainersOptions{ + all: options.All, + latest: options.Latest, + filters: options.Filters, + names: namesOrIds, + ignore: true, // Force ignore as `podman rm` also handles external containers + }) + if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } - if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) { - // Failed to get containers. If force is specified, get the containers ID - // and evict them - if !options.Force { - return nil, err - } - for _, ctr := range names { - logrus.Debugf("Evicting container %q", ctr) - report := reports.RmReport{ - Id: ctr, - RawInput: idToRawInput[ctr], - } - _, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) - if err != nil { - if options.Ignore && errors.Is(err, define.ErrNoSuchCtr) { - logrus.Debugf("Ignoring error (--allow-missing): %v", err) - rmReports = append(rmReports, &report) + libpodContainers := make([]*libpod.Container, 0, len(containers)) + idToRawInput := make(map[string]string, len(containers)) + for i, ctr := range containers { + if ctr.doesNotExist { + // If the container does not exist in Podman's database, it may + // be an external one. Hence, try removing the external + // "storage" container. + if err := ic.Libpod.RemoveStorageContainer(ctr.rawInput, options.Force); err != nil { + if options.Ignore && (errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrExists)) { continue } - report.Err = err - rmReports = append(rmReports, &report) - continue + return nil, err } - rmReports = append(rmReports, &report) + rmReports = append(rmReports, &reports.RmReport{RawInput: ctr.rawInput}) + } else { + // If the container exists in the Podman database, we + // can remove it correctly below. + libpodContainers = append(libpodContainers, containers[i].Container) + idToRawInput[ctr.ID()] = ctr.rawInput } - return rmReports, nil } alreadyRemoved := make(map[string]bool) @@ -460,26 +423,15 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, rmReports = append(rmReports, newReports[i]) } } - if !options.All && options.Depend { - // Add additional containers based on dependencies to container map - for _, ctr := range ctrs { - if alreadyRemoved[ctr.ID()] { - continue - } - reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) - if err != nil { - return rmReports, err - } - addReports(reports) - } - return rmReports, nil - } - // If --all is set, make sure to remove the infra containers' - // dependencies before doing the parallel removal below. - if options.All { - for _, ctr := range ctrs { - if alreadyRemoved[ctr.ID()] || !ctr.IsInfra() { + // First, remove dependent containers. + if options.All || options.Depend { + for _, ctr := range libpodContainers { + // When `All` is set, remove the infra containers and + // their dependencies first. Otherwise, we'd error out. + // + // TODO: All this logic should probably live in libpod. + if alreadyRemoved[ctr.ID()] || (options.All && !ctr.IsInfra()) { continue } reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) @@ -490,7 +442,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, } } - errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { + errMap, err := parallelctr.ContainerOp(ctx, libpodContainers, func(c *libpod.Container) error { if alreadyRemoved[c.ID()] { return nil } @@ -499,6 +451,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, if err != nil { return nil, err } + for ctr, err := range errMap { if alreadyRemoved[ctr.ID()] { continue @@ -509,6 +462,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, report.RawInput = idToRawInput[ctr.ID()] rmReports = append(rmReports, report) } + return rmReports, nil } @@ -643,11 +597,6 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { - var ( - ctrs []*libpod.Container - rawInputs []string - err error - ) checkOpts := libpod.ContainerCheckpointOptions{ Keep: options.Keep, TCPEstablished: options.TCPEstablished, @@ -662,35 +611,19 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ FileLocks: options.FileLocks, CreateImage: options.CreateImage, } - - idToRawInput := map[string]string{} - if options.All { - running := func(c *libpod.Container) bool { - state, _ := c.State() - return state == define.ContainerStateRunning - } - ctrs, err = ic.Libpod.GetContainers(running) - if err != nil { - return nil, err - } - } else { - ctrs, rawInputs, err = getContainersAndInputByContext(false, options.Latest, false, namesOrIds, nil, ic.Libpod) - if err != nil { - return nil, err - } - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } + // NOTE: all maps to running + containers, err := getContainers(ic.Libpod, getContainersOptions{running: options.All, latest: options.Latest, names: namesOrIds}) + if err != nil { + return nil, err } - reports := make([]*entities.CheckpointReport, 0, len(ctrs)) - for _, c := range ctrs { + + reports := make([]*entities.CheckpointReport, 0, len(containers)) + for _, c := range containers { criuStatistics, runtimeCheckpointDuration, err := c.Checkpoint(ctx, checkOpts) reports = append(reports, &entities.CheckpointReport{ Err: err, Id: c.ID(), - RawInput: idToRawInput[c.ID()], + RawInput: c.rawInput, RuntimeDuration: runtimeCheckpointDuration, CRIUStatistics: criuStatistics, }) @@ -732,9 +665,16 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st case options.Import != "": ctrs, err = checkpoint.CRImportCheckpointTar(ctx, ic.Libpod, options) case options.All: - ctrs, err = ic.Libpod.GetContainers(filterFuncs...) + ctrs, err = ic.Libpod.GetContainers(false, filterFuncs...) case options.Latest: - ctrs, err = getContainersByContext(false, options.Latest, false, namesOrIds, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, names: namesOrIds}) + if err != nil { + return nil, err + } + ctrs = make([]*libpod.Container, 0, len(containers)) + for i := range containers { + ctrs[i] = containers[i].Container + } default: for _, nameOrID := range namesOrIds { logrus.Debugf("look up container: %q", nameOrID) @@ -818,11 +758,15 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG } func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { - ctrs, err := getContainersByContext(false, options.Latest, false, []string{nameOrID}, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, names: []string{nameOrID}}) if err != nil { return err } - ctr := ctrs[0] + if len(containers) != 1 { + return fmt.Errorf("%w: expected to find exactly one container but got %d", define.ErrInternal, len(containers)) + } + + ctr := containers[0] conState, err := ctr.State() if err != nil { return fmt.Errorf("unable to determine state of %s: %w", ctr.ID(), err) @@ -832,7 +776,7 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, } // If the container is in a pod, also set to recursively start dependencies - err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false) + err = terminal.StartAttachCtr(ctx, ctr.Container, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false) if err != nil && !errors.Is(err, define.ErrDetach) { return fmt.Errorf("attaching to container %s: %w", ctr.ID(), err) } @@ -870,7 +814,7 @@ func makeExecConfig(options entities.ExecOptions, rt *libpod.Runtime) (*libpod.E func checkExecPreserveFDs(options entities.ExecOptions) error { if options.PreserveFDs > 0 { - entries, err := os.ReadDir("/proc/self/fd") + entries, err := os.ReadDir(processFileDescriptorsPath) if err != nil { return err } @@ -879,7 +823,7 @@ func checkExecPreserveFDs(options entities.ExecOptions) error { for _, e := range entries { i, err := strconv.Atoi(e.Name()) if err != nil { - return fmt.Errorf("cannot parse %s in /proc/self/fd: %w", e.Name(), err) + return fmt.Errorf("cannot parse %s in %s: %w", e.Name(), processFileDescriptorsPath, err) } m[i] = true } @@ -899,18 +843,21 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o if err != nil { return ec, err } - ctrs, err := getContainersByContext(false, options.Latest, false, []string{nameOrID}, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, names: []string{nameOrID}}) if err != nil { return ec, err } - ctr := ctrs[0] + if len(containers) != 1 { + return ec, fmt.Errorf("%w: expected to find exactly one container but got %d", define.ErrInternal, len(containers)) + } + ctr := containers[0] execConfig, err := makeExecConfig(options, ic.Libpod) if err != nil { return ec, err } - ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) + ec, err = terminal.ExecAttachCtr(ctx, ctr.Container, execConfig, &streams) return define.TranslateExecErrorToExitCode(ec, err), err } @@ -919,11 +866,15 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s if err != nil { return "", err } - ctrs, err := getContainersByContext(false, options.Latest, false, []string{nameOrID}, ic.Libpod) + + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, names: []string{nameOrID}}) if err != nil { return "", err } - ctr := ctrs[0] + if len(containers) != 1 { + return "", fmt.Errorf("%w: expected to find exactly one container but got %d", define.ErrInternal, len(containers)) + } + ctr := containers[0] execConfig, err := makeExecConfig(options, ic.Libpod) if err != nil { @@ -946,19 +897,13 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, options.Filters, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds, filters: options.Filters}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } // There can only be one container if attach was used - for i := range ctrs { - ctr := ctrs[i] + for i := range containers { + ctr := containers[i] ctrState, err := ctr.State() if err != nil { return nil, err @@ -966,13 +911,13 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri ctrRunning := ctrState == define.ContainerStateRunning if options.Attach { - err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning) + err = terminal.StartAttachCtr(ctx, ctr.Container, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning) if errors.Is(err, define.ErrDetach) { // User manually detached // Exit cleanly immediately reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, Err: nil, ExitCode: 0, }) @@ -983,7 +928,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri logrus.Debugf("Deadlock error: %v", err) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, Err: err, ExitCode: define.ExitCode(err), }) @@ -993,7 +938,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if ctrRunning { reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, Err: nil, ExitCode: 0, }) @@ -1003,22 +948,22 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if err != nil { reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, Err: err, ExitCode: exitCode, }) if ctr.AutoRemove() { - if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { + if err := ic.removeContainer(ctx, ctr.Container, entities.RmOptions{}); err != nil { logrus.Errorf("Removing container %s: %v", ctr.ID(), err) } } return reports, fmt.Errorf("unable to start container %s: %w", ctr.ID(), err) } - exitCode = ic.GetContainerExitCode(ctx, ctr) + exitCode = ic.GetContainerExitCode(ctx, ctr.Container) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, Err: err, ExitCode: exitCode, }) @@ -1031,7 +976,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri // If the container is in a pod, also set to recursively start dependencies report := &entities.ContainerStartReport{ Id: ctr.ID(), - RawInput: idToRawInput[ctr.ID()], + RawInput: ctr.rawInput, ExitCode: 125, } if err := ctr.Start(ctx, true); err != nil { @@ -1044,7 +989,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri report.Err = fmt.Errorf("unable to start container %q: %w", ctr.ID(), err) reports = append(reports, report) if ctr.AutoRemove() { - if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { + if err := ic.removeContainer(ctx, ctr.Container, entities.RmOptions{}); err != nil { logrus.Errorf("Removing container %s: %v", ctr.ID(), err) } } @@ -1110,6 +1055,44 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta fmt.Fprintf(os.Stderr, "%s\n", w) } + if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() { + // If this is a checkpoint image, restore it. + img, resolvedImageName := opts.Spec.GetImage() + if img != nil && resolvedImageName != "" { + imgData, err := img.Inspect(ctx, nil) + if err != nil { + return nil, err + } + if imgData != nil { + _, isCheckpointImage := imgData.Annotations[define.CheckpointAnnotationRuntimeName] + if isCheckpointImage { + var restoreOptions entities.RestoreOptions + restoreOptions.Name = opts.Spec.Name + restoreOptions.Pod = opts.Spec.Pod + responses, err := ic.ContainerRestore(ctx, []string{resolvedImageName}, restoreOptions) + if err != nil { + return nil, err + } + + report := entities.ContainerRunReport{} + for _, r := range responses { + report.Id = r.Id + report.ExitCode = 0 + if r.Err != nil { + logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.Err) + report.ExitCode = 126 + } + if r.RawInput != "" { + logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.RawInput) + report.ExitCode = 126 + } + } + return &report, nil + } + } + } + } + rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec, false, nil) if err != nil { return nil, err @@ -1120,7 +1103,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if opts.CIDFile != "" { - if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { + if err := util.CreateIDFile(opts.CIDFile, ctr.ID()); err != nil { // If you fail to create CIDFile then remove the container _ = removeContainer(ctr, true) return nil, err @@ -1194,7 +1177,7 @@ func (ic *ContainerEngine) GetContainerExitCode(ctx context.Context, ctr *libpod return int(exitCode) } -func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { +func (ic *ContainerEngine) ContainerLogs(ctx context.Context, namesOrIds []string, options entities.ContainerLogsOptions) error { if options.StdoutWriter == nil && options.StderrWriter == nil { return errors.New("no io.Writer set for container logs") } @@ -1202,7 +1185,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin var wg sync.WaitGroup isPod := false - for _, c := range containers { + for _, c := range namesOrIds { ctr, err := ic.Libpod.LookupContainer(c) if err != nil { return err @@ -1213,13 +1196,13 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin } } - ctrs, err := getContainersByContext(false, options.Latest, isPod, containers, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{latest: options.Latest, isPod: isPod, names: namesOrIds}) if err != nil { return err } logOpts := &logs.LogOptions{ - Multi: len(ctrs) > 1, + Multi: len(containers) > 1, Details: options.Details, Follow: options.Follow, Since: options.Since, @@ -1231,13 +1214,18 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin WaitGroup: &wg, } - chSize := len(ctrs) * int(options.Tail) + chSize := len(containers) * int(options.Tail) if chSize <= 0 { chSize = 1 } logChannel := make(chan *logs.LogLine, chSize) - if err := ic.Libpod.Log(ctx, ctrs, logOpts, logChannel); err != nil { + libpodContainers := make([]*libpod.Container, len(containers)) + for i := range containers { + libpodContainers[i] = containers[i].Container + } + + if err := ic.Libpod.Log(ctx, libpodContainers, logOpts, logChannel); err != nil { return err } @@ -1254,20 +1242,14 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin } func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, nil, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } reports := []*entities.ContainerCleanupReport{} - for _, ctr := range ctrs { + for _, ctr := range containers { var err error - report := entities.ContainerCleanupReport{Id: ctr.ID(), RawInput: idToRawInput[ctr.ID()]} + report := entities.ContainerCleanupReport{Id: ctr.ID(), RawInput: ctr.rawInput} if options.Exec != "" { if options.Remove { @@ -1284,7 +1266,7 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st if options.Remove && !ctr.ShouldRestart(ctx) { var timeout *uint - err = ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout) + err = ic.Libpod.RemoveContainer(ctx, ctr.Container, false, true, timeout) if err != nil { report.RmErr = fmt.Errorf("failed to clean up and remove container %v: %w", ctr.ID(), err) } @@ -1308,19 +1290,13 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, false, namesOrIds, nil, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: namesOrIds}) if err != nil { return nil, err } - idToRawInput := map[string]string{} - if len(rawInputs) == len(ctrs) { - for i := range ctrs { - idToRawInput[ctrs[i].ID()] = rawInputs[i] - } - } - reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) - for _, ctr := range ctrs { - report := entities.ContainerInitReport{Id: ctr.ID(), RawInput: idToRawInput[ctr.ID()]} + reports := make([]*entities.ContainerInitReport, 0, len(containers)) + for _, ctr := range containers { + report := entities.ContainerInitReport{Id: ctr.ID(), RawInput: ctr.rawInput} err := ctr.Init(ctx, ctr.PodID() != "") // If we're initializing all containers, ignore invalid state errors @@ -1362,11 +1338,11 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin } } - ctrs, err := getContainersByContext(options.All, options.Latest, false, names, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: names}) if err != nil { return nil, err } - for _, ctr := range ctrs { + for _, ctr := range containers { report := entities.ContainerMountReport{Id: ctr.ID()} report.Path, report.Err = ctr.Mount() reports = append(reports, &report) @@ -1400,11 +1376,11 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin } // No containers were passed, so we send back what is mounted - ctrs, err = getContainersByContext(true, false, false, []string{}, ic.Libpod) + containers, err = getContainers(ic.Libpod, getContainersOptions{all: true}) if err != nil { return nil, err } - for _, ctr := range ctrs { + for _, ctr := range containers { mounted, path, err := ctr.Mounted() if err != nil { return nil, err @@ -1452,11 +1428,11 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []str reports = append(reports, &report) } } - ctrs, err := getContainersByContext(options.All, options.Latest, false, names, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: names}) if err != nil { return nil, err } - for _, ctr := range ctrs { + for _, ctr := range containers { state, err := ctr.State() if err != nil { logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) @@ -1486,12 +1462,12 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { } func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { - ctrs, err := getContainersByContext(options.All, options.Latest, false, []string{nameOrID}, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: []string{nameOrID}}) if err != nil { return nil, err } reports := []*entities.ContainerPortReport{} - for _, con := range ctrs { + for _, con := range containers { state, err := con.State() if err != nil { return nil, err @@ -1748,16 +1724,16 @@ func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *e if err != nil { return "", err } - ctrs, err := getContainersByContext(false, false, false, []string{updateOptions.NameOrID}, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{names: []string{updateOptions.NameOrID}}) if err != nil { return "", err } - if len(ctrs) != 1 { + if len(containers) != 1 { return "", fmt.Errorf("container not found") } - if err = ctrs[0].Update(updateOptions.Specgen.ResourceLimits); err != nil { + if err = containers[0].Update(updateOptions.Specgen.ResourceLimits); err != nil { return "", err } - return ctrs[0].ID(), nil + return containers[0].ID(), nil } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_freebsd.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_freebsd.go new file mode 100644 index 00000000..a095a87c --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_freebsd.go @@ -0,0 +1,3 @@ +package abi + +const processFileDescriptorsPath = "/dev/fd" diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_linux.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_linux.go new file mode 100644 index 00000000..10b3d33a --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/containers_linux.go @@ -0,0 +1,3 @@ +package abi + +const processFileDescriptorsPath = "/proc/self/fd" diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/healthcheck.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/healthcheck.go index c4d8cfb0..8761521c 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/healthcheck.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/healthcheck.go @@ -8,13 +8,15 @@ import ( ) func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrID string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { - status, err := ic.Libpod.HealthCheck(nameOrID) + status, err := ic.Libpod.HealthCheck(ctx, nameOrID) if err != nil { return nil, err } hcStatus := define.HealthCheckUnhealthy if status == define.HealthCheckSuccess { hcStatus = define.HealthCheckHealthy + } else if status == define.HealthCheckStartup { + hcStatus = define.HealthCheckStarting } report := define.HealthCheckResults{ Status: hcStatus, diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/images.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/images.go index 16b75829..35ec3125 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/images.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/images.go @@ -32,7 +32,6 @@ import ( "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/storage" - dockerRef "github.com/docker/distribution/reference" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" @@ -237,6 +236,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti pullOptions.SignaturePolicyPath = options.SignaturePolicy pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify pullOptions.Writer = options.Writer + pullOptions.OciDecryptConfig = options.OciDecryptConfig if !options.Quiet && pullOptions.Writer == nil { pullOptions.Writer = os.Stderr @@ -304,12 +304,17 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri pushOptions.Password = options.Password pushOptions.ManifestMIMEType = manifestType pushOptions.RemoveSignatures = options.RemoveSignatures + pushOptions.PolicyAllowStorage = true + pushOptions.SignaturePolicyPath = options.SignaturePolicy + pushOptions.Signers = options.Signers pushOptions.SignBy = options.SignBy pushOptions.SignPassphrase = options.SignPassphrase pushOptions.SignBySigstorePrivateKeyFile = options.SignBySigstorePrivateKeyFile pushOptions.SignSigstorePrivateKeyPassphrase = options.SignSigstorePrivateKeyPassphrase pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify pushOptions.Writer = options.Writer + pushOptions.OciEncryptConfig = options.OciEncryptConfig + pushOptions.OciEncryptLayers = options.OciEncryptLayers compressionFormat := options.CompressionFormat if compressionFormat == "" { @@ -355,6 +360,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri } return pushError } + func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { // Allow tagging manifest list instead of resolving instances from manifest lookupOptions := &libimage.LookupImageOptions{ManifestList: true} @@ -894,7 +900,7 @@ func localPathFromURI(url *url.URL) (string, error) { } // putSignature creates signature and saves it to the signstore file -func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error { +func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference reference.Reference, options entities.SignOptions) error { newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy) if err != nil { return err diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/manifest.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/manifest.go index ac3eedbe..1c894891 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/manifest.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/manifest.go @@ -17,7 +17,9 @@ import ( "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/podman/v4/pkg/domain/entities" + envLib "github.com/containers/podman/v4/pkg/env" "github.com/containers/storage" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -67,7 +69,7 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti } // ManifestInspect returns the content of a manifest list or image -func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) { // NOTE: we have to do a bit of a limbo here as `podman manifest // inspect foo` wants to do a remote-inspect of foo iff "foo" in the // containers storage is an ordinary image but not a manifest list. @@ -77,7 +79,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte if errors.Is(err, storage.ErrImageUnknown) || errors.Is(err, libimage.ErrNotAManifestList) { // Do a remote inspect if there's no local image or if the // local image is not a manifest list. - return ir.remoteManifestInspect(ctx, name) + return ir.remoteManifestInspect(ctx, name, opts) } return nil, err @@ -101,9 +103,14 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte } // inspect a remote manifest list. -func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) ([]byte, error) { +func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) { sys := ir.Libpod.SystemContext() + sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify + if opts.SkipTLSVerify == types.OptionalBoolTrue { + sys.OCIInsecureSkipTLSVerify = true + } + resolved, err := shortnames.Resolve(sys, name) if err != nil { return nil, err @@ -225,8 +232,9 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []st } annotations[spec[0]] = spec[1] } - annotateOptions.Annotations = annotations + opts.Annotations = envLib.Join(opts.Annotations, annotations) } + annotateOptions.Annotations = opts.Annotations if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil { return "", err @@ -263,8 +271,9 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string, } annotations[spec[0]] = spec[1] } - annotateOptions.Annotations = annotations + opts.Annotations = envLib.Join(opts.Annotations, annotations) } + annotateOptions.Annotations = opts.Annotations if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil { return "", err @@ -324,6 +333,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin pushOptions.ImageListSelection = cp.CopySpecificImages pushOptions.ManifestMIMEType = manifestType pushOptions.RemoveSignatures = opts.RemoveSignatures + pushOptions.Signers = opts.Signers pushOptions.SignBy = opts.SignBy pushOptions.SignPassphrase = opts.SignPassphrase pushOptions.SignBySigstorePrivateKeyFile = opts.SignBySigstorePrivateKeyFile diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/network.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/network.go index 300c3ea0..f648a392 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/network.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/network.go @@ -13,6 +13,17 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" ) +func (ic *ContainerEngine) NetworkUpdate(ctx context.Context, netName string, options entities.NetworkUpdateOptions) error { + var networkUpdateOptions types.NetworkUpdateOptions + networkUpdateOptions.AddDNSServers = options.AddDNSServers + networkUpdateOptions.RemoveDNSServers = options.RemoveDNSServers + err := ic.Libpod.Network().NetworkUpdate(netName, networkUpdateOptions) + if err != nil { + return err + } + return nil +} + func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) { // dangling filter is not provided by netutil var wantDangling bool @@ -70,13 +81,13 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { - ctrs, err := getContainersByContext(options.All, options.Latest, false, names, ic.Libpod) + containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: names}) if err != nil { return nil, err } - reports := make([]*entities.NetworkReloadReport, 0, len(ctrs)) - for _, ctr := range ctrs { + reports := make([]*entities.NetworkReloadReport, 0, len(containers)) + for _, ctr := range containers { report := new(entities.NetworkReloadReport) report.Id = ctr.ID() report.Err = ctr.ReloadNetwork() @@ -117,7 +128,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, fmt.Errorf("%q has associated containers with it. Use -f to forcibly delete containers and pods: %w", name, define.ErrNetworkInUse) } if c.IsInfra() { - // if we have a infra container we need to remove the pod + // if we have an infra container we need to remove the pod pod, err := ic.Libpod.GetPod(c.PodID()) if err != nil { return reports, err @@ -138,11 +149,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, nil } -func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) { - if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) { +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) { + // TODO (5.0): Stop accepting "pasta" as value here + if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns", "default"}) { return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name) } - network, err := ic.Libpod.Network().NetworkCreate(network) + network, err := ic.Libpod.Network().NetworkCreate(network, createOptions) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/play.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/play.go index d97cda7c..70fe4329 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/play.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/play.go @@ -18,6 +18,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/types" + "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" @@ -29,6 +30,7 @@ import ( "github.com/containers/podman/v4/pkg/specgenutil" "github.com/containers/podman/v4/pkg/systemd/notifyproxy" "github.com/containers/podman/v4/pkg/util" + "github.com/containers/podman/v4/utils" "github.com/coreos/go-systemd/v22/daemon" "github.com/ghodss/yaml" "github.com/opencontainers/go-digest" @@ -66,6 +68,8 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri ImageVolume: "bind", IsInfra: false, MemorySwappiness: -1, + ReadOnly: true, + ReadWriteTmpFS: false, // No need to spin up slirp etc. Net: &entities.NetOptions{Network: specgen.Namespace{NSMode: specgen.NoNetwork}}, } @@ -120,12 +124,16 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options // when no network options are specified, create a common network for all the pods if len(options.Networks) == 0 { _, err := ic.NetworkCreate( - ctx, nettypes.Network{ + ctx, + nettypes.Network{ Name: kubeDefaultNetwork, DNSEnabled: true, }, + &nettypes.NetworkCreateOptions{ + IgnoreIfExists: true, + }, ) - if err != nil && !errors.Is(err, nettypes.ErrNetworkExists) { + if err != nil { return nil, err } } @@ -153,7 +161,25 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options var configMaps []v1.ConfigMap ranContainers := false + // FIXME: both, the service container and the proxies, should ideally + // be _state_ of an object. The Kube code below is quite Spaghetti-code + // which we should refactor at some point to make it easier to extend + // (via shared state instead of passing data around) and make it more + // maintainable long term. var serviceContainer *libpod.Container + var notifyProxies []*notifyproxy.NotifyProxy + defer func() { + // Close the notify proxy on return. At that point we know + // that a) all containers have send their READY message and + // that b) the service container has exited (and hence all + // containers). + for _, proxy := range notifyProxies { + if err := proxy.Close(); err != nil { + logrus.Errorf("Closing notify proxy %q: %v", proxy.SocketPath(), err) + } + } + }() + // create pod on each document if it is a pod or deployment // any other kube kind will be skipped for _, document := range documentList { @@ -203,10 +229,11 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options podYAML.Annotations[name] = val } - r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations, configMaps, serviceContainer) + r, proxies, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations, configMaps, serviceContainer) if err != nil { return nil, err } + notifyProxies = append(notifyProxies, proxies...) report.Pods = append(report.Pods, r.Pods...) validKinds++ @@ -218,10 +245,11 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options return nil, fmt.Errorf("unable to read YAML as Kube Deployment: %w", err) } - r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer) + r, proxies, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer) if err != nil { return nil, err } + notifyProxies = append(notifyProxies, proxies...) report.Pods = append(report.Pods, r.Pods...) validKinds++ @@ -233,6 +261,19 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options return nil, fmt.Errorf("unable to read YAML as Kube PersistentVolumeClaim: %w", err) } + for name, val := range options.Annotations { + if pvcYAML.Annotations == nil { + pvcYAML.Annotations = make(map[string]string) + } + pvcYAML.Annotations[name] = val + } + + if options.IsRemote { + if _, ok := pvcYAML.Annotations[util.VolumeImportSourceAnnotation]; ok { + return nil, fmt.Errorf("importing volumes is not supported for remote requests") + } + } + r, err := ic.playKubePVC(ctx, &pvcYAML) if err != nil { return nil, err @@ -274,54 +315,51 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options } if options.ServiceContainer && ranContainers { - // We can consider the service to be up and running now. - // Send the sd-notify messages pointing systemd to the - // service container. - data, err := serviceContainer.Inspect(false) - if err != nil { - return nil, err - } - message := fmt.Sprintf("MAINPID=%d\n%s", data.State.ConmonPid, daemon.SdNotifyReady) + message := fmt.Sprintf("MAINPID=%d\n%s", os.Getpid(), daemon.SdNotifyReady) if err := notifyproxy.SendMessage("", message); err != nil { return nil, err } + + if _, err := serviceContainer.Wait(ctx); err != nil { + return nil, fmt.Errorf("waiting for service container: %w", err) + } } return report, nil } -func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) { var ( deploymentName string podSpec v1.PodTemplateSpec numReplicas int32 - i int32 report entities.PlayKubeReport ) deploymentName = deploymentYAML.ObjectMeta.Name if deploymentName == "" { - return nil, errors.New("deployment does not have a name") + return nil, nil, errors.New("deployment does not have a name") } numReplicas = 1 if deploymentYAML.Spec.Replicas != nil { numReplicas = *deploymentYAML.Spec.Replicas } + if numReplicas > 1 { + logrus.Warnf("Limiting replica count to 1, more than one replica is not supported by Podman") + } podSpec = deploymentYAML.Spec.Template - // create "replicas" number of pods - for i = 0; i < numReplicas; i++ { - podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) - podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer) - if err != nil { - return nil, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err) - } - report.Pods = append(report.Pods, podReport.Pods...) + podName := fmt.Sprintf("%s-pod", deploymentName) + podReport, proxies, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer) + if err != nil { + return nil, nil, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err) } - return &report, nil + report.Pods = podReport.Pods + + return &report, proxies, nil } -func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) { var ( writer io.Writer playKubePod entities.PlayKubePod @@ -330,18 +368,18 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY mainSdNotifyMode, err := getSdNotifyMode(annotations, "") if err != nil { - return nil, err + return nil, nil, err } // Create the secret manager before hand secretsManager, err := ic.Libpod.SecretsManager() if err != nil { - return nil, err + return nil, nil, err } // Assert the pod has a name if podName == "" { - return nil, fmt.Errorf("pod does not have a name") + return nil, nil, fmt.Errorf("pod does not have a name") } podOpt := entities.PodCreateOptions{ @@ -351,7 +389,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML) if err != nil { - return nil, err + return nil, nil, err } // add kube default network if no network is explicitly added @@ -360,9 +398,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } if len(options.Networks) > 0 { - ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks) + var pastaNetworkNameExists bool + + _, err := ic.Libpod.Network().NetworkInspect("pasta") + if err == nil { + pastaNetworkNameExists = true + } + + ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks, pastaNetworkNameExists) if err != nil { - return nil, err + return nil, nil, err } podOpt.Net.Network = ns @@ -382,16 +427,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY // Validate the userns modes supported. podOpt.Userns, err = specgen.ParseUserNamespace(options.Userns) if err != nil { - return nil, err + return nil, nil, err } // FIXME This is very hard to support properly with a good ux if len(options.StaticIPs) > *ipIndex { if !podOpt.Net.Network.IsBridge() { - return nil, fmt.Errorf("static ip addresses can only be set when the network mode is bridge: %w", define.ErrInvalidArg) + return nil, nil, fmt.Errorf("static ip addresses can only be set when the network mode is bridge: %w", define.ErrInvalidArg) } if len(podOpt.Net.Networks) != 1 { - return nil, fmt.Errorf("cannot set static ip addresses for more than network, use netname:ip= syntax to specify ips for more than network: %w", define.ErrInvalidArg) + return nil, nil, fmt.Errorf("cannot set static ip addresses for more than network, use netname:ip= syntax to specify ips for more than network: %w", define.ErrInvalidArg) } for name, netOpts := range podOpt.Net.Networks { netOpts.StaticIPs = append(netOpts.StaticIPs, options.StaticIPs[*ipIndex]) @@ -403,10 +448,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } if len(options.StaticMACs) > *ipIndex { if !podOpt.Net.Network.IsBridge() { - return nil, fmt.Errorf("static mac address can only be set when the network mode is bridge: %w", define.ErrInvalidArg) + return nil, nil, fmt.Errorf("static mac address can only be set when the network mode is bridge: %w", define.ErrInvalidArg) } if len(podOpt.Net.Networks) != 1 { - return nil, fmt.Errorf("cannot set static mac address for more than network, use netname:mac= syntax to specify mac for more than network: %w", define.ErrInvalidArg) + return nil, nil, fmt.Errorf("cannot set static mac address for more than network, use netname:mac= syntax to specify mac for more than network: %w", define.ErrInvalidArg) } for name, netOpts := range podOpt.Net.Networks { netOpts.StaticMAC = nettypes.HardwareAddr(options.StaticMACs[*ipIndex]) @@ -418,14 +463,22 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } *ipIndex++ + if len(options.PublishPorts) > 0 { + publishPorts, err := specgenutil.CreatePortBindings(options.PublishPorts) + if err != nil { + return nil, nil, err + } + mergePublishPorts(&podOpt, publishPorts) + } + p := specgen.NewPodSpecGenerator() if err != nil { - return nil, err + return nil, nil, err } p, err = entities.ToPodSpecGen(*p, &podOpt) if err != nil { - return nil, err + return nil, nil, err } podSpec := entities.PodSpec{PodSpecGen: *p} @@ -436,17 +489,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY for _, p := range options.ConfigMaps { f, err := os.Open(p) if err != nil { - return nil, err + return nil, nil, err } defer f.Close() cm, err := readConfigMapFromFile(f) if err != nil { - return nil, fmt.Errorf("%q: %w", p, err) + return nil, nil, fmt.Errorf("%q: %w", p, err) } if _, present := configMapIndex[cm.Name]; present { - return nil, fmt.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p) + return nil, nil, fmt.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p) } configMaps = append(configMaps, cm) @@ -454,7 +507,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps, secretsManager) if err != nil { - return nil, err + return nil, nil, err } // Go through the volumes and create a podman volume for all volumes that have been @@ -468,27 +521,27 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY // error out instead reuse the current volume. vol, err = ic.Libpod.GetVolume(v.Source) if err != nil { - return nil, fmt.Errorf("cannot re-use local volume for volume from configmap %q: %w", v.Source, err) + return nil, nil, fmt.Errorf("cannot re-use local volume for volume from configmap %q: %w", v.Source, err) } } else { - return nil, fmt.Errorf("cannot create a local volume for volume from configmap %q: %w", v.Source, err) + return nil, nil, fmt.Errorf("cannot create a local volume for volume from configmap %q: %w", v.Source, err) } } mountPoint, err := vol.MountPoint() if err != nil || mountPoint == "" { - return nil, fmt.Errorf("unable to get mountpoint of volume %q: %w", vol.Name(), err) + return nil, nil, fmt.Errorf("unable to get mountpoint of volume %q: %w", vol.Name(), err) } // Create files and add data to the volume mountpoint based on the Items in the volume for k, v := range v.Items { dataPath := filepath.Join(mountPoint, k) f, err := os.Create(dataPath) if err != nil { - return nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err) + return nil, nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err) } defer f.Close() _, err = f.Write(v) if err != nil { - return nil, err + return nil, nil, err } } } @@ -496,7 +549,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) if err != nil { - return nil, err + return nil, nil, err } var ctrRestartPolicy string @@ -515,6 +568,8 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY infraImage := util.DefaultContainerConfig().Engine.InfraImage infraOptions := entities.NewInfraContainerCreateOptions() infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname + infraOptions.ReadOnly = true + infraOptions.ReadWriteTmpFS = false infraOptions.UserNS = options.Userns podSpec.PodSpecGen.InfraImage = infraImage podSpec.PodSpecGen.NoInfra = false @@ -524,7 +579,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{}) if err != nil { - return nil, err + return nil, nil, err } } @@ -535,12 +590,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY // Create the Pod pod, err := generate.MakePod(&podSpec, ic.Libpod) if err != nil { - return nil, err + return nil, nil, err } podInfraID, err := pod.InfraContainerID() if err != nil { - return nil, err + return nil, nil, err } if !options.Quiet { @@ -556,24 +611,34 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } else { cwd, err = os.Getwd() if err != nil { - return nil, err + return nil, nil, err } } + cfg, err := ic.Libpod.GetConfigNoCopy() + if err != nil { + return nil, nil, err + } + + var readOnly types.OptionalBool + if cfg.Containers.ReadOnly { + readOnly = types.NewOptionalBool(cfg.Containers.ReadOnly) + } + ctrNames := make(map[string]string) for _, initCtr := range podYAML.Spec.InitContainers { // Error out if same name is used for more than one container if _, ok := ctrNames[initCtr.Name]; ok { - return nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name) + return nil, nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name) } ctrNames[initCtr.Name] = "" // Init containers cannot have either of lifecycle, livenessProbe, readinessProbe, or startupProbe set if initCtr.Lifecycle != nil || initCtr.LivenessProbe != nil || initCtr.ReadinessProbe != nil || initCtr.StartupProbe != nil { - return nil, fmt.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set") + return nil, nil, fmt.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set") } pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, initCtr, options) if err != nil { - return nil, err + return nil, nil, err } for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels @@ -598,7 +663,8 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY PodInfraID: podInfraID, PodName: podName, PodSecurityContext: podYAML.Spec.SecurityContext, - RestartPolicy: ctrRestartPolicy, + ReadOnly: readOnly, + RestartPolicy: define.RestartPolicyNo, SeccompPaths: seccompPaths, SecretsManager: secretsManager, UserNSIsHost: p.Userns.IsHost(), @@ -606,17 +672,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { - return nil, err + return nil, nil, err } specGen.SdNotifyMode = define.SdNotifyModeIgnore rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen, false, nil) if err != nil { - return nil, err + return nil, nil, err } opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore)) ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { - return nil, err + return nil, nil, err } initContainers = append(initContainers, ctr) @@ -627,12 +693,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY for _, container := range podYAML.Spec.Containers { // Error out if the same name is used for more than one container if _, ok := ctrNames[container.Name]; ok { - return nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name) + return nil, nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name) } ctrNames[container.Name] = "" pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options) if err != nil { - return nil, err + return nil, nil, err } for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels @@ -652,27 +718,39 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY PodInfraID: podInfraID, PodName: podName, PodSecurityContext: podYAML.Spec.SecurityContext, + ReadOnly: readOnly, RestartPolicy: ctrRestartPolicy, SeccompPaths: seccompPaths, SecretsManager: secretsManager, + PidNSIsHost: p.Pid.IsHost(), UserNSIsHost: p.Userns.IsHost(), Volumes: volumes, } specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { - return nil, err + return nil, nil, err } + + // Make sure to complete the spec (#17016) + warn, err := generate.CompleteSpec(ctx, ic.Libpod, specGen) + if err != nil { + return nil, nil, err + } + for _, w := range warn { + fmt.Fprintf(os.Stderr, "%s\n", w) + } + specGen.RawImageName = container.Image rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen, false, nil) if err != nil { - return nil, err + return nil, nil, err } sdNotifyMode := mainSdNotifyMode ctrNotifyMode, err := getSdNotifyMode(annotations, container.Name) if err != nil { - return nil, err + return nil, nil, err } if ctrNotifyMode != "" { sdNotifyMode = ctrNotifyMode @@ -688,7 +766,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if sdNotifyMode != "" && sdNotifyMode != define.SdNotifyModeIgnore { proxy, err = notifyproxy.New("") if err != nil { - return nil, err + return nil, nil, err } sdNotifyProxies = append(sdNotifyProxies, proxy) opts = append(opts, libpod.WithSdNotifySocket(proxy.SocketPath())) @@ -696,7 +774,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { - return nil, err + return nil, nil, err } if proxy != nil { proxy.AddContainer(ctr) @@ -708,7 +786,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY // Start the containers podStartErrors, err := pod.Start(ctx) if err != nil && !errors.Is(err, define.ErrPodPartialFail) { - return nil, err + return nil, nil, err } for id, err := range podStartErrors { playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, fmt.Errorf("starting container %s: %w", id, err).Error()) @@ -721,8 +799,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY errors := make([]error, len(sdNotifyProxies)) for i := range sdNotifyProxies { wg.Add(1) + defer func() { + if err := sdNotifyProxies[i].Close(); err != nil { + logrus.Errorf("Closing sdnotify proxy %q: %v", sdNotifyProxies[i].SocketPath(), err) + } + }() go func(i int) { - err := sdNotifyProxies[i].WaitAndClose() + err := sdNotifyProxies[i].Wait() if err != nil { err = fmt.Errorf("waiting for sd-notify proxy: %w", err) } @@ -733,7 +816,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY wg.Wait() for _, err := range errors { if err != nil { - return nil, err + // Close all proxies on error. + for _, proxy := range sdNotifyProxies { + _ = proxy.Close() + } + return nil, nil, err } } } @@ -748,7 +835,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY report.Pods = append(report.Pods, playKubePod) - return &report, nil + return &report, sdNotifyProxies, nil } // getImageAndLabelInfo returns the image information and how the image should be pulled plus as well as labels to be used for the container in the pod. @@ -854,11 +941,13 @@ func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.Persiste volOptions := []libpod.VolumeCreateOption{ libpod.WithVolumeName(name), libpod.WithVolumeLabels(pvcYAML.Labels), + libpod.WithVolumeIgnoreIfExist(), } // Get pvc annotations and create remaining podman volume options if available. // These are podman volume options that do not match any of the persistent volume claim // attributes, so they can be configured using annotations since they will not affect k8s. + var importFrom string for k, v := range pvcYAML.Annotations { switch k { case util.VolumeDriverAnnotation: @@ -883,16 +972,45 @@ func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.Persiste opts["GID"] = v case util.VolumeMountOptsAnnotation: opts["o"] = v + case util.VolumeImportSourceAnnotation: + importFrom = v } } volOptions = append(volOptions, libpod.WithVolumeOptions(opts)) + // Validate the file and open it before creating the volume for fast-fail + var tarFile *os.File + if len(importFrom) > 0 { + err := parse.ValidateFileName(importFrom) + if err != nil { + return nil, err + } + + // open tar file + tarFile, err = os.Open(importFrom) + if err != nil { + return nil, err + } + defer tarFile.Close() + } + // Create volume. vol, err := ic.Libpod.NewVolume(ctx, volOptions...) if err != nil { return nil, err } + if tarFile != nil { + err = ic.importVolume(ctx, vol, tarFile) + if err != nil { + // Remove the volume to avoid partial success + if rmErr := ic.Libpod.RemoveVolume(ctx, vol, true, nil); rmErr != nil { + logrus.Debug(rmErr) + } + return nil, err + } + } + report.Volumes = append(report.Volumes, entities.PlayKubeVolume{ Name: vol.Name(), }) @@ -900,6 +1018,74 @@ func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.Persiste return &report, nil } +func mergePublishPorts(p *entities.PodCreateOptions, publishPortsOption []nettypes.PortMapping) { + for _, publishPortSpec := range p.Net.PublishPorts { + if !portAlreadyPublished(publishPortSpec, publishPortsOption) { + publishPortsOption = append(publishPortsOption, publishPortSpec) + } + } + p.Net.PublishPorts = publishPortsOption +} + +func portAlreadyPublished(port nettypes.PortMapping, publishedPorts []nettypes.PortMapping) bool { + for _, publishedPort := range publishedPorts { + if port.ContainerPort >= publishedPort.ContainerPort && + port.ContainerPort < publishedPort.ContainerPort+publishedPort.Range && + isSamePortProtocol(port.Protocol, publishedPort.Protocol) { + return true + } + } + return false +} + +func isSamePortProtocol(a, b string) bool { + if len(a) == 0 { + a = string(v1.ProtocolTCP) + } + if len(b) == 0 { + b = string(v1.ProtocolTCP) + } + + ret := strings.EqualFold(a, b) + return ret +} + +func (ic *ContainerEngine) importVolume(ctx context.Context, vol *libpod.Volume, tarFile *os.File) error { + volumeConfig, err := vol.Config() + if err != nil { + return err + } + + mountPoint := volumeConfig.MountPoint + if len(mountPoint) == 0 { + return errors.New("volume is not mounted anywhere on host") + } + + driver := volumeConfig.Driver + volumeOptions := volumeConfig.Options + volumeMountStatus, err := ic.VolumeMounted(ctx, vol.Name()) + if err != nil { + return err + } + + // Check if volume needs a mount and export only if volume is mounted + if vol.NeedsMount() && !volumeMountStatus.Value { + return fmt.Errorf("volume needs to be mounted but is not mounted on %s", mountPoint) + } + + // Check if volume is using `local` driver and has mount options type other than tmpfs + if len(driver) == 0 || driver == define.VolumeDriverLocal { + if mountOptionType, ok := volumeOptions["type"]; ok { + if mountOptionType != "tmpfs" && !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + } + + // dont care if volume is mounted or not we are gonna import everything to mountPoint + return utils.UntarToFileSystem(mountPoint, tarFile, nil) +} + // readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { var cm v1.ConfigMap @@ -1029,9 +1215,10 @@ func getBuildFile(imageName string, cwd string) (string, error) { return "", err } -func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, options entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { var ( - podNames []string + podNames []string + volumeNames []string ) reports := new(entities.PlayKubeReport) @@ -1077,10 +1264,17 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ e if deploymentYAML.Spec.Replicas != nil { numReplicas = *deploymentYAML.Spec.Replicas } - for i := 0; i < int(numReplicas); i++ { - podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) - podNames = append(podNames, podName) + if numReplicas > 1 { + logrus.Warnf("Limiting replica count to 1, more than one replica is not supported by Podman") } + podName := fmt.Sprintf("%s-pod", deploymentName) + podNames = append(podNames, podName) + case "PersistentVolumeClaim": + var pvcYAML v1.PersistentVolumeClaim + if err := yaml.Unmarshal(document, &pvcYAML); err != nil { + return nil, fmt.Errorf("unable to read YAML as Kube PersistentVolumeClaim: %w", err) + } + volumeNames = append(volumeNames, pvcYAML.Name) default: continue } @@ -1097,6 +1291,13 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ e return nil, err } + if options.Force { + reports.VolumeRmReport, err = ic.VolumeRm(ctx, volumeNames, entities.VolumeRmOptions{}) + if err != nil { + return nil, err + } + } + return reports, nil } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/system.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/system.go index da903df9..45731f4e 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/system.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/system.go @@ -136,7 +136,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) paths := []string{} for _, ctr := range ctrs { - paths = append(paths, ctr.Config().ConmonPidFile) + paths = append(paths, ctr.ConfigNoCopy().ConmonPidFile) } if len(paths) > 0 { @@ -160,6 +160,18 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) // SystemPrune removes unused data from the system. Pruning pods, containers, networks, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) + + if options.External { + if options.All || options.Volume || len(options.Filters) > 0 { + return nil, fmt.Errorf("system prune --external cannot be combined with other options") + } + err := ic.Libpod.GarbageCollect() + if err != nil { + return nil, err + } + return systemPruneReport, nil + } + filters := []string{} for k, v := range options.Filters { filters = append(filters, fmt.Sprintf("%s=%s", k, v[0])) @@ -253,7 +265,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System dfImages = []*entities.SystemDfImageReport{} ) - imageStats, err := ic.Libpod.LibimageRuntime().DiskUsage(ctx) + imageStats, totalImageSize, err := ic.Libpod.LibimageRuntime().DiskUsage(ctx) if err != nil { return nil, err } @@ -352,7 +364,9 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System } dfVolumes = append(dfVolumes, &report) } + return &entities.SystemDfReport{ + ImagesSize: totalImageSize, Images: dfImages, Containers: dfContainers, Volumes: dfVolumes, diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/terminal/terminal_common.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/terminal/terminal_common.go index 3dbdfc95..f9a012b7 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/terminal/terminal_common.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/terminal/terminal_common.go @@ -84,11 +84,13 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, streams.AttachInput = false } - if !startContainer { - if sigProxy { - ProxySignals(ctr) - } + if sigProxy { + // To prevent a race condition, install the signal handler + // before starting/attaching to the container. + ProxySignals(ctr) + } + if !startContainer { return ctr.Attach(streams, detachKeys, resize) } @@ -97,10 +99,6 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, return err } - if sigProxy { - ProxySignals(ctr) - } - if stdout == nil && stderr == nil { fmt.Printf("%s\n", ctr.ID()) } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/volumes.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/volumes.go index bdfd4d5a..5430f134 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/volumes.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/abi/volumes.go @@ -33,6 +33,11 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum } volumeOptions = append(volumeOptions, parsedOptions...) } + + if opts.IgnoreIfExists { + volumeOptions = append(volumeOptions, libpod.WithVolumeIgnoreIfExist()) + } + vol, err := ic.Libpod.NewVolume(ctx, volumeOptions...) if err != nil { return nil, err diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/runtime_libpod.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/runtime_libpod.go index a23a2365..bd73d20f 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/runtime_libpod.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/runtime_libpod.go @@ -146,7 +146,7 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo if fs.Changed("root") { storageSet = true - storageOpts.GraphRoot = cfg.Engine.StaticDir + storageOpts.GraphRoot = cfg.ContainersConf.Engine.StaticDir storageOpts.GraphDriverOptions = []string{} } if fs.Changed("runroot") { @@ -171,6 +171,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo storageOpts.GraphDriverOptions = cfg.StorageOpts } } + if fs.Changed("transient-store") { + options = append(options, libpod.WithTransientStore(cfg.TransientStore)) + } if opts.migrate { options = append(options, libpod.WithMigrate()) if opts.name != "" { @@ -205,8 +208,8 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO CLI flags for image config? // TODO CLI flag for signature policy? - if len(cfg.Engine.Namespace) > 0 { - options = append(options, libpod.WithNamespace(cfg.Engine.Namespace)) + if len(cfg.ContainersConf.Engine.Namespace) > 0 { + options = append(options, libpod.WithNamespace(cfg.ContainersConf.Engine.Namespace)) } if fs.Changed("runtime") { @@ -217,25 +220,25 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo options = append(options, libpod.WithConmonPath(cfg.ConmonPath)) } if fs.Changed("tmpdir") { - options = append(options, libpod.WithTmpDir(cfg.Engine.TmpDir)) + options = append(options, libpod.WithTmpDir(cfg.ContainersConf.Engine.TmpDir)) } if fs.Changed("network-cmd-path") { - options = append(options, libpod.WithNetworkCmdPath(cfg.Engine.NetworkCmdPath)) + options = append(options, libpod.WithNetworkCmdPath(cfg.ContainersConf.Engine.NetworkCmdPath)) } if fs.Changed("network-backend") { - options = append(options, libpod.WithNetworkBackend(cfg.Network.NetworkBackend)) + options = append(options, libpod.WithNetworkBackend(cfg.ContainersConf.Network.NetworkBackend)) } if fs.Changed("events-backend") { - options = append(options, libpod.WithEventsLogger(cfg.Engine.EventsLogger)) + options = append(options, libpod.WithEventsLogger(cfg.ContainersConf.Engine.EventsLogger)) } if fs.Changed("volumepath") { - options = append(options, libpod.WithVolumePath(cfg.Engine.VolumePath)) + options = append(options, libpod.WithVolumePath(cfg.ContainersConf.Engine.VolumePath)) } if fs.Changed("cgroup-manager") { - options = append(options, libpod.WithCgroupManager(cfg.Engine.CgroupManager)) + options = append(options, libpod.WithCgroupManager(cfg.ContainersConf.Engine.CgroupManager)) } else { unified, err := cgroups.IsCgroup2UnifiedMode() if err != nil { @@ -250,13 +253,13 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO flag to set libpod tmp dir? if fs.Changed("network-config-dir") { - options = append(options, libpod.WithCNIConfigDir(cfg.Network.NetworkConfigDir)) + options = append(options, libpod.WithNetworkConfigDir(cfg.ContainersConf.Network.NetworkConfigDir)) } if fs.Changed("default-mounts-file") { - options = append(options, libpod.WithDefaultMountsFile(cfg.Containers.DefaultMountsFile)) + options = append(options, libpod.WithDefaultMountsFile(cfg.ContainersConf.Containers.DefaultMountsFile)) } if fs.Changed("hooks-dir") { - options = append(options, libpod.WithHooksDir(cfg.Engine.HooksDir...)) + options = append(options, libpod.WithHooksDir(cfg.ContainersConf.Engine.HooksDir...)) } if fs.Changed("registries-conf") { options = append(options, libpod.WithRegistriesConf(cfg.RegistriesConf)) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/containers.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/containers.go index 0b573686..ab9a9cf9 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/containers.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "reflect" "strconv" "strings" "sync" @@ -37,17 +38,17 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) { - cons, err := getContainersByContext(ic.ClientCtx, false, false, namesOrIds) - if err != nil { - return nil, err - } - responses := make([]entities.WaitReport, 0, len(cons)) + responses := make([]entities.WaitReport, 0, len(namesOrIds)) options := new(containers.WaitOptions).WithCondition(opts.Condition).WithInterval(opts.Interval.String()) - for _, c := range cons { - response := entities.WaitReport{Id: c.ID} - exitCode, err := containers.Wait(ic.ClientCtx, c.ID, options) + for _, n := range namesOrIds { + response := entities.WaitReport{} + exitCode, err := containers.Wait(ic.ClientCtx, n, options) if err != nil { - response.Error = err + if opts.Ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) { + response.ExitCode = -1 + } else { + response.Error = err + } } else { response.ExitCode = exitCode } @@ -273,6 +274,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, } for i := range newReports { alreadyRemoved[newReports[i].Id] = true + newReports[i].RawInput = idToRawInput[newReports[i].Id] rmReports = append(rmReports, newReports[i]) } } @@ -353,17 +355,7 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error { - var ( - err error - w io.Writer - ) - if len(options.Output) > 0 { - w, err = os.Create(options.Output) - if err != nil { - return err - } - } - return containers.Export(ic.ClientCtx, nameOrID, w, nil) + return containers.Export(ic.ClientCtx, nameOrID, options.Output, nil) } func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { @@ -554,6 +546,13 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, return fmt.Errorf("you can only attach to running containers") } options := new(containers.AttachOptions).WithStream(true).WithDetachKeys(opts.DetachKeys) + if opts.SigProxy { + remoteProxySignals(ctr.ID, func(signal string) error { + killOpts := entities.KillOptions{All: false, Latest: false, Signal: signal} + _, err := ic.ContainerKill(ctx, []string{ctr.ID}, killOpts) + return err + }) + } return containers.Attach(ic.ClientCtx, nameOrID, opts.Stdin, opts.Stdout, opts.Stderr, nil, options) } @@ -619,7 +618,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s return sessionID, nil } -func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { +func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, sigProxy bool, input, output, errput *os.File) error { if output == nil && errput == nil { fmt.Printf("%s\n", name) } @@ -629,6 +628,14 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, if dk := detachKeys; dk != nil { options.WithDetachKeys(*dk) } + if sigProxy { + remoteProxySignals(name, func(signal string) error { + killOpts := entities.KillOptions{All: false, Latest: false, Signal: signal} + _, err := ic.ContainerKill(ic.ClientCtx, []string{name}, killOpts) + return err + }) + } + go func() { err := containers.Attach(ic.ClientCtx, name, input, output, errput, attachReady, options) attachErr <- err @@ -701,7 +708,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } ctrRunning := ctr.State == define.ContainerStateRunning.String() if options.Attach { - err = startAndAttach(ic, name, &options.DetachKeys, options.Stdin, options.Stdout, options.Stderr) + err = startAndAttach(ic, name, &options.DetachKeys, options.SigProxy, options.Stdin, options.Stdout, options.Stderr) if err == define.ErrDetach { // User manually detached // Exit cleanly immediately @@ -789,6 +796,30 @@ func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entitie } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() && opts.Spec.RawImageName != "" { + // If this is a checkpoint image, restore it. + getImageOptions := new(images.GetOptions).WithSize(false) + inspectReport, err := images.GetImage(ic.ClientCtx, opts.Spec.RawImageName, getImageOptions) + if err != nil { + return nil, fmt.Errorf("no such container or image: %s", opts.Spec.RawImageName) + } + if inspectReport != nil { + _, isCheckpointImage := inspectReport.Annotations[define.CheckpointAnnotationRuntimeName] + if isCheckpointImage { + restoreOptions := new(containers.RestoreOptions) + restoreOptions.WithName(opts.Spec.Name) + restoreOptions.WithPod(opts.Spec.Pod) + + restoreReport, err := containers.Restore(ic.ClientCtx, inspectReport.ID, restoreOptions) + if err != nil { + return nil, err + } + runReport := entities.ContainerRunReport{Id: restoreReport.Id} + return &runReport, nil + } + } + } + con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil) if err != nil { return nil, err @@ -804,7 +835,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if opts.CIDFile != "" { - if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil { + if err := util.CreateIDFile(opts.CIDFile, con.ID); err != nil { // If you fail to create CIDFile then remove the container _ = removeContainer(con.ID, true) return nil, err @@ -835,7 +866,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return err }) } - if err := startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream); err != nil { + if err := startAndAttach(ic, con.ID, &opts.DetachKeys, opts.SigProxy, opts.InputStream, opts.OutputStream, opts.ErrorStream); err != nil { if err == define.ErrDetach { return &report, nil } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/generate.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/generate.go deleted file mode 100644 index d3c3638c..00000000 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/generate.go +++ /dev/null @@ -1,51 +0,0 @@ -package tunnel - -import ( - "context" - "fmt" - - "github.com/containers/podman/v4/pkg/bindings/generate" - "github.com/containers/podman/v4/pkg/domain/entities" -) - -func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { - options := new( - generate.SystemdOptions). - WithUseName(opts.Name). - WithContainerPrefix(opts.ContainerPrefix). - WithNew(opts.New).WithNoHeader(opts.NoHeader). - WithTemplateUnitFile(opts.TemplateUnitFile). - WithPodPrefix(opts.PodPrefix). - WithSeparator(opts.Separator). - WithWants(opts.Wants). - WithAfter(opts.After). - WithRequires(opts.Requires). - WithAdditionalEnvVariables(opts.AdditionalEnvVariables) - - if opts.StartTimeout != nil { - options.WithStartTimeout(*opts.StartTimeout) - } - if opts.StopTimeout != nil { - options.WithStopTimeout(*opts.StopTimeout) - } - if opts.RestartPolicy != nil { - options.WithRestartPolicy(*opts.RestartPolicy) - } - if opts.RestartSec != nil { - options.WithRestartSec(*opts.RestartSec) - } - - return generate.Systemd(ic.ClientCtx, nameOrID, options) -} - -// GenerateKube Kubernetes YAML (v1 specification) for nameOrIDs -// -// Note: Caller is responsible for closing returned Reader -func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { - options := new(generate.KubeOptions).WithService(opts.Service) - return generate.Kube(ic.ClientCtx, nameOrIDs, options) -} - -func (ic *ContainerEngine) GenerateSpec(ctx context.Context, opts *entities.GenerateSpecOptions) (*entities.GenerateSpecReport, error) { - return nil, fmt.Errorf("GenerateSpec is not supported on the remote API") -} diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/images.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/images.go index 9ae1ff95..86e43137 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/images.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/images.go @@ -105,6 +105,10 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption } func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { + if opts.OciDecryptConfig != nil { + return nil, fmt.Errorf("decryption is not supported for remote clients") + } + options := new(images.PullOptions) options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS) options.WithVariant(opts.Variant).WithPassword(opts.Password) @@ -240,6 +244,13 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti } func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error { + if opts.Signers != nil { + return fmt.Errorf("forwarding Signers is not supported for remote clients") + } + if opts.OciEncryptConfig != nil { + return fmt.Errorf("encryption is not supported for remote clients") + } + options := new(images.PushOptions) options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format).WithRemoveSignatures(opts.RemoveSignatures).WithQuiet(opts.Quiet).WithCompressionFormat(opts.CompressionFormat).WithProgressWriter(opts.Writer) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/kube.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/kube.go new file mode 100644 index 00000000..6e42240f --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/kube.go @@ -0,0 +1,86 @@ +package tunnel + +import ( + "context" + "fmt" + "io" + + "github.com/containers/image/v5/types" + "github.com/containers/podman/v4/pkg/bindings/generate" + "github.com/containers/podman/v4/pkg/bindings/kube" + "github.com/containers/podman/v4/pkg/bindings/play" + "github.com/containers/podman/v4/pkg/domain/entities" +) + +func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { + options := new( + generate.SystemdOptions). + WithUseName(opts.Name). + WithContainerPrefix(opts.ContainerPrefix). + WithNew(opts.New).WithNoHeader(opts.NoHeader). + WithTemplateUnitFile(opts.TemplateUnitFile). + WithPodPrefix(opts.PodPrefix). + WithSeparator(opts.Separator). + WithWants(opts.Wants). + WithAfter(opts.After). + WithRequires(opts.Requires). + WithAdditionalEnvVariables(opts.AdditionalEnvVariables) + + if opts.StartTimeout != nil { + options.WithStartTimeout(*opts.StartTimeout) + } + if opts.StopTimeout != nil { + options.WithStopTimeout(*opts.StopTimeout) + } + if opts.RestartPolicy != nil { + options.WithRestartPolicy(*opts.RestartPolicy) + } + if opts.RestartSec != nil { + options.WithRestartSec(*opts.RestartSec) + } + + return generate.Systemd(ic.ClientCtx, nameOrID, options) +} + +// GenerateKube Kubernetes YAML (v1 specification) for nameOrIDs +// +// Note: Caller is responsible for closing returned Reader +func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { + options := new(generate.KubeOptions).WithService(opts.Service) + return generate.Kube(ic.ClientCtx, nameOrIDs, options) +} + +func (ic *ContainerEngine) GenerateSpec(ctx context.Context, opts *entities.GenerateSpecOptions) (*entities.GenerateSpecReport, error) { + return nil, fmt.Errorf("GenerateSpec is not supported on the remote API") +} + +func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { + options := new(kube.PlayOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password) + options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps) + options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Networks).WithSeccompProfileRoot(opts.SeccompProfileRoot) + options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs) + if len(opts.LogOptions) > 0 { + options.WithLogOptions(opts.LogOptions) + } + if opts.Annotations != nil { + options.WithAnnotations(opts.Annotations) + } + options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + options.WithSkipTLSVerify(s == types.OptionalBoolTrue) + } + if start := opts.Start; start != types.OptionalBoolUndefined { + options.WithStart(start == types.OptionalBoolTrue) + } + options.WithPublishPorts(opts.PublishPorts) + return play.KubeWithBody(ic.ClientCtx, body, options) +} + +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, options entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { + return play.DownWithBody(ic.ClientCtx, body, kube.DownOptions{Force: &options.Force}) +} + +func (ic *ContainerEngine) KubeApply(ctx context.Context, body io.Reader, opts entities.ApplyOptions) error { + options := new(kube.ApplyOptions).WithKubeconfig(opts.Kubeconfig).WithCACertFile(opts.CACertFile).WithNamespace(opts.Namespace) + return kube.ApplyWithBody(ic.ClientCtx, body, options) +} diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/manifest.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/manifest.go index 696d0a96..0de73a32 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/manifest.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/manifest.go @@ -3,7 +3,6 @@ package tunnel import ( "context" "encoding/json" - "errors" "fmt" "strings" @@ -11,6 +10,7 @@ import ( "github.com/containers/podman/v4/pkg/bindings/images" "github.com/containers/podman/v4/pkg/bindings/manifests" "github.com/containers/podman/v4/pkg/domain/entities" + envLib "github.com/containers/podman/v4/pkg/env" ) // ManifestCreate implements manifest create via ImageEngine @@ -33,8 +33,17 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti } // ManifestInspect returns contents of manifest list with given name -func (ir *ImageEngine) ManifestInspect(_ context.Context, name string) ([]byte, error) { - list, err := manifests.Inspect(ir.ClientCtx, name, nil) +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) { + options := new(manifests.InspectOptions) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + + list, err := manifests.InspectListData(ir.ClientCtx, name, options) if err != nil { return nil, fmt.Errorf("getting content of manifest list or image %s: %w", name, err) } @@ -51,6 +60,7 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames [] options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant) options.WithFeatures(opts.Features).WithImages(imageNames).WithOS(opts.OS).WithOSVersion(opts.OSVersion) options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile) + if len(opts.Annotation) != 0 { annotations := make(map[string]string) for _, annotationSpec := range opts.Annotation { @@ -60,8 +70,10 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames [] } annotations[spec[0]] = spec[1] } - options.WithAnnotation(annotations) + opts.Annotations = envLib.Join(opts.Annotations, annotations) } + options.WithAnnotation(opts.Annotations) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { options.WithSkipTLSVerify(true) @@ -79,7 +91,27 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames [] // ManifestAnnotate updates an entry of the manifest list func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, images string, opts entities.ManifestAnnotateOptions) (string, error) { - return "", errors.New("not implemented") + options := new(manifests.ModifyOptions).WithArch(opts.Arch).WithVariant(opts.Variant) + options.WithFeatures(opts.Features).WithOS(opts.OS).WithOSVersion(opts.OSVersion) + + if len(opts.Annotation) != 0 { + annotations := make(map[string]string) + for _, annotationSpec := range opts.Annotation { + spec := strings.SplitN(annotationSpec, "=", 2) + if len(spec) != 2 { + return "", fmt.Errorf("no value given for annotation %q", spec[0]) + } + annotations[spec[0]] = spec[1] + } + opts.Annotations = envLib.Join(opts.Annotations, annotations) + } + options.WithAnnotations(opts.Annotations) + + id, err := manifests.Annotate(ir.ClientCtx, name, []string{images}, options) + if err != nil { + return id, fmt.Errorf("annotating to manifest list %s: %w", name, err) + } + return id, nil } // ManifestRemoveDigest removes the digest from manifest list @@ -98,6 +130,10 @@ func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entitie // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { + if opts.Signers != nil { + return "", fmt.Errorf("forwarding Signers is not supported for remote clients") + } + options := new(images.PushOptions) options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat).WithQuiet(opts.Quiet).WithProgressWriter(opts.Writer) diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/network.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/network.go index 6e27b8e5..36d8ddce 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/network.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/network.go @@ -12,6 +12,11 @@ import ( "github.com/containers/podman/v4/pkg/errorhandling" ) +func (ic *ContainerEngine) NetworkUpdate(ctx context.Context, netName string, opts entities.NetworkUpdateOptions) error { + options := new(network.UpdateOptions).WithAddDNSServers(opts.AddDNSServers).WithRemoveDNSServers(opts.RemoveDNSServers) + return network.Update(ic.ClientCtx, netName, options) +} + func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]types.Network, error) { options := new(network.ListOptions).WithFilters(opts.Filters) return network.List(ic.ClientCtx, options) @@ -66,8 +71,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, nil } -func (ic *ContainerEngine) NetworkCreate(ctx context.Context, net types.Network) (*types.Network, error) { - net, err := network.Create(ic.ClientCtx, &net) +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, net types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) { + options := new(network.ExtraCreateOptions) + if createOptions != nil { + options = options.WithIgnoreIfExists(createOptions.IgnoreIfExists) + } + net, err := network.CreateWithOptions(ic.ClientCtx, &net, options) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/play.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/play.go deleted file mode 100644 index ee919568..00000000 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/play.go +++ /dev/null @@ -1,36 +0,0 @@ -package tunnel - -import ( - "context" - "io" - - "github.com/containers/image/v5/types" - "github.com/containers/podman/v4/pkg/bindings/kube" - "github.com/containers/podman/v4/pkg/bindings/play" - "github.com/containers/podman/v4/pkg/domain/entities" -) - -func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { - options := new(kube.PlayOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password) - options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps) - options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Networks).WithSeccompProfileRoot(opts.SeccompProfileRoot) - options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs) - if len(opts.LogOptions) > 0 { - options.WithLogOptions(opts.LogOptions) - } - if opts.Annotations != nil { - options.WithAnnotations(opts.Annotations) - } - options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns) - if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { - options.WithSkipTLSVerify(s == types.OptionalBoolTrue) - } - if start := opts.Start; start != types.OptionalBoolUndefined { - options.WithStart(start == types.OptionalBoolTrue) - } - return play.KubeWithBody(ic.ClientCtx, body, options) -} - -func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { - return play.DownWithBody(ic.ClientCtx, body) -} diff --git a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/system.go b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/system.go index 4185134d..29737775 100644 --- a/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/system.go +++ b/vendor/github.com/containers/podman/v4/pkg/domain/infra/tunnel/system.go @@ -19,7 +19,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) // SystemPrune prunes unused data from the system. func (ic *ContainerEngine) SystemPrune(ctx context.Context, opts entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { - options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters) + options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters).WithExternal(opts.External) return system.Prune(ic.ClientCtx, options) } diff --git a/vendor/github.com/containers/podman/v4/pkg/env/env_windows.go b/vendor/github.com/containers/podman/v4/pkg/env/env_windows.go index 5df08fad..1496dbfe 100644 --- a/vendor/github.com/containers/podman/v4/pkg/env/env_windows.go +++ b/vendor/github.com/containers/podman/v4/pkg/env/env_windows.go @@ -8,7 +8,7 @@ func ParseSlice(s []string) (map[string]string, error) { if len(e) > 0 && e[0] == '=' { // The legacy Windows CMD command interpreter uses a hack, where to emulate // DOS semantics, it uses an illegal (by windows definition) env name for - // state storage to avoid conlficting with user defined env names. This is + // state storage to avoid conflicting with user defined env names. This is // used to preserve drive letter paths. E.g., typing c: from another drive // will remember the last CWD because CMD stores it in an env named "=C:". // Since these are illegal, they are filtered from standard user access but diff --git a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/apps/v1/types.go b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/apps/v1/types.go index 3fa643ef..86e32061 100644 --- a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/apps/v1/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/apps/v1/types.go @@ -525,7 +525,7 @@ type RollingUpdateDaemonSet struct { MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` // The maximum number of nodes with an existing available DaemonSet pod that - // can have an updated DaemonSet pod during during an update. + // can have an updated DaemonSet pod during an update. // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). // This can not be 0 if MaxUnavailable is 0. // Absolute number is calculated from percentage by rounding up to a minimum of 1. @@ -537,7 +537,7 @@ type RollingUpdateDaemonSet struct { // pod is available (Ready for at least minReadySeconds) the old DaemonSet pod // on that node is marked deleted. If the old pod becomes unavailable for any // reason (Ready transitions to false, is evicted, or is drained) an updated - // pod is immediatedly created on that node without considering surge limits. + // pod is immediately created on that node without considering surge limits. // Allowing surge implies the possibility that the resources consumed by the // daemonset on any given node can double if the readiness check fails, and // so resource intensive daemonsets should take into account that they may @@ -777,7 +777,7 @@ type ReplicaSetSpec struct { // ReplicaSetStatus represents the current status of a ReplicaSet. type ReplicaSetStatus struct { - // Replicas is the most recently oberved number of replicas. + // Replicas is the most recently observed number of replicas. // More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller Replicas int32 `json:"replicas"` diff --git a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/annotation_key_constants.go b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/annotation_key_constants.go index 7fde0912..eb9517e1 100644 --- a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/annotation_key_constants.go +++ b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/annotation_key_constants.go @@ -121,7 +121,7 @@ const ( EndpointsLastChangeTriggerTime = "endpoints.kubernetes.io/last-change-trigger-time" // EndpointsOverCapacity will be set on an Endpoints resource when it - // exceeds the maximum capacity of 1000 addresses. Inititially the Endpoints + // exceeds the maximum capacity of 1000 addresses. Initially the Endpoints // controller will set this annotation with a value of "warning". In a // future release, the controller may set this annotation with a value of // "truncated" to indicate that any addresses exceeding the limit of 1000 diff --git a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/types.go b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/types.go index 114c552e..2020cbe4 100644 --- a/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/k8s.io/api/core/v1/types.go @@ -324,7 +324,7 @@ const ( PersistentVolumeClaimFileSystemResizePending PersistentVolumeClaimConditionType = "FileSystemResizePending" ) -// PersistentVolumeClaimCondition contails details about state of pvc +// PersistentVolumeClaimCondition contains details about state of pvc type PersistentVolumeClaimCondition struct { Type PersistentVolumeClaimConditionType `json:"type"` Status ConditionStatus `json:"status"` @@ -1249,7 +1249,7 @@ type Container struct { // sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the // first client attaches to stdin, and then remains open and accepts data until the client disconnects, // at which time stdin is closed and remains closed until the container is restarted. If this - // flag is false, a container processes that reads from stdin will never receive an EOF. + // flag is false, a container process that reads from stdin will never receive an EOF. // Default is false // +optional StdinOnce bool `json:"stdinOnce,omitempty"` @@ -2367,7 +2367,7 @@ type EphemeralContainerCommon struct { // sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the // first client attaches to stdin, and then remains open and accepts data until the client disconnects, // at which time stdin is closed and remains closed until the container is restarted. If this - // flag is false, a container processes that reads from stdin will never receive an EOF. + // flag is false, a container process that reads from stdin will never receive an EOF. // Default is false // +optional StdinOnce bool `json:"stdinOnce,omitempty"` @@ -2393,7 +2393,7 @@ var _ = Container(EphemeralContainerCommon{}) type EphemeralContainer struct { // Ephemeral containers have all of the fields of Container, plus additional fields // specific to ephemeral containers. Fields in common with Container are in the - // following inlined struct so than an EphemeralContainer may easily be converted + // following inlined struct so that an EphemeralContainer may easily be converted // to a Container. EphemeralContainerCommon `json:",inline"` @@ -2639,7 +2639,7 @@ type ReplicationControllerSpec struct { // ReplicationControllerStatus represents the current status of a replication // controller. type ReplicationControllerStatus struct { - // Replicas is the most recently oberved number of replicas. + // Replicas is the most recently observed number of replicas. // More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller Replicas int32 `json:"replicas"` @@ -4490,3 +4490,248 @@ type PortStatus struct { // +kubebuilder:validation:MaxLength=316 Error *string `json:"error,omitempty"` } + +// The following has been copied from https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/api/v1/types.go +// It holds the struct information for a kubeconfig that let's us unmarshal a given kubeconfig so that we can deploy workloads +// to the cluster with podman generate kube. + +// Config holds the information needed to build connect to remote kubernetes clusters as a given user +type Config struct { + // Legacy field from pkg/api/types.go TypeMeta. + // TODO(jlowdermilk): remove this after eliminating downstream dependencies. + // +k8s:conversion-gen=false + // +optional + Kind string `json:"kind,omitempty"` + // Legacy field from pkg/api/types.go TypeMeta. + // TODO(jlowdermilk): remove this after eliminating downstream dependencies. + // +k8s:conversion-gen=false + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // Preferences holds general information to be use for cli interactions + Preferences Preferences `json:"preferences"` + // Clusters is a map of referencable names to cluster configs + Clusters []NamedCluster `json:"clusters"` + // AuthInfos is a map of referencable names to user configs + AuthInfos []NamedAuthInfo `json:"users"` + // Contexts is a map of referencable names to context configs + Contexts []NamedContext `json:"contexts"` + // CurrentContext is the name of the context that you would like to use by default + CurrentContext string `json:"current-context"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + // +optional + Extensions []NamedExtension `json:"extensions,omitempty"` +} + +type Preferences struct { + // +optional + Colors bool `json:"colors,omitempty"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + // +optional + Extensions []NamedExtension `json:"extensions,omitempty"` +} + +// Cluster contains information about how to communicate with a kubernetes cluster +type Cluster struct { + // Server is the address of the kubernetes cluster (https://hostname:port). + Server string `json:"server"` + // TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used. + // +optional + TLSServerName string `json:"tls-server-name,omitempty"` + // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. + // +optional + InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` + // CertificateAuthority is the path to a cert file for the certificate authority. + // +optional + CertificateAuthority string `json:"certificate-authority,omitempty"` + // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority + // +optional + CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"` + // ProxyURL is the URL to the proxy to be used for all requests made by this + // client. URLs with "http", "https", and "socks5" schemes are supported. If + // this configuration is not provided or the empty string, the client + // attempts to construct a proxy configuration from http_proxy and + // https_proxy environment variables. If these environment variables are not + // set, the client does not attempt to proxy requests. + // + // socks5 proxying does not currently support spdy streaming endpoints (exec, + // attach, port forward). + // +optional + ProxyURL string `json:"proxy-url,omitempty"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + // +optional + Extensions []NamedExtension `json:"extensions,omitempty"` +} + +// AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are. +type AuthInfo struct { + // ClientCertificate is the path to a client cert file for TLS. + // +optional + ClientCertificate string `json:"client-certificate,omitempty"` + // ClientCertificateData contains PEM-encoded data from a client cert file for TLS. Overrides ClientCertificate + // +optional + ClientCertificateData []byte `json:"client-certificate-data,omitempty"` + // ClientKey is the path to a client key file for TLS. + // +optional + ClientKey string `json:"client-key,omitempty"` + // ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey + // +optional + ClientKeyData []byte `json:"client-key-data,omitempty" datapolicy:"security-key"` + // Token is the bearer token for authentication to the kubernetes cluster. + // +optional + Token string `json:"token,omitempty" datapolicy:"token"` + // TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence. + // +optional + TokenFile string `json:"tokenFile,omitempty"` + // Impersonate is the username to impersonate. The name matches the flag. + // +optional + Impersonate string `json:"as,omitempty"` + // ImpersonateUID is the uid to impersonate. + // +optional + ImpersonateUID string `json:"as-uid,omitempty"` + // ImpersonateGroups is the groups to impersonate. + // +optional + ImpersonateGroups []string `json:"as-groups,omitempty"` + // ImpersonateUserExtra contains additional information for impersonated user. + // +optional + ImpersonateUserExtra map[string][]string `json:"as-user-extra,omitempty"` + // Username is the username for basic authentication to the kubernetes cluster. + // +optional + Username string `json:"username,omitempty"` + // Password is the password for basic authentication to the kubernetes cluster. + // +optional + Password string `json:"password,omitempty" datapolicy:"password"` + // AuthProvider specifies a custom authentication plugin for the kubernetes cluster. + // +optional + AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"` + // Exec specifies a custom exec-based authentication plugin for the kubernetes cluster. + // +optional + Exec *ExecConfig `json:"exec,omitempty"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + // +optional + Extensions []NamedExtension `json:"extensions,omitempty"` +} + +// Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with) +type Context struct { + // Cluster is the name of the cluster for this context + Cluster string `json:"cluster"` + // AuthInfo is the name of the authInfo for this context + AuthInfo string `json:"user"` + // Namespace is the default namespace to use on unspecified requests + // +optional + Namespace string `json:"namespace,omitempty"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + // +optional + Extensions []NamedExtension `json:"extensions,omitempty"` +} + +// NamedCluster relates nicknames to cluster information +type NamedCluster struct { + // Name is the nickname for this Cluster + Name string `json:"name"` + // Cluster holds the cluster information + Cluster Cluster `json:"cluster"` +} + +// NamedContext relates nicknames to context information +type NamedContext struct { + // Name is the nickname for this Context + Name string `json:"name"` + // Context holds the context information + Context Context `json:"context"` +} + +// NamedAuthInfo relates nicknames to auth information +type NamedAuthInfo struct { + // Name is the nickname for this AuthInfo + Name string `json:"name"` + // AuthInfo holds the auth information + AuthInfo AuthInfo `json:"user"` +} + +// NamedExtension relates nicknames to extension information +type NamedExtension struct { + // Name is the nickname for this Extension + Name string `json:"name"` + // Extension holds the extension information + Extension interface{} `json:"extension"` +} + +// AuthProviderConfig holds the configuration for a specified auth provider. +type AuthProviderConfig struct { + Name string `json:"name"` + Config map[string]string `json:"config"` +} + +// ExecConfig specifies a command to provide client credentials. The command is exec'd +// and outputs structured stdout holding credentials. +// +// See the client.authentication.k8s.io API group for specifications of the exact input +// and output format +type ExecConfig struct { + // Command to execute. + Command string `json:"command"` + // Arguments to pass to the command when executing it. + // +optional + Args []string `json:"args"` + // Env defines additional environment variables to expose to the process. These + // are unioned with the host's environment, as well as variables client-go uses + // to pass argument to the plugin. + // +optional + Env []ExecEnvVar `json:"env"` + + // Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + // the same encoding version as the input. + APIVersion string `json:"apiVersion,omitempty"` + + // This text is shown to the user when the executable doesn't seem to be + // present. For example, `brew install foo-cli` might be a good InstallHint for + // foo-cli on Mac OS systems. + InstallHint string `json:"installHint,omitempty"` + + // ProvideClusterInfo determines whether or not to provide cluster information, + // which could potentially contain very large CA data, to this exec plugin as a + // part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + // to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + // reading this environment variable. + ProvideClusterInfo bool `json:"provideClusterInfo"` + + // InteractiveMode determines this plugin's relationship with standard input. Valid + // values are "Never" (this exec plugin never uses standard input), "IfAvailable" (this + // exec plugin wants to use standard input if it is available), or "Always" (this exec + // plugin requires standard input to function). See ExecInteractiveMode values for more + // details. + // + // If APIVersion is client.authentication.k8s.io/v1alpha1 or + // client.authentication.k8s.io/v1beta1, then this field is optional and defaults + // to "IfAvailable" when unset. Otherwise, this field is required. + //+optional + InteractiveMode ExecInteractiveMode `json:"interactiveMode,omitempty"` +} + +// ExecEnvVar is used for setting environment variables when executing an exec-based +// credential plugin. +type ExecEnvVar struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// ExecInteractiveMode is a string that describes an exec plugin's relationship with standard input. +type ExecInteractiveMode string + +const ( + // NeverExecInteractiveMode declares that this exec plugin never needs to use standard + // input, and therefore the exec plugin will be run regardless of whether standard input is + // available for user input. + NeverExecInteractiveMode ExecInteractiveMode = "Never" + // IfAvailableExecInteractiveMode declares that this exec plugin would like to use standard input + // if it is available, but can still operate if standard input is not available. Therefore, the + // exec plugin will be run regardless of whether stdin is available for user input. If standard + // input is available for user input, then it will be provided to this exec plugin. + IfAvailableExecInteractiveMode ExecInteractiveMode = "IfAvailable" + // AlwaysExecInteractiveMode declares that this exec plugin requires standard input in order to + // run, and therefore the exec plugin will only be run if standard input is available for user + // input. If standard input is not available for user input, then the exec plugin will not be run + // and an error will be returned by the exec plugin runner. + AlwaysExecInteractiveMode ExecInteractiveMode = "Always" +) diff --git a/vendor/github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types.go b/vendor/github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types.go index 11751412..7482266e 100644 --- a/vendor/github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types.go +++ b/vendor/github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types.go @@ -1190,7 +1190,7 @@ const ( // or a string representing a sub-field or item. The string will follow one of these four formats: // 'f:', where is the name of a field in a struct, or key in a map // 'v:', where is the exact json formatted value of a list item -// 'i:', where is position of a item in a list +// 'i:', where is position of an item in a list // 'k:', where is a map of a list item's key fields to their unique values // If a key maps to an empty Fields value, the field that key represents is part of the set. // diff --git a/vendor/github.com/containers/podman/v4/pkg/lookup/lookup.go b/vendor/github.com/containers/podman/v4/pkg/lookup/lookup.go index b8ac3046..8413f5b6 100644 --- a/vendor/github.com/containers/podman/v4/pkg/lookup/lookup.go +++ b/vendor/github.com/containers/podman/v4/pkg/lookup/lookup.go @@ -21,7 +21,7 @@ type Overrides struct { ContainerEtcGroupPath string } -// GetUserGroupInfo takes string forms of the the container's mount path and the container user and +// GetUserGroupInfo takes string forms of the container's mount path and the container user and // returns a ExecUser with uid, gid, sgids, and home. And override can be provided for defaults. func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) { var ( diff --git a/vendor/github.com/containers/podman/v4/pkg/namespaces/namespaces.go b/vendor/github.com/containers/podman/v4/pkg/namespaces/namespaces.go index 6dd576ea..4d4e496a 100644 --- a/vendor/github.com/containers/podman/v4/pkg/namespaces/namespaces.go +++ b/vendor/github.com/containers/podman/v4/pkg/namespaces/namespaces.go @@ -19,6 +19,7 @@ const ( privateType = "private" shareableType = "shareable" slirpType = "slirp4netns" + pastaType = "pasta" ) // KeepIDUserNsOptions defines how to keepIDmatically create a user namespace. @@ -440,6 +441,11 @@ func (n NetworkMode) IsSlirp4netns() bool { return n == slirpType || strings.HasPrefix(string(n), slirpType+":") } +// IsPasta indicates if we are running a rootless network stack using pasta +func (n NetworkMode) IsPasta() bool { + return n == pastaType || strings.HasPrefix(string(n), pastaType+":") +} + // IsNS indicates a network namespace passed in by path (ns:) func (n NetworkMode) IsNS() bool { return strings.HasPrefix(string(n), nsType) @@ -461,5 +467,5 @@ func (n NetworkMode) IsPod() bool { // IsUserDefined indicates user-created network func (n NetworkMode) IsUserDefined() bool { - return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns() && !n.IsNS() + return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns() && !n.IsPasta() && !n.IsNS() } diff --git a/vendor/github.com/containers/podman/v4/pkg/ps/ps.go b/vendor/github.com/containers/podman/v4/pkg/ps/ps.go index 37da0b11..30c1c8c8 100644 --- a/vendor/github.com/containers/podman/v4/pkg/ps/ps.go +++ b/vendor/github.com/containers/podman/v4/pkg/ps/ps.go @@ -11,6 +11,7 @@ import ( "strings" "time" + libnetworkTypes "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" @@ -50,7 +51,14 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp filterFuncs = append(filterFuncs, runningOnly) } - cons, err := runtime.GetContainers(filterFuncs...) + // Load the containers with their states populated. This speeds things + // up considerably as we use a signel DB connection to load the + // containers' states instead of one per container. + // + // This may return slightly outdated states but that's acceptable for + // listing containers; any state is outdated the point a container lock + // gets released. + cons, err := runtime.GetContainers(true, filterFuncs...) if err != nil { return nil, err } @@ -134,6 +142,9 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities startedTime time.Time exitedTime time.Time cgroup, ipc, mnt, net, pidns, user, uts string + portMappings []libnetworkTypes.PortMapping + networks []string + healthStatus string ) batchErr := ctr.Batch(func(c *libpod.Container) error { @@ -143,7 +154,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } } - conConfig = c.Config() + conConfig = c.ConfigNoCopy() conState, err = c.State() if err != nil { return fmt.Errorf("unable to obtain container state: %w", err) @@ -167,6 +178,21 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities return fmt.Errorf("unable to obtain container pid: %w", err) } + portMappings, err = c.PortMappings() + if err != nil { + return err + } + + networks, err = c.Networks() + if err != nil { + return err + } + + healthStatus, err = c.HealthCheckStatus() + if err != nil { + return err + } + if !opts.Size && !opts.Namespace { return nil } @@ -203,16 +229,6 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities return entities.ListContainer{}, batchErr } - portMappings, err := ctr.PortMappings() - if err != nil { - return entities.ListContainer{}, err - } - - networks, err := ctr.Networks() - if err != nil { - return entities.ListContainer{}, err - } - ps := entities.ListContainer{ AutoRemove: ctr.AutoRemove(), Command: conConfig.Command, @@ -234,6 +250,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities Size: size, StartedAt: startedTime.Unix(), State: conState.String(), + Status: healthStatus, } if opts.Pod && len(conConfig.Pod) > 0 { podName, err := rt.GetName(conConfig.Pod) @@ -258,12 +275,6 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } } - if hc, err := ctr.HealthCheckStatus(); err == nil { - ps.Status = hc - } else { - logrus.Debug(err) - } - return ps, nil } diff --git a/vendor/github.com/containers/podman/v4/pkg/rctl/rctl.go b/vendor/github.com/containers/podman/v4/pkg/rctl/rctl.go index 135cc60c..dccf9e7c 100644 --- a/vendor/github.com/containers/podman/v4/pkg/rctl/rctl.go +++ b/vendor/github.com/containers/podman/v4/pkg/rctl/rctl.go @@ -3,9 +3,6 @@ package rctl -// #include -import "C" - import ( "bytes" "fmt" diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless.go b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless.go index 94535f45..6b9b30f3 100644 --- a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless.go +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless.go @@ -29,7 +29,7 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { } // It could not join the pause process, let's lock the file before trying to delete it. - pidFileLock, err := lockfile.GetLockfile(pausePidPath) + pidFileLock, err := lockfile.GetLockFile(pausePidPath) if err != nil { // The file was deleted by another process. if os.IsNotExist(err) { @@ -40,9 +40,7 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { pidFileLock.Lock() defer func() { - if pidFileLock.Locked() { - pidFileLock.Unlock() - } + pidFileLock.Unlock() }() // Now the pause PID file is locked. Try to join once again in case it changed while it was not locked. @@ -137,7 +135,7 @@ func GetAvailableGids() (int64, error) { return countAvailableIDs(gids), nil } -// findIDInMappings find the the mapping that contains the specified ID. +// findIDInMappings find the mapping that contains the specified ID. // It assumes availableMappings is sorted by ID. func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap { i := sort.Search(len(availableMappings), func(i int) bool { diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.c b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.c new file mode 100644 index 00000000..f8f0ba4b --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +static int open_files_max_fd; +static fd_set *open_files_set; + +int +is_fd_inherited(int fd) +{ + if (open_files_set == NULL || fd > open_files_max_fd || fd < 0) + return 0; + + return FD_ISSET(fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])) ? 1 : 0; +} + +static void __attribute__((constructor)) init() +{ + /* Store how many FDs were open before the Go runtime kicked in. */ + DIR* d = opendir ("/dev/fd"); + if (d) + { + struct dirent *ent; + size_t size = 0; + + for (ent = readdir (d); ent; ent = readdir (d)) + { + int fd; + + if (ent->d_name[0] == '.') + continue; + + fd = atoi (ent->d_name); + if (fd == dirfd (d)) { + continue; + } + + if (fd >= size * FD_SETSIZE) + { + int i; + size_t new_size; + + new_size = (fd / FD_SETSIZE) + 1; + open_files_set = realloc (open_files_set, new_size * sizeof (fd_set)); + if (open_files_set == NULL) + _exit (EXIT_FAILURE); + + for (i = size; i < new_size; i++) + FD_ZERO (&(open_files_set[i])); + + size = new_size; + } + + if (fd > open_files_max_fd) { + open_files_max_fd = fd; + } + + FD_SET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])); + } + } +} diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.go b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.go new file mode 100644 index 00000000..525f789d --- /dev/null +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_freebsd.go @@ -0,0 +1,69 @@ +//go:build freebsd && cgo +// +build freebsd,cgo + +package rootless + +import ( + "errors" + + "github.com/containers/storage/pkg/idtools" +) + +// extern int is_fd_inherited(int fd); +import "C" + +// IsRootless returns whether the user is rootless +func IsRootless() bool { + return false +} + +// BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed +// into a new user namespace and the return code from the re-executed podman process. +// If podman was re-executed the caller needs to propagate the error code returned by the child +// process. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration. +func BecomeRootInUserNS(pausePid string) (bool, int, error) { + return false, -1, errors.New("Rootless mode is not supported on FreeBSD - run podman as root") +} + +// GetRootlessUID returns the UID of the user in the parent userNS +func GetRootlessUID() int { + return -1 +} + +// GetRootlessGID returns the GID of the user in the parent userNS +func GetRootlessGID() int { + return -1 +} + +// TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths. +// This is useful when there are already running containers and we +// don't have a pause process yet. We can use the paths to the conmon +// processes to attempt joining their namespaces. +// If needNewNamespace is set, the file is read from a temporary user +// namespace, this is useful for containers that are running with a +// different uidmap and the unprivileged user has no way to read the +// file owned by the root in the container. +func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []string) (bool, int, error) { + return false, -1, errors.New("this function is not supported on this os") +} + +// ConfigurationMatches checks whether the additional uids/gids configured for the user +// match the current user namespace. +func ConfigurationMatches() (bool, error) { + return true, nil +} + +// GetConfiguredMappings returns the additional IDs configured for the current user. +func GetConfiguredMappings(quiet bool) ([]idtools.IDMap, []idtools.IDMap, error) { + return nil, nil, errors.New("this function is not supported on this os") +} + +// ReadMappingsProc returns the uid_map and gid_map +func ReadMappingsProc(path string) ([]idtools.IDMap, error) { + return nil, nil +} + +// IsFdInherited checks whether the fd is opened and valid to use +func IsFdInherited(fd int) bool { + return int(C.is_fd_inherited(C.int(fd))) > 0 +} diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.c b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.c index b47f3d2b..7e8b3f78 100644 --- a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.c +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,9 @@ #include #include +#define ETC_PREEXEC_HOOKS "/etc/containers/pre-exec-hooks" +#define LIBEXECPODMAN "/usr/libexec/podman" + #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ @@ -117,6 +121,155 @@ rootless_gid () return rootless_gid_init; } +/* exec the specified executable and exit if it fails. */ +static void +exec_binary (const char *path, char **argv, int argc) +{ + int r, status = 0; + pid_t pid; + + pid = fork (); + if (pid < 0) + { + fprintf (stderr, "fork: %m\n"); + exit (EXIT_FAILURE); + } + if (pid == 0) + { + size_t i; + char **newargv = malloc ((argc + 2) * sizeof(char *)); + if (!newargv) + { + fprintf (stderr, "malloc: %m\n"); + exit (EXIT_FAILURE); + } + newargv[0] = (char*) path; + for (i = 0; i < argc; i++) + newargv[i+1] = argv[i]; + + newargv[i+1] = NULL; + errno = 0; + execv (path, newargv); + /* If the file was deleted in the meanwhile, return success. */ + if (errno == ENOENT) + exit (EXIT_SUCCESS); + exit (EXIT_FAILURE); + } + + r = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (r < 0) + { + fprintf (stderr, "waitpid: %m\n"); + exit (EXIT_FAILURE); + } + if (WIFEXITED(status) && WEXITSTATUS (status)) + { + fprintf (stderr, "external preexec hook %s failed\n", path); + exit (WEXITSTATUS(status)); + } + if (WIFSIGNALED (status)) + { + fprintf (stderr, "external preexec hook %s failed\n", path); + exit (127+WTERMSIG (status)); + } + if (WIFSTOPPED (status)) + { + fprintf (stderr, "external preexec hook %s failed\n", path); + exit (EXIT_FAILURE); + } +} + +static void +do_preexec_hooks_dir (const char *dir, char **argv, int argc) +{ + cleanup_free char *buffer = NULL; + cleanup_dir DIR *d = NULL; + size_t i, nfiles = 0; + struct dirent *de; + + /* Store how many FDs were open before the Go runtime kicked in. */ + d = opendir (dir); + if (!d) + { + if (errno != ENOENT) + { + fprintf (stderr, "opendir %s: %m\n", dir); + exit (EXIT_FAILURE); + } + return; + } + + errno = 0; + + for (de = readdir (d); de; de = readdir (d)) + { + buffer = realloc (buffer, (nfiles + 1) * (NAME_MAX + 1)); + if (buffer == NULL) + { + fprintf (stderr, "realloc buffer: %m\n"); + exit (EXIT_FAILURE); + } + + if (de->d_type != DT_REG) + continue; + + strncpy (buffer + nfiles * (NAME_MAX + 1), de->d_name, NAME_MAX + 1); + nfiles++; + buffer[nfiles * (NAME_MAX + 1)] = '\0'; + } + + qsort (buffer, nfiles, NAME_MAX + 1, (int (*)(const void *, const void *)) strcmp); + + for (i = 0; i < nfiles; i++) + { + const char *fname = buffer + i * (NAME_MAX + 1); + char path[PATH_MAX]; + struct stat st; + int ret; + + ret = snprintf (path, PATH_MAX, "%s/%s", dir, fname); + if (ret == PATH_MAX) + { + fprintf (stderr, "internal error: path too long\n"); + exit (EXIT_FAILURE); + } + + ret = stat (path, &st); + if (ret < 0) + { + /* Ignore the failure if the file was deleted. */ + if (errno == ENOENT) + continue; + + fprintf (stderr, "stat %s: %m\n", path); + exit (EXIT_FAILURE); + } + + /* Not an executable. */ + if ((st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) + continue; + + exec_binary (path, argv, argc); + errno = 0; + } + + if (errno) + { + fprintf (stderr, "readdir %s: %m\n", dir); + exit (EXIT_FAILURE); + } +} + +static void +do_preexec_hooks (char **argv, int argc) +{ + char *preexec_hooks = getenv ("PODMAN_PREEXEC_HOOKS_DIR"); + do_preexec_hooks_dir (LIBEXECPODMAN "/pre-exec-hooks", argv, argc); + do_preexec_hooks_dir (ETC_PREEXEC_HOOKS, argv, argc); + if (preexec_hooks && preexec_hooks[0]) + do_preexec_hooks_dir (preexec_hooks, argv, argc); +} + static void do_pause () { @@ -134,7 +287,7 @@ do_pause () sigaction (sig[i], &act, NULL); /* Attempt to execv catatonit to keep the pause process alive. */ - execl ("/usr/libexec/podman/catatonit", "catatonit", "-P", NULL); + execl (LIBEXECPODMAN "catatonit", "catatonit", "-P", NULL); execl ("/usr/bin/catatonit", "catatonit", "-P", NULL); /* and if the catatonit executable could not be found, fallback here... */ @@ -144,7 +297,7 @@ do_pause () } static char ** -get_cmd_line_args () +get_cmd_line_args (int *argc_out) { cleanup_free char *buffer = NULL; cleanup_close int fd = -1; @@ -204,13 +357,15 @@ get_cmd_line_args () /* Move ownership. */ buffer = NULL; + if (argc_out) + *argc_out = argc; + return argv; } static bool -can_use_shortcut () +can_use_shortcut (char **argv) { - cleanup_free char **argv = NULL; cleanup_free char *argv0 = NULL; bool ret = true; int argc; @@ -219,10 +374,6 @@ can_use_shortcut () return false; #endif - argv = get_cmd_line_args (); - if (argv == NULL) - return false; - argv0 = argv[0]; if (strstr (argv[0], "podman") == NULL) @@ -287,7 +438,9 @@ static void __attribute__((constructor)) init() const char *listen_pid; const char *listen_fds; const char *listen_fdnames; + cleanup_free char **argv = NULL; cleanup_dir DIR *d = NULL; + int argc; pause = getenv ("_PODMAN_PAUSE"); if (pause && pause[0]) @@ -337,30 +490,40 @@ static void __attribute__((constructor)) init() } } - listen_pid = getenv("LISTEN_PID"); - listen_fds = getenv("LISTEN_FDS"); - listen_fdnames = getenv("LISTEN_FDNAMES"); - - if (listen_pid != NULL && listen_fds != NULL && strtol(listen_pid, NULL, 10) == getpid()) - { - // save systemd socket environment for rootless child - do_socket_activation = true; - saved_systemd_listen_pid = strdup(listen_pid); - saved_systemd_listen_fds = strdup(listen_fds); - if (listen_fdnames != NULL) - saved_systemd_listen_fdnames = strdup(listen_fdnames); - if (saved_systemd_listen_pid == NULL - || saved_systemd_listen_fds == NULL) - { - fprintf (stderr, "save socket listen environments error: %m\n"); - _exit (EXIT_FAILURE); - } - } + argv = get_cmd_line_args (&argc); + if (argv == NULL) + { + fprintf(stderr, "cannot retrieve cmd line"); + _exit (EXIT_FAILURE); + } + + if (geteuid () != 0 || getenv ("_CONTAINERS_USERNS_CONFIGURED") == NULL) + do_preexec_hooks(argv, argc); + + listen_pid = getenv("LISTEN_PID"); + listen_fds = getenv("LISTEN_FDS"); + listen_fdnames = getenv("LISTEN_FDNAMES"); + + if (listen_pid != NULL && listen_fds != NULL && strtol(listen_pid, NULL, 10) == getpid()) + { + // save systemd socket environment for rootless child + do_socket_activation = true; + saved_systemd_listen_pid = strdup(listen_pid); + saved_systemd_listen_fds = strdup(listen_fds); + if (listen_fdnames != NULL) + saved_systemd_listen_fdnames = strdup(listen_fdnames); + if (saved_systemd_listen_pid == NULL + || saved_systemd_listen_fds == NULL) + { + fprintf (stderr, "save socket listen environments error: %m\n"); + _exit (EXIT_FAILURE); + } + } /* Shortcut. If we are able to join the pause pid file, do it now so we don't need to re-exec. */ xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); - if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) + if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut (argv)) { cleanup_free char *cwd = NULL; cleanup_close int userns_fd = -1; @@ -648,7 +811,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) sprintf (uid, "%d", geteuid ()); sprintf (gid, "%d", getegid ()); - argv = get_cmd_line_args (); + argv = get_cmd_line_args (NULL); if (argv == NULL) { fprintf (stderr, "cannot read argv: %m\n"); @@ -898,7 +1061,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re _exit (EXIT_FAILURE); } - argv = get_cmd_line_args (); + argv = get_cmd_line_args (NULL); if (argv == NULL) { fprintf (stderr, "cannot read argv: %m\n"); diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.go b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.go index 7de50eaf..1937a133 100644 --- a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_linux.go @@ -172,7 +172,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { if err != nil { return false, 0, err } - if hasCapSysAdmin || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { + if (os.Geteuid() == 0 && hasCapSysAdmin) || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { return false, 0, nil } @@ -193,7 +193,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { } // GetConfiguredMappings returns the additional IDs configured for the current user. -func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { +func GetConfiguredMappings(quiet bool) ([]idtools.IDMap, []idtools.IDMap, error) { var uids, gids []idtools.IDMap username := os.Getenv("USER") if username == "" { @@ -211,7 +211,7 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { mappings, err := idtools.NewIDMappings(username, username) if err != nil { logLevel := logrus.ErrorLevel - if os.Geteuid() == 0 && GetRootlessUID() == 0 { + if quiet || (os.Geteuid() == 0 && GetRootlessUID() == 0) { logLevel = logrus.DebugLevel } logrus.StandardLogger().Logf(logLevel, "cannot find UID/GID for user %s: %v - check rootless mode in man pages.", username, err) @@ -223,6 +223,11 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { } func copyMappings(from, to string) error { + // when running as non-root always go through the newuidmap/newgidmap + // configuration since this is the expectation when running on Kubernetes + if os.Geteuid() != 0 { + return errors.New("copying mappings is allowed only for root") + } content, err := os.ReadFile(from) if err != nil { return err @@ -243,7 +248,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo return false, 0, err } - if hasCapSysAdmin || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { + if (os.Geteuid() == 0 && hasCapSysAdmin) || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { if os.Getenv("_CONTAINERS_USERNS_CONFIGURED") == "init" { return false, 0, runInUser() } @@ -317,7 +322,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo return false, -1, fmt.Errorf("cannot re-exec process") } - uids, gids, err := GetConfiguredMappings() + uids, gids, err := GetConfiguredMappings(false) if err != nil { return false, -1, err } @@ -592,7 +597,7 @@ func ConfigurationMatches() (bool, error) { return true, nil } - uids, gids, err := GetConfiguredMappings() + uids, gids, err := GetConfiguredMappings(false) if err != nil { return false, err } diff --git a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_unsupported.go b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_unsupported.go index a77e7e07..c2e86fa1 100644 --- a/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_unsupported.go +++ b/vendor/github.com/containers/podman/v4/pkg/rootless/rootless_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux || !cgo -// +build !linux !cgo +//go:build !(linux || freebsd) || !cgo +// +build !linux,!freebsd !cgo package rootless @@ -57,7 +57,7 @@ func ConfigurationMatches() (bool, error) { } // GetConfiguredMappings returns the additional IDs configured for the current user. -func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { +func GetConfiguredMappings(quiet bool) ([]idtools.IDMap, []idtools.IDMap, error) { return nil, nil, errors.New("this function is not supported on this os") } diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/config_linux.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/config_linux.go index a4696616..965072a0 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/config_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/config_linux.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" @@ -19,6 +20,19 @@ import ( // DevicesFromPath computes a list of devices func DevicesFromPath(g *generate.Generator, devicePath string) error { + if isCDIDevice(devicePath) { + registry := cdi.GetRegistry( + cdi.WithAutoRefresh(false), + ) + if err := registry.Refresh(); err != nil { + logrus.Debugf("The following error was triggered when refreshing the CDI registry: %v", err) + } + _, err := registry.InjectDevices(g.Config, devicePath) + if err != nil { + return fmt.Errorf("setting up CDI devices: %w", err) + } + return nil + } devs := strings.Split(devicePath, ":") resolvedDevicePath := devs[0] // check if it is a symbolic link diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container.go index 736fa5ab..54d96d32 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container.go @@ -213,7 +213,6 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat sandboxID = infra.ID() } annotations[ann.SandboxID] = sandboxID - annotations[ann.ContainerType] = ann.ContainerTypeContainer // Check if this is an init-ctr and if so, check if // the pod is running. we do not want to add init-ctrs to // a running pod because it creates confusion for us. @@ -474,7 +473,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s } } specg.OverlayVolumes = overlay - _, mounts := c.SortUserVolumes(c.Spec()) + _, mounts := c.SortUserVolumes(c.ConfigNoCopy().Spec) specg.Mounts = mounts specg.HostDeviceList = conf.DeviceHostSrc specg.Networks = conf.Networks diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container_create.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container_create.go index 6ef5ca79..635a0820 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container_create.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/container_create.go @@ -88,8 +88,11 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return nil, nil, nil, err } s.UserNS = defaultNS - - mappings, err := util.ParseIDMapping(namespaces.UsernsMode(s.UserNS.NSMode), nil, nil, "", "") + value := string(s.UserNS.NSMode) + if s.UserNS.Value != "" { + value = value + ":" + s.UserNS.Value + } + mappings, err := util.ParseIDMapping(namespaces.UsernsMode(value), nil, nil, "", "") if err != nil { return nil, nil, nil, err } @@ -122,6 +125,16 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, nil, nil, err } + + if imageData != nil { + ociRuntimeVariant := rtc.Engine.ImagePlatformToRuntime(imageData.Os, imageData.Architecture) + // Don't unnecessarily set and invoke additional libpod + // option if OCI runtime is still default. + if ociRuntimeVariant != rtc.Engine.OCIRuntime { + options = append(options, libpod.WithCtrOCIRuntime(ociRuntimeVariant)) + } + } + if newImage != nil { // If the input name changed, we could properly resolve the // image. Otherwise, it must have been an ID where we're @@ -191,7 +204,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } resources := runtimeSpec.Linux.Resources - // resources get overwrritten similarly to pod inheritance, manually assign here if there is a new value + // resources get overwritten similarly to pod inheritance, manually assign here if there is a new value marshalRes, err := json.Marshal(resources) if err != nil { return nil, nil, nil, err @@ -391,6 +404,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l Dest: v.Dest, Options: v.Options, IsAnonymous: v.IsAnonymous, + SubPath: v.SubPath, }) } options = append(options, libpod.WithNamedVolumes(vols)) @@ -514,6 +528,9 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l options = append(options, libpod.WithHealthCheck(s.ContainerHealthCheckConfig.HealthConfig)) logrus.Debugf("New container has a health check") } + if s.ContainerHealthCheckConfig.StartupHealthConfig != nil { + options = append(options, libpod.WithStartupHealthcheck(s.ContainerHealthCheckConfig.StartupHealthConfig)) + } if s.ContainerHealthCheckConfig.HealthCheckOnFailureAction != define.HealthCheckOnFailureActionNone { options = append(options, libpod.WithHealthCheckOnFailureAction(s.ContainerHealthCheckConfig.HealthCheckOnFailureAction)) @@ -598,8 +615,13 @@ func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtim return nil, nil, nil, err } + // podman pod container can override pod ipc NS + if !s.IpcNS.IsDefault() { + inheritSpec.IpcNS = s.IpcNS + } + // this causes errors when shmSize is the default value, it will still get passed down unless we manually override. - if s.IpcNS.NSMode == specgen.Host && (compatibleOptions.ShmSize != nil && compatibleOptions.IsDefaultShmSize()) { + if inheritSpec.IpcNS.NSMode == specgen.Host && (compatibleOptions.ShmSize != nil && compatibleOptions.IsDefaultShmSize()) { s.ShmSize = nil } return options, infraSpec, compatibleOptions, nil diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/kube.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/kube.go index 5186a2f7..b3098aa0 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/kube.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/kube.go @@ -8,6 +8,7 @@ import ( "math" "net" "os" + "path/filepath" "regexp" "runtime" "strconv" @@ -16,10 +17,12 @@ import ( "github.com/containers/common/libimage" "github.com/containers/common/libnetwork/types" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/secrets" cutil "github.com/containers/common/pkg/util" "github.com/containers/image/v5/manifest" + itypes "github.com/containers/image/v5/types" "github.com/containers/podman/v4/libpod/define" ann "github.com/containers/podman/v4/pkg/annotations" "github.com/containers/podman/v4/pkg/domain/entities" @@ -31,6 +34,7 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/docker/docker/pkg/system" "github.com/docker/go-units" + "github.com/ghodss/yaml" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -49,6 +53,9 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { p.Share = append(p.Share, "pid") } + if podYAML.Spec.HostPID { + p.Pid = "host" + } p.Hostname = podYAML.Spec.Hostname if p.Hostname == "" { p.Hostname = podName @@ -119,12 +126,16 @@ type CtrSpecGenOptions struct { ConfigMaps []v1.ConfigMap // SeccompPaths for finding the seccomp profile path SeccompPaths *KubeSeccompPaths + // ReadOnly make all containers root file system readonly + ReadOnly itypes.OptionalBool // RestartPolicy defines the restart policy of the container RestartPolicy string // NetNSIsHost tells the container to use the host netns NetNSIsHost bool // UserNSIsHost tells the container to use the host userns UserNSIsHost bool + // PidNSIsHost tells the container to use the host pidns + PidNSIsHost bool // SecretManager to access the secrets SecretsManager *secrets.SecretsManager // LogDriver which should be used for the container @@ -145,6 +156,21 @@ type CtrSpecGenOptions struct { func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { s := specgen.NewSpecGenerator(opts.Container.Image, false) + rtc, err := config.Default() + if err != nil { + return nil, err + } + + if s.CgroupsMode == "" { + s.CgroupsMode = rtc.Cgroups() + } + if len(s.ImageVolumeMode) == 0 { + s.ImageVolumeMode = rtc.Engine.ImageVolumeMode + } + if s.ImageVolumeMode == "bind" { + s.ImageVolumeMode = "anonymous" + } + // pod name should be non-empty for Deployment objects to be able to create // multiple pods having containers with unique names if len(opts.PodName) < 1 { @@ -196,10 +222,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.InitContainerType = opts.InitContainerType setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext) - err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) + err = setupLivenessProbe(s, opts.Container, opts.RestartPolicy) if err != nil { return nil, fmt.Errorf("failed to configure livenessProbe: %w", err) } + err = setupStartupProbe(s, opts.Container, opts.RestartPolicy) + if err != nil { + return nil, fmt.Errorf("failed to configure startupProbe: %w", err) + } // Since we prefix the container name with pod name to work-around the uniqueness requirement, // the seccomp profile should reference the actual container name from the YAML @@ -299,7 +329,6 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } if opts.PodInfraID != "" { annotations[ann.SandboxID] = opts.PodInfraID - annotations[ann.ContainerType] = ann.ContainerTypeContainer } s.Annotations = annotations @@ -350,6 +379,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } volume.MountPath = dest + path := volumeSource.Source + if len(volume.SubPath) > 0 { + path = filepath.Join(path, volume.SubPath) + } switch volumeSource.Type { case KubeVolumeTypeBindMount: // If the container has bind mounts, we need to check if @@ -358,14 +391,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // Make sure the z/Z option is not already there (from editing the YAML) if k == define.BindMountPrefix { lastIndex := strings.LastIndex(v, ":") - if v[:lastIndex] == volumeSource.Source && !cutil.StringInSlice("z", options) && !cutil.StringInSlice("Z", options) { + if v[:lastIndex] == path && !cutil.StringInSlice("z", options) && !cutil.StringInSlice("Z", options) { options = append(options, v[lastIndex+1:]) } } } mount := spec.Mount{ Destination: volume.MountPath, - Source: volumeSource.Source, + Source: path, Type: "bind", Options: options, } @@ -375,6 +408,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Dest: volume.MountPath, Name: volumeSource.Source, Options: options, + SubPath: volume.SubPath, } s.Volumes = append(s.Volumes, &namedVolume) case KubeVolumeTypeConfigMap: @@ -382,13 +416,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Dest: volume.MountPath, Name: volumeSource.Source, Options: options, + SubPath: volume.SubPath, } s.Volumes = append(s.Volumes, &cmVolume) case KubeVolumeTypeCharDevice: // We are setting the path as hostPath:mountPath to comply with pkg/specgen/generate.DeviceFromPath. // The type is here just to improve readability as it is not taken into account when the actual device is created. device := spec.LinuxDevice{ - Path: fmt.Sprintf("%s:%s", volumeSource.Source, volume.MountPath), + Path: fmt.Sprintf("%s:%s", path, volume.MountPath), Type: "c", } s.Devices = append(s.Devices, device) @@ -396,7 +431,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // We are setting the path as hostPath:mountPath to comply with pkg/specgen/generate.DeviceFromPath. // The type is here just to improve readability as it is not taken into account when the actual device is created. device := spec.LinuxDevice{ - Path: fmt.Sprintf("%s:%s", volumeSource.Source, volume.MountPath), + Path: fmt.Sprintf("%s:%s", path, volume.MountPath), Type: "b", } s.Devices = append(s.Devices, device) @@ -407,6 +442,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Dest: volume.MountPath, Name: volumeSource.Source, Options: options, + SubPath: volume.SubPath, } s.Volumes = append(s.Volumes, &secretVolume) case KubeVolumeTypeEmptyDir: @@ -415,6 +451,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Name: volumeSource.Source, Options: options, IsAnonymous: true, + SubPath: volume.SubPath, } s.Volumes = append(s.Volumes, &emptyDirVolume) default: @@ -430,6 +467,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener if opts.UserNSIsHost { s.UserNS.NSMode = specgen.Host } + if opts.PidNSIsHost { + s.PidNS.NSMode = specgen.Host + } // Add labels that come from kube if len(s.Labels) == 0 { @@ -444,6 +484,12 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } } + if ro := opts.ReadOnly; ro != itypes.OptionalBoolUndefined { + s.ReadOnlyFilesystem = (ro == itypes.OptionalBoolTrue) + } + // This should default to true for kubernetes yaml + s.ReadWriteTmpfs = true + // Make sure the container runs in a systemd unit which is // stored as a label at container creation. if unit := os.Getenv(systemdDefine.EnvVariable); unit != "" { @@ -488,6 +534,41 @@ func parseMountPath(mountPath string, readOnly bool, propagationMode *v1.MountPr return dest, opts, nil } +func probeToHealthConfig(probe *v1.Probe) (*manifest.Schema2HealthConfig, error) { + var commandString string + failureCmd := "exit 1" + probeHandler := probe.Handler + + // configure healthcheck on the basis of Handler Actions. + switch { + case probeHandler.Exec != nil: + // `makeHealthCheck` function can accept a json array as the command. + cmd, err := json.Marshal(probeHandler.Exec.Command) + if err != nil { + return nil, err + } + commandString = string(cmd) + case probeHandler.HTTPGet != nil: + // set defaults as in https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#http-probes + uriScheme := v1.URISchemeHTTP + if probeHandler.HTTPGet.Scheme != "" { + uriScheme = probeHandler.HTTPGet.Scheme + } + host := "localhost" // Kubernetes default is host IP, but with Podman there is only one node + if probeHandler.HTTPGet.Host != "" { + host = probeHandler.HTTPGet.Host + } + path := "/" + if probeHandler.HTTPGet.Path != "" { + path = probeHandler.HTTPGet.Path + } + commandString = fmt.Sprintf("curl -f %s://%s:%d%s || %s", uriScheme, host, probeHandler.HTTPGet.Port.IntValue(), path, failureCmd) + case probeHandler.TCPSocket != nil: + commandString = fmt.Sprintf("nc -z -v %s %d || %s", probeHandler.TCPSocket.Host, probeHandler.TCPSocket.Port.IntValue(), failureCmd) + } + return makeHealthCheck(commandString, probe.PeriodSeconds, probe.FailureThreshold, probe.TimeoutSeconds, probe.InitialDelaySeconds) +} + func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, restartPolicy string) error { var err error if containerYAML.LivenessProbe == nil { @@ -495,38 +576,42 @@ func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, re } emptyHandler := v1.Handler{} if containerYAML.LivenessProbe.Handler != emptyHandler { - var commandString string - failureCmd := "exit 1" - probe := containerYAML.LivenessProbe - probeHandler := probe.Handler - - // configure healthcheck on the basis of Handler Actions. - switch { - case probeHandler.Exec != nil: - execString := strings.Join(probeHandler.Exec.Command, " ") - commandString = fmt.Sprintf("%s || %s", execString, failureCmd) - case probeHandler.HTTPGet != nil: - // set defaults as in https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#http-probes - uriScheme := v1.URISchemeHTTP - if probeHandler.HTTPGet.Scheme != "" { - uriScheme = probeHandler.HTTPGet.Scheme - } - host := "localhost" // Kubernetes default is host IP, but with Podman there is only one node - if probeHandler.HTTPGet.Host != "" { - host = probeHandler.HTTPGet.Host - } - path := "/" - if probeHandler.HTTPGet.Path != "" { - path = probeHandler.HTTPGet.Path - } - commandString = fmt.Sprintf("curl -f %s://%s:%d%s || %s", uriScheme, host, probeHandler.HTTPGet.Port.IntValue(), path, failureCmd) - case probeHandler.TCPSocket != nil: - commandString = fmt.Sprintf("nc -z -v %s %d || %s", probeHandler.TCPSocket.Host, probeHandler.TCPSocket.Port.IntValue(), failureCmd) + s.HealthConfig, err = probeToHealthConfig(containerYAML.LivenessProbe) + if err != nil { + return err + } + // if restart policy is in place, ensure the health check enforces it + if restartPolicy == "always" || restartPolicy == "onfailure" { + s.HealthCheckOnFailureAction = define.HealthCheckOnFailureActionRestart } - s.HealthConfig, err = makeHealthCheck(commandString, probe.PeriodSeconds, probe.FailureThreshold, probe.TimeoutSeconds, probe.InitialDelaySeconds) + return nil + } + return nil +} + +func setupStartupProbe(s *specgen.SpecGenerator, containerYAML v1.Container, restartPolicy string) error { + if containerYAML.StartupProbe == nil { + return nil + } + emptyHandler := v1.Handler{} + if containerYAML.StartupProbe.Handler != emptyHandler { + healthConfig, err := probeToHealthConfig(containerYAML.StartupProbe) if err != nil { return err } + + // currently, StartupProbe still an optional feature, and it requires HealthConfig. + if s.HealthConfig == nil { + probe := containerYAML.StartupProbe + s.HealthConfig, err = makeHealthCheck("exit 0", probe.PeriodSeconds, probe.FailureThreshold, probe.TimeoutSeconds, probe.InitialDelaySeconds) + if err != nil { + return err + } + } + s.StartupHealthConfig = &define.StartupHealthCheck{ + Schema2HealthConfig: *healthConfig, + Successes: int(containerYAML.StartupProbe.SuccessThreshold), + } // if restart policy is in place, ensure the health check enforces it if restartPolicy == "always" || restartPolicy == "onfailure" { s.HealthCheckOnFailureAction = define.HealthCheckOnFailureActionRestart @@ -553,6 +638,8 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, // ...otherwise pass it to "/bin/sh -c" inside the container cmd = []string{define.HealthConfigTestCmdShell} cmd = append(cmd, strings.Split(inCmd, " ")...) + } else { + cmd = append([]string{define.HealthConfigTestCmd}, cmd...) } } hc := manifest.Schema2HealthConfig{ @@ -668,17 +755,31 @@ func quantityToInt64(quantity *resource.Quantity) (int64, error) { return 0, fmt.Errorf("quantity cannot be represented as int64: %v", quantity) } -// read a k8s secret in JSON format from the secret manager +// read a k8s secret in JSON/YAML format from the secret manager +// k8s secret is stored as YAML, we have to read data as JSON for backward compatibility func k8sSecretFromSecretManager(name string, secretsManager *secrets.SecretsManager) (map[string][]byte, error) { - _, jsonSecret, err := secretsManager.LookupSecretData(name) + _, inputSecret, err := secretsManager.LookupSecretData(name) if err != nil { return nil, err } var secrets map[string][]byte - if err := json.Unmarshal(jsonSecret, &secrets); err != nil { - return nil, fmt.Errorf("secret %v is not valid JSON: %v", name, err) + if err := json.Unmarshal(inputSecret, &secrets); err != nil { + secrets = make(map[string][]byte) + var secret v1.Secret + if err := yaml.Unmarshal(inputSecret, &secret); err != nil { + return nil, fmt.Errorf("secret %v is not valid JSON/YAML: %v", name, err) + } + + for key, val := range secret.Data { + secrets[key] = val + } + + for key, val := range secret.StringData { + secrets[key] = []byte(val) + } } + return secrets, nil } diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/volume.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/volume.go index 4e98305c..c792372b 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/volume.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/kube/volume.go @@ -9,10 +9,9 @@ import ( "github.com/containers/common/pkg/secrets" "github.com/containers/podman/v4/libpod" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" - metav1 "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/ghodss/yaml" "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" ) const ( @@ -131,44 +130,38 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) // VolumeFromSecret creates a new kube volume from a kube secret. func VolumeFromSecret(secretSource *v1.SecretVolumeSource, secretsManager *secrets.SecretsManager) (*KubeVolume, error) { + kv := &KubeVolume{ + Type: KubeVolumeTypeSecret, + Source: secretSource.SecretName, + Items: map[string][]byte{}, + } + // returns a byte array of a kube secret data, meaning this needs to go into a string map _, secretByte, err := secretsManager.LookupSecretData(secretSource.SecretName) if err != nil { + if errors.Is(err, secrets.ErrNoSuchSecret) && secretSource.Optional != nil && *secretSource.Optional { + kv.Optional = true + return kv, nil + } return nil, err } - // unmarshaling directly into a v1.secret creates type mismatch errors - // use a more friendly, string only secret struct. - type KubeSecret struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - // +optional - Immutable *bool `json:"immutable,omitempty"` - Data map[string]string `json:"data,omitempty"` - // +optional - StringData map[string]string `json:"stringData,omitempty"` - // +optional - Type string `json:"type,omitempty"` - } - - data := &KubeSecret{} + data := &v1.Secret{} err = yaml.Unmarshal(secretByte, data) if err != nil { return nil, err } - kv := &KubeVolume{} - kv.Type = KubeVolumeTypeSecret - kv.Source = secretSource.SecretName - kv.Optional = *secretSource.Optional - kv.Items = make(map[string][]byte) - // add key: value pairs to the items array for key, entry := range data.Data { + kv.Items[key] = entry + } + + for key, entry := range data.StringData { kv.Items[key] = []byte(entry) } + return kv, nil } @@ -252,7 +245,7 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, s case volumeSource.EmptyDir != nil: return VolumeFromEmptyDir(volumeSource.EmptyDir, volName) default: - return nil, errors.New("HostPath, ConfigMap, EmptyDir, and PersistentVolumeClaim are currently the only supported VolumeSource") + return nil, errors.New("HostPath, ConfigMap, EmptyDir, Secret, and PersistentVolumeClaim are currently the only supported VolumeSource") } } diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces.go index 9497894f..fa4b6a2f 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces.go @@ -89,7 +89,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) case "cgroup": return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) case "net": - ns, _, _, err := specgen.ParseNetworkFlag(nil) + ns, _, _, err := specgen.ParseNetworkFlag(nil, false) return ns, err } @@ -233,7 +233,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. } // This wipes the UserNS settings that get set from the infra container - // when we are inheritting from the pod. So only apply this if the container + // when we are inheriting from the pod. So only apply this if the container // is not being created in a pod. if s.IDMappings != nil { if pod == nil { @@ -300,6 +300,13 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) + case specgen.Pasta: + portMappings, expose, err := createPortMappings(s, imageData) + if err != nil { + return nil, err + } + val := "pasta" + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) case specgen.Bridge, specgen.Private, specgen.Default: portMappings, expose, err := createPortMappings(s, imageData) if err != nil { diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces_linux.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces_linux.go index 9fda000f..4feaa28c 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/namespaces_linux.go @@ -152,8 +152,6 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt } if s.PublishExposedPorts { g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue - } else { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse } return nil diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_freebsd.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_freebsd.go index 71c926fd..56245b57 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_freebsd.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_freebsd.go @@ -63,8 +63,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if s.Remove { configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse } if len(s.VolumesFrom) > 0 { @@ -73,14 +71,10 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if s.Privileged { configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse } if s.Init { configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse } if s.OOMScoreAdj != nil { diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_linux.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_linux.go index 341853de..5be10f5a 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/oci_linux.go @@ -38,7 +38,7 @@ func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { } if isNewUserns { switch s.NetNS.NSMode { - case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + case specgen.Slirp, specgen.Pasta, specgen.Private, specgen.NoNetwork, specgen.Bridge: return true default: return false @@ -83,7 +83,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt addCgroup := true isRootless := rootless.IsRootless() - isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() || s.UserNS.IsPod() || s.UserNS.IsAuto() canMountSys := canMountSys(isRootless, isNewUserns, s) @@ -107,11 +107,19 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } sysMnt := spec.Mount{ Destination: "/sys", - Type: "bind", // should we use a constant for this, like createconfig? + Type: "bind", Source: "/sys", Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, } g.AddMount(sysMnt) + g.RemoveMount("/sys/fs/cgroup") + sysFsCgroupMnt := spec.Mount{ + Destination: "/sys/fs/cgroup", + Type: "bind", + Source: "/sys/fs/cgroup", + Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, + } + g.AddMount(sysFsCgroupMnt) if !s.Privileged && isRootless { g.AddLinuxMaskedPaths("/sys/kernel") } @@ -286,8 +294,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if s.Remove { configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse } if len(s.VolumesFrom) > 0 { @@ -296,14 +302,10 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if s.Privileged { configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse } if s.Init { configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse } if s.OOMScoreAdj != nil { diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/pod_create.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/pod_create.go index b99a5457..1988eeb7 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/pod_create.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/pod_create.go @@ -18,7 +18,15 @@ import ( "github.com/sirupsen/logrus" ) -func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { +func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (_ *libpod.Pod, finalErr error) { + var createdPod *libpod.Pod + defer func() { + if finalErr != nil && createdPod != nil { + if err := rt.RemovePod(context.Background(), createdPod, true, true, nil); err != nil { + logrus.Errorf("Removing pod: %v", err) + } + } + }() if err := p.PodSpecGen.Validate(); err != nil { return nil, err } @@ -69,6 +77,8 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { if err != nil { return nil, err } + createdPod = pod + if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil { if p.PodSpecGen.InfraContainerSpec.Name == "" { p.PodSpecGen.InfraContainerSpec.Name = pod.ID()[:12] + "-infra" @@ -195,6 +205,16 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { p.InfraContainerSpec.NetworkOptions = p.NetworkOptions p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp } + case specgen.Pasta: + logrus.Debugf("Pod will use pasta") + if p.InfraContainerSpec.NetNS.NSMode != specgen.Host { + p.InfraContainerSpec.NetworkOptions = p.NetworkOptions + p.InfraContainerSpec.NetNS.NSMode = specgen.Pasta + } + case specgen.Path: + logrus.Debugf("Pod will use namespace path networking") + p.InfraContainerSpec.NetNS.NSMode = specgen.Path + p.InfraContainerSpec.NetNS.Value = p.PodNetworkConfig.NetNS.Value case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") if len(p.InfraContainerSpec.PortMappings) > 0 || diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/ports.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/ports.go index dbfd2a4a..782a7f65 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/ports.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/ports.go @@ -95,7 +95,7 @@ func addPortToUsedPorts(ports *[]types.PortMapping, allHostPorts, allContainerPo } // getRandomHostPort get a random host port mapping for the given port -// the caller has to supply a array with he already used ports +// the caller has to supply an array with the already used ports func getRandomHostPort(hostPorts *[65536]bool, port types.PortMapping) (types.PortMapping, error) { outer: for i := 0; i < 15; i++ { @@ -311,6 +311,8 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ return nil, err } portMappings = append(portMappings, p) + // Mark this port as used so it doesn't get re-generated + allPorts[p.HostPort] = true } else { newProtocols = append(newProtocols, protocol) } diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/storage.go b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/storage.go index b6246d82..ffaaa0e1 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/generate/storage.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/generate/storage.go @@ -159,14 +159,19 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Check for conflicts between named volumes and mounts for dest := range baseMounts { if _, ok := baseVolumes[dest]; ok { - return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest) + return nil, nil, nil, fmt.Errorf("baseMounts conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest) } } for dest := range baseVolumes { if _, ok := baseMounts[dest]; ok { - return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest) + return nil, nil, nil, fmt.Errorf("baseVolumes conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest) } } + + if s.ReadWriteTmpfs { + baseMounts = addReadWriteTmpfsMounts(baseMounts, s.Volumes) + } + // Final step: maps to arrays finalMounts := make([]spec.Mount, 0, len(baseMounts)) for _, mount := range baseMounts { @@ -286,7 +291,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s // Now we get the container's spec and loop through its volumes // and append them in if we can find them. - spec := ctr.Spec() + spec := ctr.ConfigNoCopy().Spec if spec == nil { return nil, nil, fmt.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) } @@ -427,3 +432,29 @@ func InitFSMounts(mounts []spec.Mount) error { } return nil } + +func addReadWriteTmpfsMounts(mounts map[string]spec.Mount, volumes []*specgen.NamedVolume) map[string]spec.Mount { + readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} + options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} + for _, dest := range readonlyTmpfs { + if _, ok := mounts[dest]; ok { + continue + } + for _, m := range volumes { + if m.Dest == dest { + continue + } + } + mnt := spec.Mount{ + Destination: dest, + Type: define.TypeTmpfs, + Source: define.TypeTmpfs, + Options: options, + } + if dest != "/run" { + mnt.Options = append(mnt.Options, "noexec") + } + mounts[dest] = mnt + } + return mounts +} diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/namespaces.go b/vendor/github.com/containers/podman/v4/pkg/specgen/namespaces.go index b6bbee86..94c96794 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/namespaces.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/namespaces.go @@ -12,6 +12,7 @@ import ( cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/namespaces" + "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -24,8 +25,7 @@ const ( // Default indicates the spec generator should determine // a sane default Default NamespaceMode = "default" - // Host means the the namespace is derived from - // the host + // Host means the namespace is derived from the host Host NamespaceMode = "host" // Path is the path to a namespace Path NamespaceMode = "path" @@ -41,10 +41,10 @@ const ( // None indicates the IPC namespace is created without mounting /dev/shm None NamespaceMode = "none" // NoNetwork indicates no network namespace should - // be joined. loopback should still exists. + // be joined. loopback should still exist. // Only used with the network namespace, invalid otherwise. NoNetwork NamespaceMode = "none" - // Bridge indicates that a CNI network stack + // Bridge indicates that the network backend (CNI/netavark) // should be used. // Only used with the network namespace, invalid otherwise. Bridge NamespaceMode = "bridge" @@ -52,6 +52,9 @@ const ( // be used. // Only used with the network namespace, invalid otherwise. Slirp NamespaceMode = "slirp4netns" + // Pasta indicates that a pasta network stack should be used. + // Only used with the network namespace, invalid otherwise. + Pasta NamespaceMode = "pasta" // KeepId indicates a user namespace to keep the owner uid inside // of the namespace itself. // Only used with the user namespace, invalid otherwise. @@ -156,6 +159,11 @@ func validateNetNS(n *Namespace) error { switch n.NSMode { case Slirp: break + case Pasta: + if rootless.IsRootless() { + break + } + return fmt.Errorf("pasta networking is only supported for rootless mode") case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge: break default: @@ -197,7 +205,7 @@ func (n *Namespace) validate() error { switch n.NSMode { case "", Default, Host, Path, FromContainer, FromPod, Private: // Valid, do nothing - case NoNetwork, Bridge, Slirp: + case NoNetwork, Bridge, Slirp, Pasta: return errors.New("cannot use network modes with non-network namespace") default: return fmt.Errorf("invalid namespace type %s specified", n.NSMode) @@ -275,7 +283,7 @@ func ParseCgroupNamespace(ns string) (Namespace, error) { return toReturn, nil } -// ParseIPCNamespace parses a ipc namespace specification in string +// ParseIPCNamespace parses an ipc namespace specification in string // form. func ParseIPCNamespace(ns string) (Namespace, error) { toReturn := Namespace{} @@ -329,7 +337,8 @@ func ParseUserNamespace(ns string) (Namespace, error) { // ParseNetworkFlag parses a network string slice into the network options // If the input is nil or empty it will use the default setting from containers.conf -func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) { +// TODO (5.0): Drop pastaNetworkNameExists +func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) { var networkOptions map[string][]string // by default we try to use the containers.conf setting // if we get at least one value use this instead @@ -385,6 +394,22 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork } toReturn.NSMode = FromContainer toReturn.Value = split[1] + case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta)+":"): + var parts []string + + if pastaNetworkNameExists { + goto nextCase + } + + parts = strings.SplitN(ns, ":", 2) + if len(parts) > 1 { + networkOptions = make(map[string][]string) + networkOptions[parts[0]] = strings.Split(parts[1], ",") + } + toReturn.NSMode = Pasta + break + nextCase: + fallthrough default: // we should have a normal network parts := strings.SplitN(ns, ":", 2) @@ -419,6 +444,7 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork if parts[0] == "" { return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg) } + // TODO (5.0): Don't accept string(Pasta) here once we drop pastaNetworkNameExists if cutil.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork), string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) { return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", parts[0], define.ErrInvalidArg) diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/pod_validate.go b/vendor/github.com/containers/podman/v4/pkg/specgen/pod_validate.go index 724be213..1c0413cd 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/pod_validate.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/pod_validate.go @@ -65,9 +65,9 @@ func (p *PodSpecGenerator) Validate() error { return exclusivePodOptions("NoInfra", "NoManageResolvConf") } } - if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Slirp && p.NetNS.NSMode != Default { + if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Slirp && p.NetNS.NSMode != Pasta && p.NetNS.NSMode != Default { if len(p.PortMappings) > 0 { - return errors.New("PortMappings can only be used with Bridge or slirp4netns networking") + return errors.New("PortMappings can only be used with Bridge, slirp4netns, or pasta networking") } } diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/podspecgen.go b/vendor/github.com/containers/podman/v4/pkg/specgen/podspecgen.go index 64a79f4e..faeeb2ed 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/podspecgen.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/podspecgen.go @@ -98,7 +98,7 @@ type PodNetworkConfig struct { // PortMappings is a set of ports to map into the infra container. // As, by default, containers share their network with the infra // container, this will forward the ports to the entire pod. - // Only available if NetNS is set to Bridge or Slirp. + // Only available if NetNS is set to Bridge, Slirp, or Pasta. // Optional. PortMappings []types.PortMapping `json:"portmappings,omitempty"` // Map of networks names to ids the container should join to. diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/specgen.go b/vendor/github.com/containers/podman/v4/pkg/specgen/specgen.go index 8c77df20..2e707811 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/specgen.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/specgen.go @@ -384,6 +384,10 @@ type ContainerSecurityConfig struct { // ReadOnlyFilesystem indicates that everything will be mounted // as read-only ReadOnlyFilesystem bool `json:"read_only_filesystem,omitempty"` + // ReadWriteTmpfs indicates that when running with a ReadOnlyFilesystem + // mount temporary file systems + ReadWriteTmpfs bool `json:"read_write_tmpfs,omitempty"` + // Umask is the umask the init process of the container will be run with. Umask string `json:"umask,omitempty"` // ProcOpts are the options used for the proc mount. @@ -421,7 +425,7 @@ type ContainerNetworkConfig struct { // Mandatory. NetNS Namespace `json:"netns,omitempty"` // PortBindings is a set of ports to map into the container. - // Only available if NetNS is set to bridge or slirp. + // Only available if NetNS is set to bridge, slirp, or pasta. // Optional. PortMappings []nettypes.PortMapping `json:"portmappings,omitempty"` // PublishExposedPorts will publish ports specified in the image to @@ -536,6 +540,10 @@ type ContainerResourceConfig struct { type ContainerHealthCheckConfig struct { HealthConfig *manifest.Schema2HealthConfig `json:"healthconfig,omitempty"` HealthCheckOnFailureAction define.HealthCheckOnFailureAction `json:"health_check_on_failure_action,omitempty"` + // Startup healthcheck for a container. + // Requires that HealthConfig be set. + // Optional. + StartupHealthConfig *define.StartupHealthCheck `json:"startupHealthConfig,omitempty"` } // SpecGenerator creates an OCI spec and Libpod configuration options to create diff --git a/vendor/github.com/containers/podman/v4/pkg/specgen/volumes.go b/vendor/github.com/containers/podman/v4/pkg/specgen/volumes.go index 0d886df5..b8b2ece8 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgen/volumes.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgen/volumes.go @@ -27,9 +27,11 @@ type NamedVolume struct { // IsAnonymous sets the named volume as anonymous even if it has a name // This is used for emptyDir volumes from a kube yaml IsAnonymous bool + // SubPath stores the sub directory of the named volume to be mounted in the container + SubPath string } -// OverlayVolume holds information about a overlay volume that will be mounted into +// OverlayVolume holds information about an overlay volume that will be mounted into // the container. type OverlayVolume struct { // Destination is the absolute path where the mount will be placed in the container. @@ -139,7 +141,7 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na } } if overlayFlag { - // This is a overlay volume + // This is an overlay volume newOverlayVol := new(OverlayVolume) newOverlayVol.Destination = dest // convert src to absolute path so we don't end up passing diff --git a/vendor/github.com/containers/podman/v4/pkg/specgenutil/specgen.go b/vendor/github.com/containers/podman/v4/pkg/specgenutil/specgen.go index 2695d887..8e2a490b 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgenutil/specgen.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgenutil/specgen.go @@ -13,7 +13,6 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/libpod/define" - ann "github.com/containers/podman/v4/pkg/annotations" "github.com/containers/podman/v4/pkg/domain/entities" envLib "github.com/containers/podman/v4/pkg/env" "github.com/containers/podman/v4/pkg/namespaces" @@ -256,7 +255,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions if c.NoHealthCheck { return errors.New("cannot specify both --no-healthcheck and --health-cmd") } - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) + s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod, false) if err != nil { return err } @@ -272,6 +271,25 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions } s.HealthCheckOnFailureAction = onFailureAction + if c.StartupHCCmd != "" { + if c.NoHealthCheck { + return errors.New("cannot specify both --no-healthcheck and --health-startup-cmd") + } + // The hardcoded "1s" will be discarded, as the startup + // healthcheck does not have a period. So just hardcode + // something that parses correctly. + tmpHcConfig, err := makeHealthCheckFromCli(c.StartupHCCmd, c.StartupHCInterval, c.StartupHCRetries, c.StartupHCTimeout, "1s", true) + if err != nil { + return err + } + s.StartupHealthConfig = new(define.StartupHealthCheck) + s.StartupHealthConfig.Test = tmpHcConfig.Test + s.StartupHealthConfig.Interval = tmpHcConfig.Interval + s.StartupHealthConfig.Timeout = tmpHcConfig.Timeout + s.StartupHealthConfig.Retries = tmpHcConfig.Retries + s.StartupHealthConfig.Successes = int(c.StartupHCSuccesses) + } + if err := setNamespaces(s, c); err != nil { return err } @@ -414,12 +432,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // ANNOTATIONS annotations := make(map[string]string) - // First, add our default annotations - annotations[ann.TTY] = "false" - if c.TTY { - annotations[ann.TTY] = "true" - } - // Last, add user annotations for _, annotation := range c.Annotation { splitAnnotation := strings.SplitN(annotation, "=", 2) @@ -574,10 +586,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.DependencyContainers = c.Requires } - // TODO - // outside of specgen and oci though - // defaults to true, check spec/storage - // s.readonly = c.ReadOnlyTmpFS + // Only add ReadWrite tmpfs mounts iff the container is + // being run ReadOnly and ReadWriteTmpFS is not disabled, + // (user specifying --read-only-tmpfs=false.) + s.ReadWriteTmpfs = c.ReadOnly && c.ReadWriteTmpFS + // TODO convert to map? // check if key=value and convert sysmap := make(map[string]string) @@ -657,7 +670,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions // Only add read-only tmpfs mounts in case that we are read-only and the // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) + mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS) if err != nil { return err } @@ -838,7 +851,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions return nil } -func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { +func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string, isStartup bool) (*manifest.Schema2HealthConfig, error) { cmdArr := []string{} isArr := true err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling @@ -886,7 +899,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start hc.Interval = intervalDuration - if retries < 1 { + if retries < 1 && !isStartup { return nil, errors.New("healthcheck-retries must be greater than 0") } hc.Retries = int(retries) diff --git a/vendor/github.com/containers/podman/v4/pkg/specgenutil/util.go b/vendor/github.com/containers/podman/v4/pkg/specgenutil/util.go index d61e57ce..4e065347 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgenutil/util.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgenutil/util.go @@ -282,6 +282,7 @@ func CreateExitCommandArgs(storageConfig storageTypes.StoreOptions, config *conf "--network-config-dir", config.Network.NetworkConfigDir, "--network-backend", config.Network.NetworkBackend, "--volumepath", config.Engine.VolumePath, + fmt.Sprintf("--transient-store=%t", storageConfig.TransientStore), } if config.Engine.OCIRuntime != "" { command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...) diff --git a/vendor/github.com/containers/podman/v4/pkg/specgenutil/volumes.go b/vendor/github.com/containers/podman/v4/pkg/specgenutil/volumes.go index fe2216d8..c70206ad 100644 --- a/vendor/github.com/containers/podman/v4/pkg/specgenutil/volumes.go +++ b/vendor/github.com/containers/podman/v4/pkg/specgenutil/volumes.go @@ -26,7 +26,7 @@ var ( // Does not handle image volumes, init, and --volumes-from flags. // Can also add tmpfs mounts from read-only tmpfs. // TODO: handle options parsing/processing via containers/storage/pkg/mount -func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { +func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { // Get mounts from the --mounts flag. unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := Mounts(mountFlag) if err != nil { @@ -78,26 +78,6 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo unifiedMounts[dest] = tmpfs } - // If requested, add tmpfs filesystems for read-only containers. - if addReadOnlyTmpfs { - readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} - for _, dest := range readonlyTmpfs { - if _, ok := unifiedMounts[dest]; ok { - continue - } - if _, ok := unifiedVolumes[dest]; ok { - continue - } - unifiedMounts[dest] = spec.Mount{ - Destination: dest, - Type: define.TypeTmpfs, - Source: "tmpfs", - Options: options, - } - } - } - // Check for conflicts between named volumes, overlay & image volumes, // and mounts allMounts := make(map[string]bool) @@ -584,6 +564,12 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { } newVolume.Dest = unixPathClean(kv[1]) setDest = true + case "idmap": + if len(kv) > 1 { + newVolume.Options = append(newVolume.Options, fmt.Sprintf("idmap=%s", kv[1])) + } else { + newVolume.Options = append(newVolume.Options, "idmap") + } case "U", "chown": if setOwnership { return newVolume, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg) diff --git a/vendor/github.com/containers/podman/v4/pkg/systemd/dbus.go b/vendor/github.com/containers/podman/v4/pkg/systemd/dbus.go index 6887a466..059d3995 100644 --- a/vendor/github.com/containers/podman/v4/pkg/systemd/dbus.go +++ b/vendor/github.com/containers/podman/v4/pkg/systemd/dbus.go @@ -129,7 +129,11 @@ func dbusAuthRootlessConnection(createBus func(opts ...godbus.ConnOption) (*godb func newRootlessConnection() (*dbus.Conn, error) { return dbus.NewConnection(func() (*godbus.Conn, error) { return dbusAuthRootlessConnection(func(opts ...godbus.ConnOption) (*godbus.Conn, error) { - path := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "systemd/private") + path := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "systemd", "private") + path, err := filepath.EvalSymlinks(path) + if err != nil { + return nil, err + } return godbus.Dial(fmt.Sprintf("unix:path=%s", path)) }) }) diff --git a/vendor/github.com/containers/podman/v4/pkg/systemd/generate/containers.go b/vendor/github.com/containers/podman/v4/pkg/systemd/generate/containers.go index ae2dac80..7b1e221c 100644 --- a/vendor/github.com/containers/podman/v4/pkg/systemd/generate/containers.go +++ b/vendor/github.com/containers/podman/v4/pkg/systemd/generate/containers.go @@ -28,6 +28,7 @@ type containerInfo struct { NotifyAccess string StopTimeout uint RestartPolicy string + RestartSec uint StartLimitBurst string PIDFile string ContainerIDFile string @@ -42,10 +43,10 @@ type containerInfo struct { ExtraEnvs []string EnvVariable string AdditionalEnvVariables []string - ExecStartPre string ExecStart string TimeoutStartSec uint TimeoutStopSec uint + ExecStartPre string ExecStop string ExecStopPost string GenerateNoHeader bool @@ -87,6 +88,9 @@ Environment={{{{- range $index, $value := .ExtraEnvs -}}}}{{{{if $index}}}} {{{{ Environment={{{{ $value }}}}{{{{end}}}} {{{{- end}}}} Restart={{{{.RestartPolicy}}}} +{{{{- if .RestartSec}}}} +RestartSec={{{{.RestartSec}}}} +{{{{- end}}}} {{{{- if .StartLimitBurst}}}} StartLimitBurst={{{{.StartLimitBurst}}}} {{{{- end}}}} @@ -94,9 +98,6 @@ StartLimitBurst={{{{.StartLimitBurst}}}} TimeoutStartSec={{{{.TimeoutStartSec}}}} {{{{- end}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}} -{{{{- if .ExecStartPre}}}} -ExecStartPre={{{{.ExecStartPre}}}} -{{{{- end}}}} ExecStart={{{{.ExecStart}}}} {{{{- if .ExecStop}}}} ExecStop={{{{.ExecStop}}}} @@ -285,6 +286,10 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.RestartPolicy = *options.RestartPolicy } + if options.RestartSec != nil { + info.RestartSec = *options.RestartSec + } + // Make sure the executable is set. if info.Executable == "" { executable, err := os.Executable() @@ -316,7 +321,6 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.NotifyAccess = "all" info.PIDFile = "" info.ContainerIDFile = "%t/%n.ctr-id" - info.ExecStartPre = formatOptionsString("/bin/rm -f {{{{.ContainerIDFile}}}}") info.ExecStop = formatOptionsString("{{{{.Executable}}}} stop --ignore {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} --cidfile={{{{.ContainerIDFile}}}}") info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} rm -f --ignore {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} --cidfile={{{{.ContainerIDFile}}}}") // The create command must at least have three arguments: @@ -457,7 +461,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst return "", err } for _, env := range envs { - // if env arg does not contain a equal sign we have to add the envar to the unit + // if env arg does not contain an equal sign we have to add the envar to the unit // because it does try to red the value from the environment if !strings.Contains(env, "=") { for _, containerEnv := range info.containerEnv { diff --git a/vendor/github.com/containers/podman/v4/pkg/systemd/generate/pods.go b/vendor/github.com/containers/podman/v4/pkg/systemd/generate/pods.go index 588bfb43..bdc7c34d 100644 --- a/vendor/github.com/containers/podman/v4/pkg/systemd/generate/pods.go +++ b/vendor/github.com/containers/podman/v4/pkg/systemd/generate/pods.go @@ -61,10 +61,8 @@ type podInfo struct { PodCreateCommand string // EnvVariable is generate.EnvVariable and must not be set. EnvVariable string - // ExecStartPre1 of the unit. - ExecStartPre1 string - // ExecStartPre2 of the unit. - ExecStartPre2 string + // ExecStartPre of the unit. + ExecStartPre string // ExecStart of the unit. ExecStart string // TimeoutStopSec of the unit. @@ -115,11 +113,8 @@ Restart={{{{.RestartPolicy}}}} RestartSec={{{{.RestartSec}}}} {{{{- end}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}} -{{{{- if .ExecStartPre1}}}} -ExecStartPre={{{{.ExecStartPre1}}}} -{{{{- end}}}} -{{{{- if .ExecStartPre2}}}} -ExecStartPre={{{{.ExecStartPre2}}}} +{{{{- if .ExecStartPre}}}} +ExecStartPre={{{{.ExecStartPre}}}} {{{{- end}}}} ExecStart={{{{.ExecStart}}}} ExecStop={{{{.ExecStop}}}} @@ -371,8 +366,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) startCommand = append(startCommand, podCreateArgs...) startCommand = escapeSystemdArguments(startCommand) - info.ExecStartPre1 = formatOptionsString("/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}") - info.ExecStartPre2 = formatOptions(startCommand) + info.ExecStartPre = formatOptions(startCommand) info.ExecStart = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod start --pod-id-file {{{{.PodIDFile}}}}") info.ExecStop = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod stop --ignore --pod-id-file {{{{.PodIDFile}}}} {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}}") info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod rm --ignore -f --pod-id-file {{{{.PodIDFile}}}}") diff --git a/vendor/github.com/containers/podman/v4/pkg/systemd/notifyproxy/notifyproxy.go b/vendor/github.com/containers/podman/v4/pkg/systemd/notifyproxy/notifyproxy.go index e9dd48c5..508bc966 100644 --- a/vendor/github.com/containers/podman/v4/pkg/systemd/notifyproxy/notifyproxy.go +++ b/vendor/github.com/containers/podman/v4/pkg/systemd/notifyproxy/notifyproxy.go @@ -14,6 +14,16 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/coreos/go-systemd/v22/daemon" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +const ( + // All constants below are defined by systemd. + _notifyRcvbufSize = 8 * 1024 * 1024 + _notifyBufferMax = 4096 + _notifyFdMax = 768 + _notifyBarrierMsg = "BARRIER=1" + _notifyRdyMsg = daemon.SdNotifyReady ) // SendMessage sends the specified message to the specified socket. @@ -76,6 +86,10 @@ func New(tmpDir string) (*NotifyProxy, error) { return nil, err } + if err := conn.SetReadBuffer(_notifyRcvbufSize); err != nil { + return nil, fmt.Errorf("setting read buffer: %w", err) + } + errorChan := make(chan error, 1) readyChan := make(chan bool, 1) @@ -100,34 +114,69 @@ func (p *NotifyProxy) waitForReady() { go func() { // Read until the `READY` message is received or the connection // is closed. - const bufferSize = 1024 + + // See https://github.com/containers/podman/issues/16515 for a description of the protocol. + fdSize := unix.CmsgSpace(4) + buffer := make([]byte, _notifyBufferMax) + oob := make([]byte, _notifyFdMax*fdSize) sBuilder := strings.Builder{} for { - for { - buffer := make([]byte, bufferSize) - num, err := p.connection.Read(buffer) - if err != nil { - if !errors.Is(err, io.EOF) { - p.errorChan <- err - return - } - } - sBuilder.Write(buffer[:num]) - if num != bufferSize || buffer[num-1] == '\n' { - // Break as we read an entire line that - // we can inspect for the `READY` - // message. - break + n, oobn, flags, _, err := p.connection.ReadMsgUnix(buffer, oob) + if err != nil { + if !errors.Is(err, io.EOF) { + p.errorChan <- err + return } + logrus.Errorf("Error reading unix message on socket %q: %v", p.socketPath, err) + } + + if n > _notifyBufferMax || oobn > _notifyFdMax*fdSize { + logrus.Errorf("Ignoring unix message on socket %q: incorrect number of bytes read (n=%d, oobn=%d)", p.socketPath, n, oobn) + continue + } + + if flags&unix.MSG_CTRUNC != 0 { + logrus.Errorf("Ignoring unix message on socket %q: message truncated", p.socketPath) + continue } + sBuilder.Reset() + sBuilder.Write(buffer[:n]) + var isBarrier, isReady bool + for _, line := range strings.Split(sBuilder.String(), "\n") { - if line == daemon.SdNotifyReady { - p.readyChan <- true - return + switch line { + case _notifyRdyMsg: + isReady = true + case _notifyBarrierMsg: + isBarrier = true } } - sBuilder.Reset() + + if isBarrier { + scms, err := unix.ParseSocketControlMessage(oob) + if err != nil { + logrus.Errorf("parsing control message on socket %q: %v", p.socketPath, err) + } + for _, scm := range scms { + fds, err := unix.ParseUnixRights(&scm) + if err != nil { + logrus.Errorf("parsing unix rights of control message on socket %q: %v", p.socketPath, err) + continue + } + for _, fd := range fds { + if err := unix.Close(fd); err != nil { + logrus.Errorf("closing fd passed on socket %q: %v", fd, err) + continue + } + } + } + continue + } + + if isReady { + p.readyChan <- true + } } }() } @@ -137,8 +186,8 @@ func (p *NotifyProxy) SocketPath() string { return p.socketPath } -// close closes the listener and removes the socket. -func (p *NotifyProxy) close() error { +// Close closes the listener and removes the socket. +func (p *NotifyProxy) Close() error { defer os.Remove(p.socketPath) return p.connection.Close() } @@ -158,20 +207,12 @@ type Container interface { ID() string } -// WaitAndClose waits until receiving the `READY` notify message and close the -// listener. Note that the this function must only be executed inside a systemd -// service which will kill the process after a given timeout. -// If the (optional) container stopped running before the `READY` is received, -// the waiting gets canceled and ErrNoReadyMessage is returned. -func (p *NotifyProxy) WaitAndClose() error { - defer func() { - // Closing the socket/connection makes sure that the other - // goroutine reading/waiting for the READY message returns. - if err := p.close(); err != nil { - logrus.Errorf("Closing notify proxy: %v", err) - } - }() - +// WaitAndClose waits until receiving the `READY` notify message. Note that the +// this function must only be executed inside a systemd service which will kill +// the process after a given timeout. If the (optional) container stopped +// running before the `READY` is received, the waiting gets canceled and +// ErrNoReadyMessage is returned. +func (p *NotifyProxy) Wait() error { // If the proxy has a container we need to watch it as it may exit // without sending a READY message. The goroutine below returns when // the container exits OR when the function returns (see deferred the @@ -183,20 +224,21 @@ func (p *NotifyProxy) WaitAndClose() error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { - select { - case <-ctx.Done(): - return - default: - state, err := p.container.State() - if err != nil { - p.errorChan <- err - return - } - if state != define.ContainerStateRunning { - p.errorChan <- fmt.Errorf("%w: %s", ErrNoReadyMessage, p.container.ID()) + for { + select { + case <-ctx.Done(): return + case <-time.After(time.Second): + state, err := p.container.State() + if err != nil { + p.errorChan <- err + return + } + if state != define.ContainerStateRunning { + p.errorChan <- fmt.Errorf("%w: %s", ErrNoReadyMessage, p.container.ID()) + return + } } - time.Sleep(time.Second) } }() } diff --git a/vendor/github.com/containers/podman/v4/pkg/trust/policy.go b/vendor/github.com/containers/podman/v4/pkg/trust/policy.go index e0c5e068..aa14fc7e 100644 --- a/vendor/github.com/containers/podman/v4/pkg/trust/policy.go +++ b/vendor/github.com/containers/podman/v4/pkg/trust/policy.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/types" "github.com/sirupsen/logrus" ) @@ -53,7 +54,7 @@ type genericRepoMap map[string]json.RawMessage // DefaultPolicyPath returns a path to the default policy of the system. func DefaultPolicyPath(sys *types.SystemContext) string { - systemDefaultPolicyPath := "/etc/containers/policy.json" + systemDefaultPolicyPath := config.DefaultSignaturePolicyPath if sys != nil { if sys.SignaturePolicyPath != "" { return sys.SignaturePolicyPath diff --git a/vendor/github.com/containers/podman/v4/pkg/util/kube.go b/vendor/github.com/containers/podman/v4/pkg/util/kube.go index 1255cdfc..1a70ed05 100644 --- a/vendor/github.com/containers/podman/v4/pkg/util/kube.go +++ b/vendor/github.com/containers/podman/v4/pkg/util/kube.go @@ -13,4 +13,6 @@ const ( VolumeGIDAnnotation = "volume.podman.io/gid" // Kube annotation for podman volume mount options. VolumeMountOptsAnnotation = "volume.podman.io/mount-options" + // Kube annotation for podman volume import source. + VolumeImportSourceAnnotation = "volume.podman.io/import-source" ) diff --git a/vendor/github.com/containers/podman/v4/pkg/util/utils.go b/vendor/github.com/containers/podman/v4/pkg/util/utils.go index 87e40398..e7952a0b 100644 --- a/vendor/github.com/containers/podman/v4/pkg/util/utils.go +++ b/vendor/github.com/containers/podman/v4/pkg/util/utils.go @@ -1,7 +1,6 @@ package util import ( - "encoding/json" "errors" "fmt" "io/fs" @@ -20,13 +19,14 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/util" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" + enchelpers "github.com/containers/ocicrypt/helpers" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/signal" "github.com/containers/storage/pkg/idtools" stypes "github.com/containers/storage/types" - v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/term" @@ -95,236 +95,6 @@ func StringMatchRegexSlice(s string, re []string) bool { return false } -// ImageConfig is a wrapper around the OCIv1 Image Configuration struct exported -// by containers/image, but containing additional fields that are not supported -// by OCIv1 (but are by Docker v2) - notably OnBuild. -type ImageConfig struct { - v1.ImageConfig - OnBuild []string -} - -// GetImageConfig produces a v1.ImageConfig from the --change flag that is -// accepted by several Podman commands. It accepts a (limited subset) of -// Dockerfile instructions. -func GetImageConfig(changes []string) (ImageConfig, error) { - // Valid changes: - // USER - // EXPOSE - // ENV - // ENTRYPOINT - // CMD - // VOLUME - // WORKDIR - // LABEL - // STOPSIGNAL - // ONBUILD - - config := ImageConfig{} - - for _, change := range changes { - // First, let's assume proper Dockerfile format - space - // separator between instruction and value - split := strings.SplitN(change, " ", 2) - - if len(split) != 2 { - split = strings.SplitN(change, "=", 2) - if len(split) != 2 { - return ImageConfig{}, fmt.Errorf("invalid change %q - must be formatted as KEY VALUE", change) - } - } - - outerKey := strings.ToUpper(strings.TrimSpace(split[0])) - value := strings.TrimSpace(split[1]) - switch outerKey { - case "USER": - // Assume literal contents are the user. - if value == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - must provide a value to USER", change) - } - config.User = value - case "EXPOSE": - // EXPOSE is either [portnum] or - // [portnum]/[proto] - // Protocol must be "tcp" or "udp" - splitPort := strings.Split(value, "/") - if len(splitPort) > 2 { - return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be formatted as PORT[/PROTO]", change) - } - portNum, err := strconv.Atoi(splitPort[0]) - if err != nil { - return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be an integer: %w", change, err) - } - if portNum > 65535 || portNum <= 0 { - return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE port must be a valid port number", change) - } - proto := "tcp" - if len(splitPort) > 1 { - testProto := strings.ToLower(splitPort[1]) - switch testProto { - case "tcp", "udp": - proto = testProto - default: - return ImageConfig{}, fmt.Errorf("invalid change %q - EXPOSE protocol must be TCP or UDP", change) - } - } - if config.ExposedPorts == nil { - config.ExposedPorts = make(map[string]struct{}) - } - config.ExposedPorts[fmt.Sprintf("%d/%s", portNum, proto)] = struct{}{} - case "ENV": - // Format is either: - // ENV key=value - // ENV key=value key=value ... - // ENV key value - // Both keys and values can be surrounded by quotes to group them. - // For now: we only support key=value - // We will attempt to strip quotation marks if present. - - var ( - key, val string - ) - - splitEnv := strings.SplitN(value, "=", 2) - key = splitEnv[0] - // We do need a key - if key == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - ENV must have at least one argument", change) - } - // Perfectly valid to not have a value - if len(splitEnv) == 2 { - val = splitEnv[1] - } - - if strings.HasPrefix(key, `"`) && strings.HasSuffix(key, `"`) { - key = strings.TrimPrefix(strings.TrimSuffix(key, `"`), `"`) - } - if strings.HasPrefix(val, `"`) && strings.HasSuffix(val, `"`) { - val = strings.TrimPrefix(strings.TrimSuffix(val, `"`), `"`) - } - config.Env = append(config.Env, fmt.Sprintf("%s=%s", key, val)) - case "ENTRYPOINT": - // Two valid forms. - // First, JSON array. - // Second, not a JSON array - we interpret this as an - // argument to `sh -c`, unless empty, in which case we - // just use a blank entrypoint. - testUnmarshal := []string{} - if err := json.Unmarshal([]byte(value), &testUnmarshal); err != nil { - // It ain't valid JSON, so assume it's an - // argument to sh -c if not empty. - if value != "" { - config.Entrypoint = []string{"/bin/sh", "-c", value} - } else { - config.Entrypoint = []string{} - } - } else { - // Valid JSON - config.Entrypoint = testUnmarshal - } - case "CMD": - // Same valid forms as entrypoint. - // However, where ENTRYPOINT assumes that 'ENTRYPOINT ' - // means no entrypoint, CMD assumes it is 'sh -c' with - // no third argument. - testUnmarshal := []string{} - if err := json.Unmarshal([]byte(value), &testUnmarshal); err != nil { - // It ain't valid JSON, so assume it's an - // argument to sh -c. - // Only include volume if it's not "" - config.Cmd = []string{"/bin/sh", "-c"} - if value != "" { - config.Cmd = append(config.Cmd, value) - } - } else { - // Valid JSON - config.Cmd = testUnmarshal - } - case "VOLUME": - // Either a JSON array or a set of space-separated - // paths. - // Acts rather similar to ENTRYPOINT and CMD, but always - // appends rather than replacing, and no sh -c prepend. - testUnmarshal := []string{} - if err := json.Unmarshal([]byte(value), &testUnmarshal); err != nil { - // Not valid JSON, so split on spaces - testUnmarshal = strings.Split(value, " ") - } - if len(testUnmarshal) == 0 { - return ImageConfig{}, fmt.Errorf("invalid change %q - must provide at least one argument to VOLUME", change) - } - for _, vol := range testUnmarshal { - if vol == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - VOLUME paths must not be empty", change) - } - if config.Volumes == nil { - config.Volumes = make(map[string]struct{}) - } - config.Volumes[vol] = struct{}{} - } - case "WORKDIR": - // This can be passed multiple times. - // Each successive invocation is treated as relative to - // the previous one - so WORKDIR /A, WORKDIR b, - // WORKDIR c results in /A/b/c - // Just need to check it's not empty... - if value == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - must provide a non-empty WORKDIR", change) - } - config.WorkingDir = filepath.Join(config.WorkingDir, value) - case "LABEL": - // Same general idea as ENV, but we no longer allow " " - // as a separator. - // We didn't do that for ENV either, so nice and easy. - // Potentially problematic: LABEL might theoretically - // allow an = in the key? If people really do this, we - // may need to investigate more advanced parsing. - var ( - key, val string - ) - - splitLabel := strings.SplitN(value, "=", 2) - // Unlike ENV, LABEL must have a value - if len(splitLabel) != 2 { - return ImageConfig{}, fmt.Errorf("invalid change %q - LABEL must be formatted key=value", change) - } - key = splitLabel[0] - val = splitLabel[1] - - if strings.HasPrefix(key, `"`) && strings.HasSuffix(key, `"`) { - key = strings.TrimPrefix(strings.TrimSuffix(key, `"`), `"`) - } - if strings.HasPrefix(val, `"`) && strings.HasSuffix(val, `"`) { - val = strings.TrimPrefix(strings.TrimSuffix(val, `"`), `"`) - } - // Check key after we strip quotations - if key == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - LABEL must have a non-empty key", change) - } - if config.Labels == nil { - config.Labels = make(map[string]string) - } - config.Labels[key] = val - case "STOPSIGNAL": - // Check the provided signal for validity. - killSignal, err := ParseSignal(value) - if err != nil { - return ImageConfig{}, fmt.Errorf("invalid change %q - KILLSIGNAL must be given a valid signal: %w", change, err) - } - config.StopSignal = fmt.Sprintf("%d", killSignal) - case "ONBUILD": - // Onbuild always appends. - if value == "" { - return ImageConfig{}, fmt.Errorf("invalid change %q - ONBUILD must be given an argument", change) - } - config.OnBuild = append(config.OnBuild, value) - default: - return ImageConfig{}, fmt.Errorf("invalid change %q - invalid instruction %s", change, outerKey) - } - } - - return config, nil -} - // ParseSignal parses and validates a signal name or number. func ParseSignal(rawSignal string) (syscall.Signal, error) { // Strip off leading dash, to allow -1 or -HUP @@ -366,13 +136,11 @@ func GetKeepIDMapping(opts *namespaces.KeepIDUserNsOptions) (*stypes.IDMappingOp gid = int(*opts.GID) } - uids, gids, err := rootless.GetConfiguredMappings() + uids, gids, err := rootless.GetConfiguredMappings(true) if err != nil { return nil, -1, -1, fmt.Errorf("cannot read mappings: %w", err) } - if len(uids) == 0 || len(gids) == 0 { - return nil, -1, -1, fmt.Errorf("keep-id requires additional UIDs or GIDs defined in /etc/subuid and /etc/subgid to function correctly: %w", err) - } + maxUID, maxGID := 0, 0 for _, u := range uids { maxUID += u.Size @@ -383,13 +151,17 @@ func GetKeepIDMapping(opts *namespaces.KeepIDUserNsOptions) (*stypes.IDMappingOp options.UIDMap, options.GIDMap = nil, nil - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)}) + if len(uids) > 0 { + options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)}) + } options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1}) if maxUID > uid { options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid}) } - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)}) + if len(gids) > 0 { + options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)}) + } options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1}) if maxGID > gid { options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid}) @@ -407,7 +179,7 @@ func GetNoMapMapping() (*stypes.IDMappingOptions, int, int, error) { HostUIDMapping: false, HostGIDMapping: false, } - uids, gids, err := rootless.GetConfiguredMappings() + uids, gids, err := rootless.GetConfiguredMappings(false) if err != nil { return nil, -1, -1, fmt.Errorf("cannot read mappings: %w", err) } @@ -682,18 +454,15 @@ func DefaultContainerConfig() *config.Config { return containerConfig } -func CreateCidFile(cidfile string, id string) error { - cidFile, err := OpenExclusiveFile(cidfile) +func CreateIDFile(path string, id string) error { + idFile, err := os.Create(path) if err != nil { - if os.IsExist(err) { - return fmt.Errorf("container id file exists. Ensure another container is not using it or delete %s", cidfile) - } - return fmt.Errorf("opening cidfile %s", cidfile) + return fmt.Errorf("creating idfile: %w", err) } - if _, err = cidFile.WriteString(id); err != nil { - logrus.Error(err) + defer idFile.Close() + if _, err = idFile.WriteString(id); err != nil { + return fmt.Errorf("writing idfile: %w", err) } - cidFile.Close() return nil } @@ -734,7 +503,7 @@ func IDtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxI } func LookupUser(name string) (*user.User, error) { - // Assume UID look up first, if it fails lookup by username + // Assume UID lookup first, if it fails look up by username if u, err := user.LookupId(name); err == nil { return u, nil } @@ -757,3 +526,37 @@ func SizeOfPath(path string) (uint64, error) { }) return size, err } + +// EncryptConfig translates encryptionKeys into a EncriptionsConfig structure +func EncryptConfig(encryptionKeys []string, encryptLayers []int) (*encconfig.EncryptConfig, *[]int, error) { + var encLayers *[]int + var encConfig *encconfig.EncryptConfig + + if len(encryptionKeys) > 0 { + // encryption + encLayers = &encryptLayers + ecc, err := enchelpers.CreateCryptoConfig(encryptionKeys, []string{}) + if err != nil { + return nil, nil, fmt.Errorf("invalid encryption keys: %w", err) + } + cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{ecc}) + encConfig = cc.EncryptConfig + } + return encConfig, encLayers, nil +} + +// DecryptConfig translates decryptionKeys into a DescriptionConfig structure +func DecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) { + var decryptConfig *encconfig.DecryptConfig + if len(decryptionKeys) > 0 { + // decryption + dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys) + if err != nil { + return nil, fmt.Errorf("invalid decryption keys: %w", err) + } + cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc}) + decryptConfig = cc.DecryptConfig + } + + return decryptConfig, nil +} diff --git a/vendor/github.com/containers/podman/v4/pkg/util/utils_linux.go b/vendor/github.com/containers/podman/v4/pkg/util/utils_linux.go index 07927db1..c094beac 100644 --- a/vendor/github.com/containers/podman/v4/pkg/util/utils_linux.go +++ b/vendor/github.com/containers/podman/v4/pkg/util/utils_linux.go @@ -6,6 +6,7 @@ import ( "io/fs" "os" "path/filepath" + "strconv" "strings" "syscall" @@ -70,6 +71,25 @@ func FindDeviceNodes() (map[string]string, error) { return nodes, nil } +// isVirtualConsoleDevice returns true if path is a virtual console device +// (/dev/tty\d+). +// The passed path must be clean (filepath.Clean). +func isVirtualConsoleDevice(path string) bool { + /* + Virtual consoles are of the form `/dev/tty\d+`, any other device such as + /dev/tty, ttyUSB0, or ttyACM0 should not be matched. + See `man 4 console` for more information. + */ + suffix := strings.TrimPrefix(path, "/dev/tty") + if suffix == path || suffix == "" { + return false + } + + // 16bit because, max. supported TTY devices is 512 in Linux 6.1.5. + _, err := strconv.ParseUint(suffix, 10, 16) + return err == nil +} + func AddPrivilegedDevices(g *generate.Generator, systemdMode bool) error { hostDevices, err := getDevices("/dev") if err != nil { @@ -90,7 +110,19 @@ func AddPrivilegedDevices(g *generate.Generator, systemdMode bool) error { Source: d.Path, Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, } - if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { + + /* The following devices should not be mounted in rootless containers: + * + * /dev/ptmx: The host-provided /dev/ptmx should not be shared to + * the rootless containers for security reasons, and + * the container runtime will create it for us + * anyway (ln -s /dev/pts/ptmx /dev/ptmx); + * /dev/tty and + * /dev/tty[0-9]+: Prevent the container from taking over the host's + * virtual consoles, even when not in systemd mode + * for backwards compatibility. + */ + if d.Path == "/dev/ptmx" || d.Path == "/dev/tty" || isVirtualConsoleDevice(d.Path) { continue } if _, found := mounts[d.Path]; found { @@ -104,7 +136,17 @@ func AddPrivilegedDevices(g *generate.Generator, systemdMode bool) error { } } else { for _, d := range hostDevices { - if systemdMode && strings.HasPrefix(d.Path, "/dev/tty") { + /* Restrict access to the virtual consoles *only* when running + * in systemd mode to improve backwards compatibility. See + * https://github.com/containers/podman/issues/15878. + * + * NOTE: May need revisiting in the future to drop the systemd + * condition if more use cases end up breaking the virtual terminals + * of people who specifically disable the systemd mode. It would + * also provide a more consistent behaviour between rootless and + * rootfull containers. + */ + if systemdMode && isVirtualConsoleDevice(d.Path) { continue } g.AddDevice(d) diff --git a/vendor/github.com/containers/podman/v4/pkg/util/utils_supported.go b/vendor/github.com/containers/podman/v4/pkg/util/utils_supported.go index b3d69015..90a2ecf8 100644 --- a/vendor/github.com/containers/podman/v4/pkg/util/utils_supported.go +++ b/vendor/github.com/containers/podman/v4/pkg/util/utils_supported.go @@ -27,6 +27,12 @@ func GetRuntimeDir() (string, error) { rootlessRuntimeDirOnce.Do(func() { runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + + if runtimeDir != "" { + rootlessRuntimeDir, rootlessRuntimeDirError = filepath.EvalSymlinks(runtimeDir) + return + } + uid := fmt.Sprintf("%d", rootless.GetRootlessUID()) if runtimeDir == "" { tmpDir := filepath.Join("/run", "user", uid) diff --git a/vendor/github.com/containers/podman/v4/utils/utils.go b/vendor/github.com/containers/podman/v4/utils/utils.go index 5fb3695c..81b77e54 100644 --- a/vendor/github.com/containers/podman/v4/utils/utils.go +++ b/vendor/github.com/containers/podman/v4/utils/utils.go @@ -13,6 +13,7 @@ import ( "github.com/containers/common/pkg/cgroups" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/chrootarchive" "github.com/godbus/dbus/v5" "github.com/sirupsen/logrus" ) @@ -63,7 +64,7 @@ func CreateTarFromSrc(source string, dest string) error { return fmt.Errorf("could not create tarball file '%s': %w", dest, err) } defer file.Close() - return TarToFilesystem(source, file) + return TarChrootToFilesystem(source, file) } // TarToFilesystem creates a tarball from source and writes to an os.file @@ -87,6 +88,28 @@ func Tar(source string) (io.ReadCloser, error) { return archive.Tar(source, archive.Uncompressed) } +// TarChrootToFilesystem creates a tarball from source and writes to an os.file +// provided while chrooted to the source. +func TarChrootToFilesystem(source string, tarball *os.File) error { + tb, err := TarWithChroot(source) + if err != nil { + return err + } + _, err = io.Copy(tarball, tb) + if err != nil { + return err + } + logrus.Debugf("wrote tarball file %s", tarball.Name()) + return nil +} + +// TarWithChroot creates a tarball from source and returns a readcloser of it +// while chrooted to the source. +func TarWithChroot(source string) (io.ReadCloser, error) { + logrus.Debugf("creating tarball of %s", source) + return chrootarchive.Tar(source, nil, source) +} + // RemoveScientificNotationFromFloat returns a float without any // scientific notation if the number has any. // golang does not handle conversion of float64s that have scientific @@ -113,9 +136,9 @@ var ( // RunsOnSystemd returns whether the system is using systemd func RunsOnSystemd() bool { runsOnSystemdOnce.Do(func() { - initCommand, err := os.ReadFile("/proc/1/comm") - // On errors, default to systemd - runsOnSystemd = err != nil || strings.TrimRight(string(initCommand), "\n") == "systemd" + // per sd_booted(3), check for this dir + fd, err := os.Stat("/run/systemd/system") + runsOnSystemd = err == nil && fd.IsDir() }) return runsOnSystemd } diff --git a/vendor/github.com/containers/podman/v4/utils/utils_freebsd.go b/vendor/github.com/containers/podman/v4/utils/utils_freebsd.go deleted file mode 100644 index d239e9a5..00000000 --- a/vendor/github.com/containers/podman/v4/utils/utils_freebsd.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build freebsd -// +build freebsd - -package utils - -import "errors" - -func RunUnderSystemdScope(pid int, slice string, unitName string) error { - return errors.New("not implemented for freebsd") -} - -func MoveUnderCgroupSubtree(subtree string) error { - return errors.New("not implemented for freebsd") -} - -func GetOwnCgroup() (string, error) { - return "", errors.New("not implemented for freebsd") -} - -func GetCgroupProcess(pid int) (string, error) { - return "", errors.New("not implemented for freebsd") -} diff --git a/vendor/github.com/containers/podman/v4/utils/utils_supported.go b/vendor/github.com/containers/podman/v4/utils/utils_supported.go index 8844d46f..418c1f87 100644 --- a/vendor/github.com/containers/podman/v4/utils/utils_supported.go +++ b/vendor/github.com/containers/podman/v4/utils/utils_supported.go @@ -1,5 +1,5 @@ -//go:build linux || darwin -// +build linux darwin +//go:build linux || darwin || freebsd +// +build linux darwin freebsd package utils diff --git a/vendor/github.com/containers/podman/v4/version/version.go b/vendor/github.com/containers/podman/v4/version/version.go index 85de6d14..4513dc3b 100644 --- a/vendor/github.com/containers/podman/v4/version/version.go +++ b/vendor/github.com/containers/podman/v4/version/version.go @@ -27,7 +27,7 @@ const ( // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -var Version = semver.MustParse("4.3.1") +var Version = semver.MustParse("4.4.2") // See https://docs.docker.com/engine/api/v1.40/ // libpod compat handlers are expected to honor docker API versions diff --git a/vendor/github.com/containers/psgo/README.md b/vendor/github.com/containers/psgo/README.md index 684c80a0..e54423ca 100644 --- a/vendor/github.com/containers/psgo/README.md +++ b/vendor/github.com/containers/psgo/README.md @@ -83,6 +83,8 @@ The ps library is compatible with all AIX format descriptors of the ps command-l - The corresponding host PID of a container process. - **huser** - The corresponding effective user of a container process on the host. +- **huid** + - The corresponding host UID of a container process. - **label** - Current security attributes of the process. - **seccomp** diff --git a/vendor/github.com/containers/psgo/psgo.go b/vendor/github.com/containers/psgo/psgo.go index d6cfcef4..5c2c4d0b 100644 --- a/vendor/github.com/containers/psgo/psgo.go +++ b/vendor/github.com/containers/psgo/psgo.go @@ -181,6 +181,11 @@ var ( header: "USER", procFn: processUSER, }, + { + normal: "uid", + header: "UID", + procFn: processUID, + }, { code: "%a", normal: "args", @@ -294,6 +299,12 @@ var ( onHost: true, procFn: processHUSER, }, + { + normal: "huid", + header: "HUID", + onHost: true, + procFn: processHUID, + }, { normal: "hgroup", header: "HGROUP", @@ -648,6 +659,11 @@ func processUSER(p *process.Process, ctx *psContext) (string, error) { return process.LookupUID(p.Status.Uids[1]) } +// processUID returns the effective UID of the process as the decimal representation. +func processUID(p *process.Process, ctx *psContext) (string, error) { + return p.Status.Uids[1], nil +} + // processRUSER returns the effective user name of the process. This will be // the textual user ID, if it can be obtained, or a decimal representation // otherwise. @@ -857,6 +873,23 @@ func processHUSER(p *process.Process, ctx *psContext) (string, error) { return "?", nil } +// processHUID returns the effective UID of the corresponding host process +// of the (container) as the decimal representation or "?" if no corresponding +// process could be found. +func processHUID(p *process.Process, ctx *psContext) (string, error) { + if hp := findHostProcess(p, ctx); hp != nil { + if ctx.opts != nil && len(ctx.opts.UIDMap) > 0 { + // Return uid without searching its textual representation. + lookupFunc := func(uid string) (string, error) { + return uid, nil + } + return findID(hp.Status.Uids[1], ctx.opts.UIDMap, lookupFunc, "/proc/sys/fs/overflowuid") + } + return hp.Status.Uids[1], nil + } + return "?", nil +} + // processHGROUP returns the effective group ID of the corresponding host // process of the (container) or "?" if no corresponding process could be // found. diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml index e3e2b58f..6f904856 100644 --- a/vendor/github.com/containers/storage/.cirrus.yml +++ b/vendor/github.com/containers/storage/.cirrus.yml @@ -18,7 +18,6 @@ env: #### Cache-image names to test with (double-quotes around names are critical) ### FEDORA_NAME: "fedora-36" - PRIOR_FEDORA_NAME: "fedora-35" UBUNTU_NAME: "ubuntu-2204" # GCE project where images live @@ -26,7 +25,6 @@ env: # VM Image built in containers/automation_images IMAGE_SUFFIX: "c5878804328480768" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" - PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" #### @@ -74,6 +72,8 @@ fedora_testing_task: &fedora_testing TEST_DRIVER: "vfs" - env: TEST_DRIVER: "overlay" + - env: + TEST_DRIVER: "overlay-transient" - env: TEST_DRIVER: "fuse-overlay" - env: @@ -90,15 +90,6 @@ fedora_testing_task: &fedora_testing journal_log_script: '${_JOURNALCMD} || true' -prior_fedora_testing_task: - <<: *fedora_testing - alias: prior_fedora_testing - name: *std_test_name - env: - OS_NAME: "${PRIOR_FEDORA_NAME}" - VM_IMAGE: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - - ubuntu_testing_task: &ubuntu_testing <<: *fedora_testing alias: ubuntu_testing @@ -117,7 +108,7 @@ lint_task: env: CIRRUS_WORKING_DIR: "/go/src/github.com/containers/storage" container: - image: golang:1.16 + image: golang:1.17 modules_cache: fingerprint_script: cat go.sum folder: $GOPATH/pkg/mod @@ -125,7 +116,7 @@ lint_task: echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/backports.list apt-get update apt-get install -y libbtrfs-dev libdevmapper-dev - test_script: make lint + test_script: make TAGS=regex_precompile local-validate && make lint && make clean # Update metadata on VM images referenced by this repository state @@ -140,7 +131,6 @@ meta_task: # Space-separated list of images used by this repository state IMGNAMES: |- ${FEDORA_CACHE_IMAGE_NAME} - ${PRIOR_FEDORA_CACHE_IMAGE_NAME} ${UBUNTU_CACHE_IMAGE_NAME} BUILDID: "${CIRRUS_BUILD_ID}" REPOREF: "${CIRRUS_CHANGE_IN_REPO}" @@ -154,7 +144,7 @@ meta_task: vendor_task: container: - image: golang:1.16 + image: golang:1.17 modules_cache: fingerprint_script: cat go.sum folder: $GOPATH/pkg/mod @@ -167,11 +157,10 @@ success_task: depends_on: - lint - fedora_testing - - prior_fedora_testing - ubuntu_testing - meta - vendor container: - image: golang:1.16 + image: golang:1.17 clone_script: 'mkdir -p "$CIRRUS_WORKING_DIR"' # Source code not needed script: /bin/true diff --git a/vendor/github.com/containers/storage/Makefile b/vendor/github.com/containers/storage/Makefile index 244576d5..ea2bb640 100644 --- a/vendor/github.com/containers/storage/Makefile +++ b/vendor/github.com/containers/storage/Makefile @@ -60,7 +60,7 @@ local-gccgo: ## build using gccgo on the host GCCGO=$(PWD)/hack/gccgo-wrapper.sh $(GO) build $(MOD_VENDOR) -compiler gccgo $(BUILDFLAGS) -o containers-storage.gccgo ./cmd/containers-storage local-cross: ## cross build the binaries for arm, darwin, and freebsd - @for target in linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64 linux/ppc64le darwin/amd64 windows/amd64 freebsd/amd64 freebsd/arm64 ; do \ + @for target in linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64 linux/ppc64le linux/s390x linux/mips linux/mipsle linux/mips64 linux/mips64le darwin/amd64 windows/amd64 freebsd/amd64 freebsd/arm64 ; do \ os=`echo $${target} | cut -f1 -d/` ; \ arch=`echo $${target} | cut -f2 -d/` ; \ suffix=$${os}.$${arch} ; \ @@ -92,7 +92,7 @@ local-test-integration: local-binary ## run the integration tests on the host (r test-integration: local-binary ## run the integration tests using VMs $(RUNINVM) $(MAKE) local-$@ -local-validate: ## validate DCO and gofmt on the host +local-validate: install.tools ## validate DCO and gofmt on the host @./hack/git-validation.sh @./hack/gofmt.sh @@ -117,9 +117,9 @@ help: ## this help @awk 'BEGIN {FS = ":.*?## "} /^[a-z A-Z_-]+:.*?## / {gsub(" ",",",$$1);gsub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-21s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) vendor-in-container: - podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src golang make vendor + podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src golang:1.17 make vendor vendor: - $(GO) mod tidy + $(GO) mod tidy -compat=1.17 $(GO) mod vendor $(GO) mod verify diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index b978278f..53999456 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.43.0 +1.45.3 diff --git a/vendor/github.com/containers/storage/containers.go b/vendor/github.com/containers/storage/containers.go index 4f2b61f5..106d1d15 100644 --- a/vendor/github.com/containers/storage/containers.go +++ b/vendor/github.com/containers/storage/containers.go @@ -10,11 +10,28 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/truncindex" digest "github.com/opencontainers/go-digest" ) +type containerLocations uint8 + +// The backing store is split in two json files, one (the volatile) +// that is written without fsync() meaning it isn't as robust to +// unclean shutdown +const ( + stableContainerLocation containerLocations = 1 << iota + volatileContainerLocation + + numContainerLocationIndex = iota +) + +func containerLocationFromIndex(index int) containerLocations { + return 1 << index +} + // A Container is a reference to a read-write layer with metadata. type Container struct { // ID is either one which was specified at create-time, or a random @@ -64,14 +81,30 @@ type Container struct { GIDMap []idtools.IDMap `json:"gidmap,omitempty"` Flags map[string]interface{} `json:"flags,omitempty"` + + // volatileStore is true if the container is from the volatile json file + volatileStore bool `json:"-"` } -// ContainerStore provides bookkeeping for information about Containers. -type ContainerStore interface { - FileBasedStore - MetadataStore - ContainerBigDataStore - FlaggableStore +// rwContainerStore provides bookkeeping for information about Containers. +type rwContainerStore interface { + metadataStore + containerBigDataStore + flaggableStore + + // startWriting makes sure the store is fresh, and locks it for writing. + // If this succeeds, the caller MUST call stopWriting(). + startWriting() error + + // stopWriting releases locks obtained by startWriting. + stopWriting() + + // startReading makes sure the store is fresh, and locks it for reading. + // If this succeeds, the caller MUST call stopReading(). + startReading() error + + // stopReading releases locks obtained by startReading. + stopReading() // Create creates a container that has a specified ID (or generates a // random one if an empty value is supplied) and optional names, @@ -81,18 +114,8 @@ type ContainerStore interface { // convenience of the caller, nothing more. Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) - // SetNames updates the list of names associated with the container - // with the specified ID. - // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. - SetNames(id string, names []string) error - - // AddNames adds the supplied values to the list of names associated with the container with - // the specified id. - AddNames(id string, names []string) error - - // RemoveNames removes the supplied values from the list of names associated with the container with - // the specified id. - RemoveNames(id string, names []string) error + // updateNames modifies names associated with a container based on (op, names). + updateNames(id string, names []string, op updateNameOperation) error // Get retrieves information about a container given an ID or name. Get(id string) (*Container, error) @@ -112,17 +135,27 @@ type ContainerStore interface { // Containers returns a slice enumerating the known containers. Containers() ([]Container, error) + + // Clean up unreferenced datadirs + GarbageCollect() error } type containerStore struct { - lockfile Locker - dir string + // The following fields are only set when constructing containerStore, and must never be modified afterwards. + // They are safe to access without any other locking. + lockfile *lockfile.LockFile // Synchronizes readers vs. writers of the _filesystem data_, both cross-process and in-process. + dir string + jsonPath [numContainerLocationIndex]string + + inProcessLock sync.RWMutex // Can _only_ be obtained with lockfile held. + // The following fields can only be read/written with read/write ownership of inProcessLock, respectively. + // Almost all users should use startReading() or startWriting(). + lastWrite lockfile.LastWrite containers []*Container idindex *truncindex.TruncIndex byid map[string]*Container bylayer map[string]*Container byname map[string]*Container - loadMut sync.Mutex } func copyContainer(c *Container) *Container { @@ -139,6 +172,7 @@ func copyContainer(c *Container) *Container { UIDMap: copyIDMap(c.UIDMap), GIDMap: copyIDMap(c.GIDMap), Flags: copyStringInterfaceMap(c.Flags), + volatileStore: c.volatileStore, } } @@ -157,12 +191,12 @@ func (c *Container) ProcessLabel() string { } func (c *Container) MountOpts() []string { - switch c.Flags[mountOptsFlag].(type) { + switch value := c.Flags[mountOptsFlag].(type) { case []string: - return c.Flags[mountOptsFlag].([]string) + return value case []interface{}: var mountOpts []string - for _, v := range c.Flags[mountOptsFlag].([]interface{}) { + for _, v := range value { if flag, ok := v.(string); ok { mountOpts = append(mountOpts, flag) } @@ -173,6 +207,187 @@ func (c *Container) MountOpts() []string { } } +// The caller must hold r.inProcessLock for reading. +func containerLocation(c *Container) containerLocations { + if c.volatileStore { + return volatileContainerLocation + } + return stableContainerLocation +} + +// startWritingWithReload makes sure the store is fresh if canReload, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +// +// This is an internal implementation detail of containerStore construction, every other caller +// should use startWriting() instead. +func (r *containerStore) startWritingWithReload(canReload bool) error { + r.lockfile.Lock() + r.inProcessLock.Lock() + succeeded := false + defer func() { + if !succeeded { + r.inProcessLock.Unlock() + r.lockfile.Unlock() + } + }() + + if canReload { + if _, err := r.reloadIfChanged(true); err != nil { + return err + } + } + + succeeded = true + return nil +} + +// startWriting makes sure the store is fresh, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +func (r *containerStore) startWriting() error { + return r.startWritingWithReload(true) +} + +// stopWriting releases locks obtained by startWriting. +func (r *containerStore) stopWriting() { + r.inProcessLock.Unlock() + r.lockfile.Unlock() +} + +// startReading makes sure the store is fresh, and locks it for reading. +// If this succeeds, the caller MUST call stopReading(). +func (r *containerStore) startReading() error { + // inProcessLocked calls the nested function with r.inProcessLock held for writing. + inProcessLocked := func(fn func() error) error { + r.inProcessLock.Lock() + defer r.inProcessLock.Unlock() + return fn() + } + + r.lockfile.RLock() + unlockFn := r.lockfile.Unlock // A function to call to clean up, or nil. + defer func() { + if unlockFn != nil { + unlockFn() + } + }() + r.inProcessLock.RLock() + unlockFn = r.stopReading + + // If we are lucky, we can just hold the read locks, check that we are fresh, and continue. + _, modified, err := r.modified() + if err != nil { + return err + } + if modified { + // We are unlucky, and need to reload. + // NOTE: Multiple goroutines can get to this place approximately simultaneously. + r.inProcessLock.RUnlock() + unlockFn = r.lockfile.Unlock + + // r.lastWrite can change at this point if another goroutine reloads the store before us. That’s why we don’t unconditionally + // trigger a load below; we (lock and) reloadIfChanged() again. + + // First try reloading with r.lockfile held for reading. + // r.inProcessLock will serialize all goroutines that got here; + // each will re-check on-disk state vs. r.lastWrite, and the first one will actually reload the data. + var tryLockedForWriting bool + if err := inProcessLocked(func() error { + // We could optimize this further: The r.lockfile.GetLastWrite() value shouldn’t change as long as we hold r.lockfile, + // so if r.lastWrite was already updated, we don’t need to actually read the on-filesystem lock. + var err error + tryLockedForWriting, err = r.reloadIfChanged(false) + return err + }); err != nil { + if !tryLockedForWriting { + return err + } + // Not good enough, we need r.lockfile held for writing. So, let’s do that. + unlockFn() + unlockFn = nil + + r.lockfile.Lock() + unlockFn = r.lockfile.Unlock + if err := inProcessLocked(func() error { + _, err := r.reloadIfChanged(true) + return err + }); err != nil { + return err + } + unlockFn() + unlockFn = nil + + r.lockfile.RLock() + unlockFn = r.lockfile.Unlock + // We need to check for a reload once more because the on-disk state could have been modified + // after we released the lock. + // If that, _again_, finds inconsistent state, just give up. + // We could, plausibly, retry a few times, but that inconsistent state (duplicate container names) + // shouldn’t be saved (by correct implementations) in the first place. + if err := inProcessLocked(func() error { + _, err := r.reloadIfChanged(false) + return err + }); err != nil { + return fmt.Errorf("(even after successfully cleaning up once:) %w", err) + } + } + + // NOTE that we hold neither a read nor write inProcessLock at this point. That’s fine in ordinary operation, because + // the on-filesystem r.lockfile should protect us against (cooperating) writers, and any use of r.inProcessLock + // protects us against in-process writers modifying data. + // In presence of non-cooperating writers, we just ensure that 1) the in-memory data is not clearly out-of-date + // and 2) access to the in-memory data is not racy; + // but we can’t protect against those out-of-process writers modifying _files_ while we are assuming they are in a consistent state. + + r.inProcessLock.RLock() + } + unlockFn = nil + return nil +} + +// stopReading releases locks obtained by startReading. +func (r *containerStore) stopReading() { + r.inProcessLock.RUnlock() + r.lockfile.Unlock() +} + +// modified returns true if the on-disk state has changed (i.e. if reloadIfChanged may need to modify the store), +// and a lockfile.LastWrite value for that update. +// +// The caller must hold r.lockfile for reading _or_ writing. +// The caller must hold r.inProcessLock for reading or writing. +func (r *containerStore) modified() (lockfile.LastWrite, bool, error) { + return r.lockfile.ModifiedSince(r.lastWrite) +} + +// reloadIfChanged reloads the contents of the store from disk if it is changed. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// reloadIfChanged() with lockedForWriting could succeed. +func (r *containerStore) reloadIfChanged(lockedForWriting bool) (bool, error) { + lastWrite, modified, err := r.modified() + if err != nil { + return false, err + } + // We require callers to always hold r.inProcessLock for WRITING, even if they might not end up calling r.load() + // and modify no fields, to ensure they see fresh data: + // r.lockfile.Modified() only returns true once per change. Without an exclusive lock, + // one goroutine might see r.lockfile.Modified() == true and decide to load, and in the meanwhile another one could + // see r.lockfile.Modified() == false and proceed to use in-memory data without noticing it is stale. + if modified { + if tryLockedForWriting, err := r.load(lockedForWriting); err != nil { + return tryLockedForWriting, err // r.lastWrite is unchanged, so we will load the next time again. + } + r.lastWrite = lastWrite + } + return false, nil +} + +// Requires startReading or startWriting. func (r *containerStore) Containers() ([]Container, error) { containers := make([]Container, len(r.containers)) for i := range r.containers { @@ -181,8 +396,38 @@ func (r *containerStore) Containers() ([]Container, error) { return containers, nil } -func (r *containerStore) containerspath() string { - return filepath.Join(r.dir, "containers.json") +// This looks for datadirs in the store directory that are not referenced +// by the json file and removes it. These can happen in the case of unclean +// shutdowns or regular restarts in transient store mode. +// Requires startReading. +func (r *containerStore) GarbageCollect() error { + entries, err := os.ReadDir(r.dir) + if err != nil { + // Unexpected, don't try any GC + return err + } + + for _, entry := range entries { + id := entry.Name() + // Does it look like a datadir directory? + if !entry.IsDir() || !nameLooksLikeID(id) { + continue + } + + // Should the id be there? + if r.byid[id] != nil { + continue + } + + // Otherwise remove datadir + moreErr := os.RemoveAll(filepath.Join(r.dir, id)) + // Propagate first error + if moreErr != nil && err == nil { + err = moreErr + } + } + + return err } func (r *containerStore) datadir(id string) string { @@ -193,84 +438,178 @@ func (r *containerStore) datapath(id, key string) string { return filepath.Join(r.datadir(id), makeBigDataBaseName(key)) } -func (r *containerStore) Load() error { - needSave := false - rpath := r.containerspath() - data, err := os.ReadFile(rpath) - if err != nil && !os.IsNotExist(err) { - return err - } +// load reloads the contents of the store from disk. +// +// Most callers should call reloadIfChanged() instead, to avoid overhead and to correctly +// manage r.lastWrite. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// retrying with lockedForWriting could succeed. +func (r *containerStore) load(lockedForWriting bool) (bool, error) { + var modifiedLocations containerLocations containers := []*Container{} - layers := make(map[string]*Container) - idlist := []string{} + ids := make(map[string]*Container) + + for locationIndex := 0; locationIndex < numContainerLocationIndex; locationIndex++ { + location := containerLocationFromIndex(locationIndex) + rpath := r.jsonPath[locationIndex] + + data, err := os.ReadFile(rpath) + if err != nil && !os.IsNotExist(err) { + return false, err + } + + locationContainers := []*Container{} + if len(data) != 0 { + if err := json.Unmarshal(data, &locationContainers); err != nil { + return false, fmt.Errorf("loading %q: %w", rpath, err) + } + } + + for _, container := range locationContainers { + // There should be no duplicated ids between json files, but lets check to be sure + if ids[container.ID] != nil { + continue // skip invalid duplicated container + } + // Remember where the container came from + if location == volatileContainerLocation { + container.volatileStore = true + } + containers = append(containers, container) + ids[container.ID] = container + } + } + + idlist := make([]string, 0, len(containers)) + layers := make(map[string]*Container) names := make(map[string]*Container) - if err = json.Unmarshal(data, &containers); len(data) == 0 || err == nil { - idlist = make([]string, 0, len(containers)) - for n, container := range containers { - idlist = append(idlist, container.ID) - ids[container.ID] = containers[n] - layers[container.LayerID] = containers[n] - for _, name := range container.Names { - if conflict, ok := names[name]; ok { - r.removeName(conflict, name) - needSave = true - } - names[name] = containers[n] + var errorToResolveBySaving error // == nil + for n, container := range containers { + idlist = append(idlist, container.ID) + layers[container.LayerID] = containers[n] + for _, name := range container.Names { + if conflict, ok := names[name]; ok { + r.removeName(conflict, name) + errorToResolveBySaving = errors.New("container store is inconsistent and the current caller does not hold a write lock") + modifiedLocations |= containerLocation(container) } + names[name] = containers[n] } } + 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.bylayer = layers r.byname = names - if needSave { - return r.Save() + if errorToResolveBySaving != nil { + if !lockedForWriting { + return true, errorToResolveBySaving + } + return false, r.save(modifiedLocations) } - return nil + return false, nil } -func (r *containerStore) Save() error { - if !r.Locked() { - return errors.New("container store is not locked") - } - rpath := r.containerspath() - if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { - return err +// save saves the contents of the store to disk. +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for reading (but usually holds it for writing in order to make the desired changes). +func (r *containerStore) save(saveLocations containerLocations) error { + r.lockfile.AssertLockedForWriting() + for locationIndex := 0; locationIndex < numContainerLocationIndex; locationIndex++ { + location := containerLocationFromIndex(locationIndex) + if location&saveLocations == 0 { + continue + } + rpath := r.jsonPath[locationIndex] + if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { + return err + } + subsetContainers := make([]*Container, 0, len(r.containers)) + for _, container := range r.containers { + if containerLocation(container) == location { + subsetContainers = append(subsetContainers, container) + } + } + + jdata, err := json.Marshal(&subsetContainers) + if err != nil { + return err + } + var opts *ioutils.AtomicFileWriterOptions + if location == volatileContainerLocation { + opts = &ioutils.AtomicFileWriterOptions{ + NoSync: true, + } + } + if err := ioutils.AtomicWriteFileWithOpts(rpath, jdata, 0600, opts); err != nil { + return err + } } - jdata, err := json.Marshal(&r.containers) + lw, err := r.lockfile.RecordWrite() if err != nil { return err } - defer r.Touch() - return ioutils.AtomicWriteFile(rpath, jdata, 0600) + r.lastWrite = lw + return nil } -func newContainerStore(dir string) (ContainerStore, error) { +// saveFor saves the contents of the store relevant for modifiedContainer to disk. +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for reading (but usually holds it for writing in order to make the desired changes). +func (r *containerStore) saveFor(modifiedContainer *Container) error { + return r.save(containerLocation(modifiedContainer)) +} + +func newContainerStore(dir string, runDir string, transient bool) (rwContainerStore, error) { if err := os.MkdirAll(dir, 0700); err != nil { return nil, err } - lockfile, err := GetLockfile(filepath.Join(dir, "containers.lock")) + volatileDir := dir + if transient { + if err := os.MkdirAll(runDir, 0700); err != nil { + return nil, err + } + volatileDir = runDir + } + lockfile, err := lockfile.GetLockFile(filepath.Join(volatileDir, "containers.lock")) if err != nil { return nil, err } - lockfile.Lock() - defer lockfile.Unlock() cstore := containerStore{ - lockfile: lockfile, - dir: dir, + lockfile: lockfile, + dir: dir, + jsonPath: [numContainerLocationIndex]string{ + filepath.Join(dir, "containers.json"), + filepath.Join(volatileDir, "volatile-containers.json"), + }, + containers: []*Container{}, byid: make(map[string]*Container), bylayer: make(map[string]*Container), byname: make(map[string]*Container), } - if err := cstore.Load(); err != nil { + + if err := cstore.startWritingWithReload(false); err != nil { + return nil, err + } + cstore.lastWrite, err = cstore.lockfile.GetLastWrite() + if err != nil { + return nil, err + } + defer cstore.stopWriting() + if _, err := cstore.load(true); err != nil { return nil, err } return &cstore, nil } +// Requires startReading or startWriting. func (r *containerStore) lookup(id string) (*Container, bool) { if container, ok := r.byid[id]; ok { return container, ok @@ -286,15 +625,17 @@ func (r *containerStore) lookup(id string) (*Container, bool) { return nil, false } +// Requires startWriting. func (r *containerStore) ClearFlag(id string, flag string) error { container, ok := r.lookup(id) if !ok { return ErrContainerUnknown } delete(container.Flags, flag) - return r.Save() + return r.saveFor(container) } +// Requires startWriting. func (r *containerStore) SetFlag(id string, flag string, value interface{}) error { container, ok := r.lookup(id) if !ok { @@ -304,9 +645,10 @@ func (r *containerStore) SetFlag(id string, flag string, value interface{}) erro container.Flags = make(map[string]interface{}) } container.Flags[flag] = value - return r.Save() + return r.saveFor(container) } +// Requires startWriting. func (r *containerStore) Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (container *Container, err error) { if id == "" { id = stringid.GenerateRandomID() @@ -337,34 +679,36 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat if err := hasOverlappingRanges(options.GIDMap); err != nil { return nil, err } - if err == nil { - container = &Container{ - ID: id, - Names: names, - ImageID: image, - LayerID: layer, - Metadata: metadata, - BigDataNames: []string{}, - BigDataSizes: make(map[string]int64), - BigDataDigests: make(map[string]digest.Digest), - Created: time.Now().UTC(), - Flags: copyStringInterfaceMap(options.Flags), - UIDMap: copyIDMap(options.UIDMap), - GIDMap: copyIDMap(options.GIDMap), - } - r.containers = append(r.containers, container) - r.byid[id] = container - r.idindex.Add(id) - r.bylayer[layer] = container - for _, name := range names { - r.byname[name] = container - } - err = r.Save() - container = copyContainer(container) + container = &Container{ + ID: id, + Names: names, + ImageID: image, + LayerID: layer, + Metadata: metadata, + BigDataNames: []string{}, + BigDataSizes: make(map[string]int64), + BigDataDigests: make(map[string]digest.Digest), + Created: time.Now().UTC(), + Flags: copyStringInterfaceMap(options.Flags), + UIDMap: copyIDMap(options.UIDMap), + GIDMap: copyIDMap(options.GIDMap), + volatileStore: options.Volatile, + } + r.containers = append(r.containers, container) + r.byid[id] = container + // 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 + for _, name := range names { + r.byname[name] = container } + err = r.saveFor(container) + container = copyContainer(container) return container, err } +// Requires startReading or startWriting. func (r *containerStore) Metadata(id string) (string, error) { if container, ok := r.lookup(id); ok { return container.Metadata, nil @@ -372,31 +716,21 @@ func (r *containerStore) Metadata(id string) (string, error) { return "", ErrContainerUnknown } +// Requires startWriting. func (r *containerStore) SetMetadata(id, metadata string) error { if container, ok := r.lookup(id); ok { container.Metadata = metadata - return r.Save() + return r.saveFor(container) } return ErrContainerUnknown } +// The caller must hold r.inProcessLock for writing. func (r *containerStore) removeName(container *Container, name string) { container.Names = stringSliceWithoutValue(container.Names, name) } -// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. -func (r *containerStore) SetNames(id string, names []string) error { - return r.updateNames(id, names, setNames) -} - -func (r *containerStore) AddNames(id string, names []string) error { - return r.updateNames(id, names, addNames) -} - -func (r *containerStore) RemoveNames(id string, names []string) error { - return r.updateNames(id, names, removeNames) -} - +// Requires startWriting. func (r *containerStore) updateNames(id string, names []string, op updateNameOperation) error { container, ok := r.lookup(id) if !ok { @@ -417,9 +751,10 @@ func (r *containerStore) updateNames(id string, names []string, op updateNameOpe r.byname[name] = container } container.Names = names - return r.Save() + return r.saveFor(container) } +// Requires startWriting. func (r *containerStore) Delete(id string) error { container, ok := r.lookup(id) if !ok { @@ -434,7 +769,9 @@ func (r *containerStore) Delete(id string) error { } } 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) for _, name := range container.Names { delete(r.byname, name) @@ -447,7 +784,7 @@ func (r *containerStore) Delete(id string) error { r.containers = append(r.containers[:toDeleteIndex], r.containers[toDeleteIndex+1:]...) } } - if err := r.Save(); err != nil { + if err := r.saveFor(container); err != nil { return err } if err := os.RemoveAll(r.datadir(id)); err != nil { @@ -456,6 +793,7 @@ func (r *containerStore) Delete(id string) error { return nil } +// Requires startReading or startWriting. func (r *containerStore) Get(id string) (*Container, error) { if container, ok := r.lookup(id); ok { return copyContainer(container), nil @@ -463,6 +801,7 @@ func (r *containerStore) Get(id string) (*Container, error) { return nil, ErrContainerUnknown } +// Requires startReading or startWriting. func (r *containerStore) Lookup(name string) (id string, err error) { if container, ok := r.lookup(name); ok { return container.ID, nil @@ -470,11 +809,13 @@ func (r *containerStore) Lookup(name string) (id string, err error) { return "", ErrContainerUnknown } +// Requires startReading or startWriting. func (r *containerStore) Exists(id string) bool { _, ok := r.lookup(id) return ok } +// Requires startReading or startWriting. func (r *containerStore) BigData(id, key string) ([]byte, error) { if key == "" { return nil, fmt.Errorf("can't retrieve container big data value for empty name: %w", ErrInvalidBigDataName) @@ -486,6 +827,7 @@ func (r *containerStore) BigData(id, key string) ([]byte, error) { return os.ReadFile(r.datapath(c.ID, key)) } +// Requires startWriting. Yes, really, WRITING (see SetBigData). func (r *containerStore) BigDataSize(id, key string) (int64, error) { if key == "" { return -1, fmt.Errorf("can't retrieve size of container big data with empty name: %w", ErrInvalidBigDataName) @@ -494,10 +836,7 @@ func (r *containerStore) BigDataSize(id, key string) (int64, error) { if !ok { return -1, ErrContainerUnknown } - if c.BigDataSizes == nil { - c.BigDataSizes = make(map[string]int64) - } - if size, ok := c.BigDataSizes[key]; ok { + if size, ok := c.BigDataSizes[key]; ok { // This is valid, and returns ok == false, for BigDataSizes == nil. return size, nil } if data, err := r.BigData(id, key); err == nil && data != nil { @@ -516,6 +855,7 @@ func (r *containerStore) BigDataSize(id, key string) (int64, error) { return -1, ErrSizeUnknown } +// Requires startWriting. Yes, really, WRITING (see SetBigData). func (r *containerStore) BigDataDigest(id, key string) (digest.Digest, error) { if key == "" { return "", fmt.Errorf("can't retrieve digest of container big data value with empty name: %w", ErrInvalidBigDataName) @@ -524,10 +864,7 @@ func (r *containerStore) BigDataDigest(id, key string) (digest.Digest, error) { if !ok { return "", ErrContainerUnknown } - if c.BigDataDigests == nil { - c.BigDataDigests = make(map[string]digest.Digest) - } - if d, ok := c.BigDataDigests[key]; ok { + if d, ok := c.BigDataDigests[key]; ok { // This is valid, and returns ok == false, for BigDataSizes == nil. return d, nil } if data, err := r.BigData(id, key); err == nil && data != nil { @@ -546,6 +883,7 @@ func (r *containerStore) BigDataDigest(id, key string) (digest.Digest, error) { return "", ErrDigestUnknown } +// Requires startReading or startWriting. func (r *containerStore) BigDataNames(id string) ([]string, error) { c, ok := r.lookup(id) if !ok { @@ -554,6 +892,7 @@ func (r *containerStore) BigDataNames(id string) ([]string, error) { return copyStringSlice(c.BigDataNames), nil } +// Requires startWriting. func (r *containerStore) SetBigData(id, key string, data []byte) error { if key == "" { return fmt.Errorf("can't set empty name for container big data item: %w", ErrInvalidBigDataName) @@ -594,12 +933,13 @@ func (r *containerStore) SetBigData(id, key string, data []byte) error { save = true } if save { - err = r.Save() + err = r.saveFor(c) } } return err } +// Requires startWriting. func (r *containerStore) Wipe() error { ids := make([]string, 0, len(r.byid)) for id := range r.byid { @@ -612,50 +952,3 @@ func (r *containerStore) Wipe() error { } return nil } - -func (r *containerStore) Lock() { - r.lockfile.Lock() -} - -func (r *containerStore) RecursiveLock() { - r.lockfile.RecursiveLock() -} - -func (r *containerStore) RLock() { - r.lockfile.RLock() -} - -func (r *containerStore) Unlock() { - r.lockfile.Unlock() -} - -func (r *containerStore) Touch() error { - return r.lockfile.Touch() -} - -func (r *containerStore) Modified() (bool, error) { - return r.lockfile.Modified() -} - -func (r *containerStore) IsReadWrite() bool { - return r.lockfile.IsReadWrite() -} - -func (r *containerStore) TouchedSince(when time.Time) bool { - return r.lockfile.TouchedSince(when) -} - -func (r *containerStore) Locked() bool { - return r.lockfile.Locked() -} - -func (r *containerStore) ReloadIfChanged() error { - r.loadMut.Lock() - defer r.loadMut.Unlock() - - modified, err := r.Modified() - if err == nil && modified { - return r.Load() - } - return err -} diff --git a/vendor/github.com/containers/storage/deprecated.go b/vendor/github.com/containers/storage/deprecated.go new file mode 100644 index 00000000..04972d83 --- /dev/null +++ b/vendor/github.com/containers/storage/deprecated.go @@ -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) +} diff --git a/vendor/github.com/containers/storage/drivers/aufs/aufs.go b/vendor/github.com/containers/storage/drivers/aufs/aufs.go index 2642874b..10341d41 100644 --- a/vendor/github.com/containers/storage/drivers/aufs/aufs.go +++ b/vendor/github.com/containers/storage/drivers/aufs/aufs.go @@ -67,7 +67,7 @@ var ( const defaultPerms = os.FileMode(0555) func init() { - graphdriver.Register("aufs", Init) + graphdriver.MustRegister("aufs", Init) } // Driver contains information about the filesystem mounted. @@ -251,6 +251,11 @@ func (a *Driver) Exists(id string) bool { return true } +// List layers (not including additional image stores) +func (a *Driver) ListLayers() ([]string, error) { + return nil, graphdriver.ErrNotSupported +} + // AdditionalImageStores returns additional image stores supported by the driver func (a *Driver) AdditionalImageStores() []string { return nil diff --git a/vendor/github.com/containers/storage/drivers/aufs/mount.go b/vendor/github.com/containers/storage/drivers/aufs/mount.go index 100e7537..156f4a4f 100644 --- a/vendor/github.com/containers/storage/drivers/aufs/mount.go +++ b/vendor/github.com/containers/storage/drivers/aufs/mount.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package aufs diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go index 0b5d1c51..3d905329 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go @@ -6,6 +6,9 @@ package btrfs /* #include #include + +// keep struct field name compatible with btrfs-progs < 6.1. +#define max_referenced max_rfer #include #include @@ -42,7 +45,7 @@ import ( const defaultPerms = os.FileMode(0555) func init() { - graphdriver.Register("btrfs", Init) + graphdriver.MustRegister("btrfs", Init) } type btrfsOptions struct { @@ -382,7 +385,7 @@ func subvolLimitQgroup(path string, size uint64) error { defer closeDir(dir) var args C.struct_btrfs_ioctl_qgroup_limit_args - args.lim.max_referenced = C.__u64(size) + args.lim.max_rfer = C.__u64(size) args.lim.flags = C.BTRFS_QGROUP_LIMIT_MAX_RFER _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QGROUP_LIMIT, uintptr(unsafe.Pointer(&args))) @@ -676,6 +679,11 @@ func (d *Driver) Exists(id string) bool { return err == nil } +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + return nil, graphdriver.ErrNotSupported +} + // AdditionalImageStores returns additional image stores supported by the driver func (d *Driver) AdditionalImageStores() []string { return nil diff --git a/vendor/github.com/containers/storage/drivers/btrfs/dummy_unsupported.go b/vendor/github.com/containers/storage/drivers/btrfs/dummy_unsupported.go index f0708888..c7d9d3b8 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/dummy_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/dummy_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux || !cgo // +build !linux !cgo package btrfs diff --git a/vendor/github.com/containers/storage/drivers/btrfs/version.go b/vendor/github.com/containers/storage/drivers/btrfs/version.go index edd8bdab..5816139f 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/version.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/version.go @@ -1,3 +1,4 @@ +//go:build linux && !btrfs_noversion && cgo // +build linux,!btrfs_noversion,cgo package btrfs diff --git a/vendor/github.com/containers/storage/drivers/chown.go b/vendor/github.com/containers/storage/drivers/chown.go index 34a76b82..ca43c3f0 100644 --- a/vendor/github.com/containers/storage/drivers/chown.go +++ b/vendor/github.com/containers/storage/drivers/chown.go @@ -115,7 +115,7 @@ func NewNaiveLayerIDMapUpdater(driver ProtoDriver) LayerIDMapUpdater { // 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 // 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 options := MountOpts{ MountLabel: mountLabel, @@ -124,9 +124,7 @@ func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost if err != nil { return err } - defer func() { - driver.Put(id) - }() + defer driverPut(driver, id, &retErr) return ChownPathByMaps(layerFs, toContainer, toHost) } diff --git a/vendor/github.com/containers/storage/drivers/chown_darwin.go b/vendor/github.com/containers/storage/drivers/chown_darwin.go index a732075f..d6150cee 100644 --- a/vendor/github.com/containers/storage/drivers/chown_darwin.go +++ b/vendor/github.com/containers/storage/drivers/chown_darwin.go @@ -83,7 +83,7 @@ func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContai uid, gid = mappedPair.UID, mappedPair.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 { 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) } } - if cap != nil { - if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil { + if capability != nil { + if err := system.Lsetxattr(path, "security.capability", capability, 0); err != nil { return fmt.Errorf("%s: %w", os.Args[0], err) } } diff --git a/vendor/github.com/containers/storage/drivers/copy/copy_unsupported.go b/vendor/github.com/containers/storage/drivers/copy/copy_unsupported.go index e97523c3..470e6a72 100644 --- a/vendor/github.com/containers/storage/drivers/copy/copy_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/copy/copy_unsupported.go @@ -1,6 +1,7 @@ +//go:build !linux || !cgo // +build !linux !cgo -package copy +package copy //nolint: predeclared import ( "io" @@ -24,7 +25,7 @@ func DirCopy(srcDir, dstDir string, _ Mode, _ bool) error { } // 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) if err != nil { 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 -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) } diff --git a/vendor/github.com/containers/storage/drivers/counter.go b/vendor/github.com/containers/storage/drivers/counter.go index 3fc45495..01576667 100644 --- a/vendor/github.com/containers/storage/drivers/counter.go +++ b/vendor/github.com/containers/storage/drivers/counter.go @@ -58,6 +58,11 @@ func (c *RefCounter) incdec(path string, infoOp func(minfo *minfo)) int { } infoOp(m) count := m.count + if count <= 0 { + // If the mounted path has been decremented enough have no references, + // then its entry can be removed. + delete(c.counts, path) + } c.mu.Unlock() return count } diff --git a/vendor/github.com/containers/storage/drivers/devmapper/devmapper_doc.go b/vendor/github.com/containers/storage/drivers/devmapper/devmapper_doc.go index 418b9e61..f85fb947 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/devmapper_doc.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/devmapper_doc.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devmapper diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go index f9f775a5..8b3ee51d 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go @@ -23,7 +23,7 @@ import ( const defaultPerms = os.FileMode(0555) func init() { - graphdriver.Register("devicemapper", Init) + graphdriver.MustRegister("devicemapper", Init) } // Driver contains the device set mounted and the home directory @@ -267,6 +267,11 @@ func (d *Driver) Exists(id string) bool { return d.DeviceSet.HasDevice(id) } +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + return nil, graphdriver.ErrNotSupported +} + // AdditionalImageStores returns additional image stores supported by the driver func (d *Driver) AdditionalImageStores() []string { return nil diff --git a/vendor/github.com/containers/storage/drivers/devmapper/mount.go b/vendor/github.com/containers/storage/drivers/devmapper/mount.go index 41e73faf..db903ca5 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/mount.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/mount.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devmapper diff --git a/vendor/github.com/containers/storage/drivers/driver.go b/vendor/github.com/containers/storage/drivers/driver.go index 7d96ebe5..b3b0614f 100644 --- a/vendor/github.com/containers/storage/drivers/driver.go +++ b/vendor/github.com/containers/storage/drivers/driver.go @@ -8,13 +8,12 @@ import ( "path/filepath" "strings" - "github.com/sirupsen/logrus" - "github.com/vbatts/tar-split/tar/storage" - "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/idtools" digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" + "github.com/vbatts/tar-split/tar/storage" ) // FsMagic unsigned id of the filesystem in use. @@ -39,7 +38,7 @@ var ( ErrLayerUnknown = errors.New("unknown layer") ) -//CreateOpts contains optional arguments for Create() and CreateReadWrite() +// CreateOpts contains optional arguments for Create() and CreateReadWrite() // methods. type CreateOpts struct { MountLabel string @@ -53,8 +52,8 @@ type MountOpts struct { // Mount label is the MAC Labels to assign to mount point (SELINUX) MountLabel string // UidMaps & GidMaps are the User Namespace mappings to be assigned to content in the mount point - UidMaps []idtools.IDMap // nolint: golint - GidMaps []idtools.IDMap // nolint: golint + UidMaps []idtools.IDMap //nolint: golint,revive + GidMaps []idtools.IDMap //nolint: golint Options []string // Volatile specifies whether the container storage can be optimized @@ -110,6 +109,9 @@ type ProtoDriver interface { // Exists returns whether a filesystem layer with the specified // ID exists on this driver. Exists(id string) bool + // Returns a list of layer ids that exist on this driver (does not include + // additional storage layers). Not supported by all backends. + ListLayers() ([]string, error) // Status returns a set of key-value pairs which give low // level diagnostic status about this driver. Status() [][2]string @@ -279,6 +281,14 @@ func init() { 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. func Register(name string, initFunc InitFunc) error { if _, exists := drivers[name]; exists { @@ -312,6 +322,7 @@ func getBuiltinDriver(name, home string, options Options) (Driver, error) { type Options struct { Root string RunRoot string + DriverPriority []string DriverOptions []string UIDMaps []idtools.IDMap GIDMaps []idtools.IDMap @@ -327,9 +338,18 @@ func New(name string, config Options) (Driver, error) { // Guess for prior driver driversMap := scanPriorDrivers(config.Root) - for _, name := range priority { - if name == "vfs" { - // don't use vfs even if there is state present. + + // use the supplied priority list unless it is empty + prioList := config.DriverPriority + if len(prioList) == 0 { + prioList = priority + } + + for _, name := range prioList { + if name == "vfs" && len(config.DriverPriority) == 0 { + // don't use vfs even if there is state present and vfs + // has not been explicitly added to the override driver + // priority list continue } if _, prior := driversMap[name]; prior { @@ -362,7 +382,7 @@ func New(name string, config Options) (Driver, error) { } // Check for priority drivers first - for _, name := range priority { + for _, name := range prioList { driver, err := getBuiltinDriver(name, config.Root, config) if err != nil { if isDriverNotSupported(err) { @@ -405,3 +425,21 @@ func scanPriorDrivers(root string) map[string]bool { } 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()) + } + } +} diff --git a/vendor/github.com/containers/storage/drivers/driver_solaris.go b/vendor/github.com/containers/storage/drivers/driver_solaris.go index 174fa967..e3293091 100644 --- a/vendor/github.com/containers/storage/drivers/driver_solaris.go +++ b/vendor/github.com/containers/storage/drivers/driver_solaris.go @@ -1,3 +1,4 @@ +//go:build solaris && cgo // +build solaris,cgo package graphdriver diff --git a/vendor/github.com/containers/storage/drivers/driver_unsupported.go b/vendor/github.com/containers/storage/drivers/driver_unsupported.go index 3932c3ea..8119d9a6 100644 --- a/vendor/github.com/containers/storage/drivers/driver_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/driver_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd && !solaris && !darwin // +build !linux,!windows,!freebsd,!solaris,!darwin package graphdriver diff --git a/vendor/github.com/containers/storage/drivers/fsdiff.go b/vendor/github.com/containers/storage/drivers/fsdiff.go index 5022037d..6b2496ec 100644 --- a/vendor/github.com/containers/storage/drivers/fsdiff.go +++ b/vendor/github.com/containers/storage/drivers/fsdiff.go @@ -65,7 +65,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare defer func() { 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 { err := archive.Close() - driver.Put(id) + driverPut(driver, id, &err) return err }), nil } @@ -90,7 +90,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare if err != nil { return nil, err } - defer driver.Put(parent) + defer driverPut(driver, parent, &err) changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings) if err != nil { @@ -104,7 +104,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() - driver.Put(id) + driverPut(driver, id, &err) // NaiveDiffDriver compares file metadata with parent layers. Parent layers // 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 // 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 if idMappings == nil { @@ -134,7 +134,7 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p if err != nil { return nil, err } - defer driver.Put(id) + defer driverPut(driver, id, &retErr) parentFs := "" @@ -147,7 +147,7 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p if err != nil { return nil, err } - defer driver.Put(parent) + defer driverPut(driver, parent, &retErr) } return archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings) @@ -171,10 +171,10 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, options ApplyDiffOpts) if err != nil { return } - defer driver.Put(id) + defer driverPut(driver, id, &err) defaultForceMask := os.FileMode(0700) - var forceMask *os.FileMode = nil + var forceMask *os.FileMode // = nil if runtime.GOOS == "darwin" { forceMask = &defaultForceMask } @@ -224,7 +224,7 @@ func (gdw *NaiveDiffDriver) DiffSize(id string, idMappings *idtools.IDMappings, if err != nil { return } - defer driver.Put(id) + defer driverPut(driver, id, &err) return archive.ChangesSize(layerFs, changes), nil } diff --git a/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go b/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go index a7924ff3..0b7c868a 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go +++ b/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go @@ -6,6 +6,7 @@ package overlay import ( "fmt" "os" + "runtime" "syscall" "unsafe" @@ -112,7 +113,14 @@ func createIDMappedMount(source, target string, pid int) error { // createUsernsProcess forks the current process and creates a user namespace using the specified // mappings. It returns the pid of the new process. func createUsernsProcess(uidMaps []idtools.IDMap, gidMaps []idtools.IDMap) (int, func(), error) { - pid, _, err := syscall.Syscall6(uintptr(unix.SYS_CLONE), unix.CLONE_NEWUSER|uintptr(unix.SIGCHLD), 0, 0, 0, 0, 0) + var pid uintptr + var err syscall.Errno + + if runtime.GOARCH == "s390x" { + pid, _, err = syscall.Syscall6(uintptr(unix.SYS_CLONE), 0, unix.CLONE_NEWUSER|uintptr(unix.SIGCHLD), 0, 0, 0, 0) + } else { + pid, _, err = syscall.Syscall6(uintptr(unix.SYS_CLONE), unix.CLONE_NEWUSER|uintptr(unix.SIGCHLD), 0, 0, 0, 0, 0) + } if err != 0 { return -1, nil, err } diff --git a/vendor/github.com/containers/storage/drivers/overlay/mount.go b/vendor/github.com/containers/storage/drivers/overlay/mount.go index cf37f800..de47951d 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/mount.go +++ b/vendor/github.com/containers/storage/drivers/overlay/mount.go @@ -8,14 +8,16 @@ import ( "flag" "fmt" "os" + "path/filepath" "runtime" + "strings" "github.com/containers/storage/pkg/reexec" "golang.org/x/sys/unix" ) func init() { - reexec.Register("storage-mountfrom", mountFromMain) + reexec.Register("storage-mountfrom", mountOverlayFromMain) } func fatal(err error) { @@ -31,7 +33,7 @@ type mountOptions struct { Flag uint32 } -func mountFrom(dir, device, target, mType string, flags uintptr, label string) error { +func mountOverlayFrom(dir, device, target, mType string, flags uintptr, label string) error { options := &mountOptions{ Device: device, Target: target, @@ -67,7 +69,7 @@ func mountFrom(dir, device, target, mType string, flags uintptr, label string) e } // mountfromMain is the entry-point for storage-mountfrom on re-exec. -func mountFromMain() { +func mountOverlayFromMain() { runtime.LockOSThread() flag.Parse() @@ -77,11 +79,96 @@ func mountFromMain() { fatal(err) } - if err := os.Chdir(flag.Arg(0)); err != nil { + // Mount the arguments passed from the specified directory. Some of the + // paths mentioned in the values we pass to the kernel are relative to + // the specified directory. + homedir := flag.Arg(0) + if err := os.Chdir(homedir); err != nil { fatal(err) } - if err := unix.Mount(options.Device, options.Target, options.Type, uintptr(options.Flag), options.Label); err != nil { + pageSize := unix.Getpagesize() + if len(options.Label) < pageSize { + if err := unix.Mount(options.Device, options.Target, options.Type, uintptr(options.Flag), options.Label); err != nil { + fatal(err) + } + os.Exit(0) + } + + // Those arguments still took up too much space. Open the diff + // directories and use their descriptor numbers as lowers, using + // /proc/self/fd as the current directory. + + // Split out the various options, since we need to manipulate the + // paths, but we don't want to mess with other options. + var upperk, upperv, workk, workv, lowerk, lowerv, labelk, labelv, others string + for _, arg := range strings.Split(options.Label, ",") { + kv := strings.SplitN(arg, "=", 2) + switch kv[0] { + case "upperdir": + upperk = "upperdir=" + upperv = kv[1] + case "workdir": + workk = "workdir=" + workv = kv[1] + case "lowerdir": + lowerk = "lowerdir=" + lowerv = kv[1] + case "label": + labelk = "label=" + labelv = kv[1] + default: + if others == "" { + others = arg + } else { + others = others + "," + arg + } + } + } + + // Make sure upperdir, workdir, and the target are absolute paths. + if upperv != "" && !filepath.IsAbs(upperv) { + upperv = filepath.Join(homedir, upperv) + } + if workv != "" && !filepath.IsAbs(workv) { + workv = filepath.Join(homedir, workv) + } + if !filepath.IsAbs(options.Target) { + options.Target = filepath.Join(homedir, options.Target) + } + + // Get a descriptor for each lower, and use that descriptor's name as + // the new value for the list of lowers, because it's shorter. + if lowerv != "" { + lowers := strings.Split(lowerv, ":") + for i := range lowers { + lowerFd, err := unix.Open(lowers[i], unix.O_RDONLY, 0) + if err != nil { + fatal(err) + } + lowers[i] = fmt.Sprintf("%d", lowerFd) + } + lowerv = strings.Join(lowers, ":") + } + + // Reconstruct the Label field. + options.Label = upperk + upperv + "," + workk + workv + "," + lowerk + lowerv + "," + labelk + labelv + "," + others + options.Label = strings.ReplaceAll(options.Label, ",,", ",") + + // Okay, try this, if we managed to make the arguments fit. + var err error + if len(options.Label) < pageSize { + if err := os.Chdir("/proc/self/fd"); err != nil { + fatal(err) + } + err = unix.Mount(options.Device, options.Target, options.Type, uintptr(options.Flag), options.Label) + } else { + err = fmt.Errorf("cannot mount layer, mount data %q too large %d >= page size %d", options.Label, len(options.Label), pageSize) + } + + // Clean up. + if err != nil { + fmt.Fprintf(os.Stderr, "creating overlay mount to %s, mount_data=%q\n", options.Target, options.Label) fatal(err) } diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 844d2c79..e33bf16d 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -17,6 +17,7 @@ import ( "strings" "sync" "syscall" + "unicode" graphdriver "github.com/containers/storage/drivers" "github.com/containers/storage/drivers/overlayutils" @@ -73,21 +74,23 @@ const ( // or root directory. Mounts are always done relative to root and // referencing the symbolic links in order to ensure the number of // lower directories can fit in a single page for making the mount -// syscall. A hard upper limit of 128 lower layers is enforced to ensure +// syscall. A hard upper limit of 500 lower layers is enforced to ensure // that mounts do not fail due to length. const ( linkDir = "l" lowerFile = "lower" - maxDepth = 128 + maxDepth = 500 // idLength represents the number of random characters // which can be used to create the unique link identifier // for every layer. If this value is too long then the // page size limit for the mount command may be exceeded. // The idLength should be selected such that following equation - // is true (512 is a buffer for label metadata). - // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) + // is true (512 is a buffer for label metadata, 128 is the + // number of lowers we want to be able to use without having + // to use bind mounts to get all the way to the kernel limit). + // ((idLength + len(linkDir) + 1) * 128) <= (pageSize - 512) idLength = 26 ) @@ -140,8 +143,8 @@ var ( ) func init() { - graphdriver.Register("overlay", Init) - graphdriver.Register("overlay2", Init) + graphdriver.MustRegister("overlay", Init) + graphdriver.MustRegister("overlay2", Init) } func hasMetacopyOption(opts []string) bool { @@ -309,9 +312,11 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) if err != nil { return nil, err } - if fsName, ok := graphdriver.FsNames[fsMagic]; ok { - backingFs = fsName + fsName, ok := graphdriver.FsNames[fsMagic] + 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)) rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) @@ -352,9 +357,9 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) if opts.forceMask != nil { return nil, errors.New("'force_mask' is supported only with 'mount_program'") } - // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs + // check if they are running over btrfs, aufs, overlay, or ecryptfs switch fsMagic { - case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: + case graphdriver.FsMagicAufs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: return nil, fmt.Errorf("'overlay' is not supported over %s, a mount_program is required: %w", backingFs, graphdriver.ErrIncompatibleFS) } if unshare.IsRootless() && isNetworkFileSystem(fsMagic) { @@ -1197,6 +1202,9 @@ func (d *Driver) Remove(id string) error { if err := system.EnsureRemoveAll(dir); err != nil && !os.IsNotExist(err) { return err } + if d.quotaCtl != nil { + d.quotaCtl.ClearQuota(dir) + } return nil } @@ -1374,27 +1382,9 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return "", errors.New("max depth exceeded") } - // absLowers is the list of lowers as absolute paths, which works well with additional stores. + // absLowers is the list of lowers as absolute paths. absLowers := []string{} - // relLowers is the list of lowers as paths relative to the driver's home directory. - relLowers := []string{} - // Check if $link/../diff{1-*} exist. If they do, add them, in order, as the front of the lowers - // lists that we're building. "diff" itself is the upper, so it won't be in the lists. - link, err := os.ReadFile(path.Join(dir, "link")) - if err != nil { - if !os.IsNotExist(err) { - return "", err - } - logrus.Warnf("Can't read parent link %q because it does not exist. Going through storage to recreate the missing links.", path.Join(dir, "link")) - if err := d.recreateSymlinks(); err != nil { - return "", fmt.Errorf("recreating the links: %w", err) - } - link, err = os.ReadFile(path.Join(dir, "link")) - if err != nil { - return "", err - } - } diffN := 1 perms := defaultPerms if d.options.forceMask != nil { @@ -1408,7 +1398,6 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO } for err == nil { absLowers = append(absLowers, filepath.Join(dir, nameWithSuffix("diff", diffN))) - relLowers = append(relLowers, dumbJoin(linkDir, string(link), "..", nameWithSuffix("diff", diffN))) diffN++ st, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN))) if err == nil && !permsKnown { @@ -1433,6 +1422,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO perms = os.FileMode(st2.Mode()) permsKnown = true } + l = lower break } lower = "" @@ -1457,12 +1447,10 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO lower = newpath } absLowers = append(absLowers, lower) - relLowers = append(relLowers, l) diffN = 1 _, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) for err == nil { absLowers = append(absLowers, dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) - relLowers = append(relLowers, dumbJoin(l, "..", nameWithSuffix("diff", diffN))) diffN++ _, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) } @@ -1470,7 +1458,6 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO if len(absLowers) == 0 { absLowers = append(absLowers, path.Join(dir, "empty")) - relLowers = append(relLowers, path.Join(id, "empty")) } // user namespace requires this to move a directory from lower to upper. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) @@ -1604,28 +1591,23 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return nil } } else if len(mountData) >= pageSize { - // Use relative paths and mountFrom when the mount data has exceeded - // the page size. The mount syscall fails if the mount data cannot - // fit within a page and relative links make the mount data much - // smaller at the expense of requiring a fork exec to chroot. + // Use mountFrom when the mount data has exceeded the page size. The mount syscall fails if + // the mount data cannot fit within a page and relative links make the mount data much + // smaller at the expense of requiring a fork exec to chdir(). workdir = path.Join(id, "work") - //FIXME: We need to figure out to get this to work with additional stores if readWrite { diffDir := path.Join(id, "diff") - opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), diffDir, workdir) + opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workdir) } else { - opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(relLowers, ":")) + opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(absLowers, ":")) } if len(optsList) > 0 { opts = fmt.Sprintf("%s,%s", opts, strings.Join(optsList, ",")) } mountData = label.FormatMountLabel(opts, options.MountLabel) - if len(mountData) >= pageSize { - return "", fmt.Errorf("cannot mount layer, mount label %q too large %d >= page size %d", options.MountLabel, len(mountData), pageSize) - } mountFunc = func(source string, target string, mType string, flags uintptr, label string) error { - return mountFrom(d.home, source, target, mType, flags, label) + return mountOverlayFrom(d.home, source, target, mType, flags, label) } mountTarget = path.Join(id, "merged") } @@ -1719,6 +1701,40 @@ func (d *Driver) Exists(id string) bool { return err == nil } +func nameLooksLikeID(name string) bool { + if len(name) != 64 { + return false + } + for _, c := range name { + if !unicode.Is(unicode.ASCII_Hex_Digit, c) { + return false + } + } + return true +} + +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + entries, err := os.ReadDir(d.home) + if err != nil { + return nil, err + } + + layers := make([]string, 0) + + for _, entry := range entries { + id := entry.Name() + // Does it look like a datadir directory? + if !entry.IsDir() || !nameLooksLikeID(id) { + continue + } + + layers = append(layers, id) + } + + return layers, err +} + // isParent returns if the passed in parent is the direct parent of the passed in layer func (d *Driver) isParent(id, parent string) bool { lowers, err := d.getLowerDirs(id) diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay_cgo.go b/vendor/github.com/containers/storage/drivers/overlay/overlay_cgo.go index 0b70a5d9..88bfbf9c 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay_cgo.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay_cgo.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package overlay diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay_nocgo.go b/vendor/github.com/containers/storage/drivers/overlay/overlay_nocgo.go index 1cdac777..2a7a307a 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay_nocgo.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay_nocgo.go @@ -1,3 +1,4 @@ +//go:build linux && !cgo // +build linux,!cgo package overlay diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay_unsupported.go b/vendor/github.com/containers/storage/drivers/overlay/overlay_unsupported.go index 49af84a2..33b163a8 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package overlay diff --git a/vendor/github.com/containers/storage/drivers/quota/projectquota.go b/vendor/github.com/containers/storage/drivers/quota/projectquota.go index ed4c7eaa..f5484dee 100644 --- a/vendor/github.com/containers/storage/drivers/quota/projectquota.go +++ b/vendor/github.com/containers/storage/drivers/quota/projectquota.go @@ -51,6 +51,7 @@ struct fsxattr { */ import "C" import ( + "errors" "fmt" "math" "os" @@ -78,6 +79,7 @@ type Control struct { backingFsBlockDev string nextProjectID uint32 quotas map[string]uint32 + basePath string } // Attempt to generate a unigue projectid. Multiple directories @@ -158,20 +160,22 @@ func NewControl(basePath string) (*Control, error) { Size: 0, Inodes: 0, } - if err := setProjectQuota(backingFsBlockDev, minProjectID, quota); err != nil { - return nil, err - } q := Control{ backingFsBlockDev: backingFsBlockDev, nextProjectID: minProjectID + 1, quotas: make(map[string]uint32), + basePath: basePath, + } + + if err := q.setProjectQuota(minProjectID, quota); err != nil { + return nil, err } // // get first project id to be used for next container // - err = q.findNextProjectID(basePath) + err = q.findNextProjectID() if err != nil { return nil, err } @@ -204,11 +208,17 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error { // set the quota limit for the container's project id // logrus.Debugf("SetQuota path=%s, size=%d, inodes=%d, projectID=%d", targetPath, quota.Size, quota.Inodes, projectID) - return setProjectQuota(q.backingFsBlockDev, projectID, quota) + return q.setProjectQuota(projectID, quota) +} + +// ClearQuota removes the map entry in the quotas map for targetPath. +// It does so to prevent the map leaking entries as directories are deleted. +func (q *Control) ClearQuota(targetPath string) { + delete(q.quotas, targetPath) } // setProjectQuota - set the quota for project id on xfs block device -func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error { +func (q *Control) setProjectQuota(projectID uint32, quota Quota) error { var d C.fs_disk_quota_t d.d_version = C.FS_DQUOT_VERSION d.d_id = C.__u32(projectID) @@ -225,15 +235,35 @@ func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) er d.d_ino_softlimit = d.d_ino_hardlimit } - var cs = C.CString(backingFsBlockDev) + var cs = C.CString(q.backingFsBlockDev) defer C.free(unsafe.Pointer(cs)) - _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM, - uintptr(unsafe.Pointer(cs)), uintptr(d.d_id), - uintptr(unsafe.Pointer(&d)), 0, 0) - if errno != 0 { + runQuotactl := func() syscall.Errno { + _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM, + uintptr(unsafe.Pointer(cs)), uintptr(d.d_id), + uintptr(unsafe.Pointer(&d)), 0, 0) + return errno + } + + errno := runQuotactl() + + // If the backingFsBlockDev does not exist any more then try to recreate it. + if errors.Is(errno, unix.ENOENT) { + if _, err := makeBackingFsDev(q.basePath); err != nil { + return fmt.Errorf( + "failed to recreate missing backingFsBlockDev %s for projid %d: %w", + q.backingFsBlockDev, projectID, err, + ) + } + + if errno := runQuotactl(); errno != 0 { + return fmt.Errorf("failed to set quota limit for projid %d on %s after backingFsBlockDev recreation: %w", + projectID, q.backingFsBlockDev, errno) + } + + } else if errno != 0 { return fmt.Errorf("failed to set quota limit for projid %d on %s: %w", - projectID, backingFsBlockDev, errno) + projectID, q.backingFsBlockDev, errno) } return nil @@ -332,16 +362,16 @@ func setProjectID(targetPath string, projectID uint32) error { // findNextProjectID - find the next project id to be used for containers // by scanning driver home directory to find used project ids -func (q *Control) findNextProjectID(home string) error { - files, err := os.ReadDir(home) +func (q *Control) findNextProjectID() error { + files, err := os.ReadDir(q.basePath) if err != nil { - return fmt.Errorf("read directory failed : %s", home) + return fmt.Errorf("read directory failed : %s", q.basePath) } for _, file := range files { if !file.IsDir() { continue } - path := filepath.Join(home, file.Name()) + path := filepath.Join(q.basePath, file.Name()) projid, err := getProjectID(path) if err != nil { return err diff --git a/vendor/github.com/containers/storage/drivers/register/register_aufs.go b/vendor/github.com/containers/storage/drivers/register/register_aufs.go index 7743dced..bbb9cb65 100644 --- a/vendor/github.com/containers/storage/drivers/register/register_aufs.go +++ b/vendor/github.com/containers/storage/drivers/register/register_aufs.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_aufs && linux // +build !exclude_graphdriver_aufs,linux package register diff --git a/vendor/github.com/containers/storage/drivers/register/register_btrfs.go b/vendor/github.com/containers/storage/drivers/register/register_btrfs.go index 40ff1cdd..425ebd79 100644 --- a/vendor/github.com/containers/storage/drivers/register/register_btrfs.go +++ b/vendor/github.com/containers/storage/drivers/register/register_btrfs.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_btrfs && linux // +build !exclude_graphdriver_btrfs,linux package register diff --git a/vendor/github.com/containers/storage/drivers/register/register_devicemapper.go b/vendor/github.com/containers/storage/drivers/register/register_devicemapper.go index cefe2e8c..a744eaea 100644 --- a/vendor/github.com/containers/storage/drivers/register/register_devicemapper.go +++ b/vendor/github.com/containers/storage/drivers/register/register_devicemapper.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_devicemapper && linux && cgo // +build !exclude_graphdriver_devicemapper,linux,cgo package register diff --git a/vendor/github.com/containers/storage/drivers/register/register_overlay.go b/vendor/github.com/containers/storage/drivers/register/register_overlay.go index 30e3b4d7..95b77b73 100644 --- a/vendor/github.com/containers/storage/drivers/register/register_overlay.go +++ b/vendor/github.com/containers/storage/drivers/register/register_overlay.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_overlay && linux && cgo // +build !exclude_graphdriver_overlay,linux,cgo package register diff --git a/vendor/github.com/containers/storage/drivers/register/register_zfs.go b/vendor/github.com/containers/storage/drivers/register/register_zfs.go index 4623e7f4..8e5788a4 100644 --- a/vendor/github.com/containers/storage/drivers/register/register_zfs.go +++ b/vendor/github.com/containers/storage/drivers/register/register_zfs.go @@ -1,3 +1,4 @@ +//go:build (!exclude_graphdriver_zfs && linux) || (!exclude_graphdriver_zfs && freebsd) || solaris // +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd solaris package register diff --git a/vendor/github.com/containers/storage/drivers/template.go b/vendor/github.com/containers/storage/drivers/template.go index d40d71cf..7b96c082 100644 --- a/vendor/github.com/containers/storage/drivers/template.go +++ b/vendor/github.com/containers/storage/drivers/template.go @@ -1,9 +1,8 @@ package graphdriver import ( - "github.com/sirupsen/logrus" - "github.com/containers/storage/pkg/idtools" + "github.com/sirupsen/logrus" ) // TemplateDriver is just barely enough of a driver that we can implement a diff --git a/vendor/github.com/containers/storage/drivers/vfs/copy_unsupported.go b/vendor/github.com/containers/storage/drivers/vfs/copy_unsupported.go index 8ac80ee1..d94756bd 100644 --- a/vendor/github.com/containers/storage/drivers/vfs/copy_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/vfs/copy_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package vfs // import "github.com/containers/storage/drivers/vfs" diff --git a/vendor/github.com/containers/storage/drivers/vfs/driver.go b/vendor/github.com/containers/storage/drivers/vfs/driver.go index b1073d55..9deaa7c3 100644 --- a/vendor/github.com/containers/storage/drivers/vfs/driver.go +++ b/vendor/github.com/containers/storage/drivers/vfs/driver.go @@ -8,6 +8,7 @@ import ( "runtime" "strconv" "strings" + "unicode" graphdriver "github.com/containers/storage/drivers" "github.com/containers/storage/pkg/archive" @@ -28,7 +29,7 @@ var ( const defaultPerms = os.FileMode(0555) func init() { - graphdriver.Register("vfs", Init) + graphdriver.MustRegister("vfs", Init) } // Init returns a new VFS driver. @@ -98,7 +99,7 @@ func (d *Driver) Status() [][2]string { // 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) { - return nil, nil + return nil, nil //nolint: nilnil } // Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver. @@ -194,7 +195,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, ro bool if parent != "" { parentDir, err := d.Get(parent, graphdriver.MountOpts{}) if err != nil { - return fmt.Errorf("%s: %s", parent, err) + return fmt.Errorf("%s: %w", parent, err) } if err := dirCopy(parentDir, dir); err != nil { return err @@ -265,6 +266,40 @@ func (d *Driver) Exists(id string) bool { return err == nil } +func nameLooksLikeID(name string) bool { + if len(name) != 64 { + return false + } + for _, c := range name { + if !unicode.Is(unicode.ASCII_Hex_Digit, c) { + return false + } + } + return true +} + +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + entries, err := os.ReadDir(d.homes[0]) + if err != nil { + return nil, err + } + + layers := make([]string, 0) + + for _, entry := range entries { + id := entry.Name() + // Does it look like a datadir directory? + if !entry.IsDir() || !nameLooksLikeID(id) { + continue + } + + layers = append(layers, id) + } + + return layers, err +} + // AdditionalImageStores returns additional image stores supported by the driver func (d *Driver) AdditionalImageStores() []string { if len(d.homes) > 1 { diff --git a/vendor/github.com/containers/storage/drivers/windows/windows.go b/vendor/github.com/containers/storage/drivers/windows/windows.go index 7def16cd..66aa460c 100644 --- a/vendor/github.com/containers/storage/drivers/windows/windows.go +++ b/vendor/github.com/containers/storage/drivers/windows/windows.go @@ -53,7 +53,7 @@ var ( // init registers the windows graph drivers to the register. func init() { - graphdriver.Register("windowsfilter", InitFilter) + graphdriver.MustRegister("windowsfilter", InitFilter) // DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes // debugging issues in the re-exec codepath significantly easier. if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" { @@ -185,6 +185,11 @@ func (d *Driver) Exists(id string) bool { return result } +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + return nil, graphdriver.ErrNotSupported +} + // CreateFromTemplate creates a layer with the same contents and parent as another layer. func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error { return graphdriver.NaiveCreateFromTemplate(d, id, template, templateIDMappings, parent, parentIDMappings, opts, readWrite) diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go index 0d400178..aeef6410 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go @@ -33,7 +33,7 @@ type zfsOptions struct { const defaultPerms = os.FileMode(0555) func init() { - graphdriver.Register("zfs", Init) + graphdriver.MustRegister("zfs", Init) } // Logger returns a zfs logger implementation. @@ -57,12 +57,12 @@ func Init(base string, opt graphdriver.Options) (graphdriver.Driver, error) { return nil, fmt.Errorf("the 'zfs' command is not available: %w", graphdriver.ErrPrerequisites) } - file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 0600) + file, err := unix.Open("/dev/zfs", unix.O_RDWR, 0600) if err != nil { logger.Debugf("cannot open /dev/zfs: %v", err) return nil, fmt.Errorf("could not open /dev/zfs: %v: %w", err, graphdriver.ErrPrerequisites) } - defer file.Close() + defer unix.Close(file) options, err := parseOptions(opt.DriverOptions) if err != nil { @@ -506,6 +506,11 @@ func (d *Driver) Exists(id string) bool { return d.filesystemsCache[d.zfsPath(id)] } +// List layers (not including additional image stores) +func (d *Driver) ListLayers() ([]string, error) { + return nil, graphdriver.ErrNotSupported +} + // AdditionalImageStores returns additional image stores supported by the driver func (d *Driver) AdditionalImageStores() []string { return nil diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs_unsupported.go b/vendor/github.com/containers/storage/drivers/zfs/zfs_unsupported.go index 643b169b..738b0ae1 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs_unsupported.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs_unsupported.go @@ -1,11 +1,4 @@ +//go:build !linux && !freebsd // +build !linux,!freebsd package zfs - -func checkRootdirFs(rootdir string) error { - return nil -} - -func getMountpoint(id string) string { - return id -} diff --git a/vendor/github.com/containers/storage/idset.go b/vendor/github.com/containers/storage/idset.go index 9567fe90..be9e45cf 100644 --- a/vendor/github.com/containers/storage/idset.go +++ b/vendor/github.com/containers/storage/idset.go @@ -1,11 +1,11 @@ package storage import ( - "errors" "fmt" "strings" "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/types" "github.com/google/go-intervals/intervalset" ) @@ -116,7 +116,7 @@ func (s *idSet) findAvailable(n int) (*idSet, error) { n -= i.length() } if n > 0 { - return nil, errors.New("could not find enough available IDs") + return nil, types.ErrNoAvailableIDs } return &idSet{set: intervalset.NewImmutableSet(intervals)}, nil } diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go index c76a6c9f..577b6f8e 100644 --- a/vendor/github.com/containers/storage/images.go +++ b/vendor/github.com/containers/storage/images.go @@ -1,7 +1,6 @@ package storage import ( - "errors" "fmt" "os" "path/filepath" @@ -10,6 +9,7 @@ import ( "time" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringutils" "github.com/containers/storage/pkg/truncindex" @@ -94,11 +94,17 @@ type Image struct { Flags map[string]interface{} `json:"flags,omitempty"` } -// ROImageStore provides bookkeeping for information about Images. -type ROImageStore interface { - ROFileBasedStore - ROMetadataStore - ROBigDataStore +// roImageStore provides bookkeeping for information about Images. +type roImageStore interface { + roMetadataStore + roBigDataStore + + // startReading makes sure the store is fresh, and locks it for reading. + // If this succeeds, the caller MUST call stopReading(). + startReading() error + + // stopReading releases locks obtained by startReading. + stopReading() // Exists checks if there is an image with the given ID or name. Exists(id string) bool @@ -106,10 +112,6 @@ type ROImageStore interface { // Get retrieves information about an image given an ID or name. 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() ([]Image, error) @@ -120,51 +122,55 @@ type ROImageStore interface { ByDigest(d digest.Digest) ([]*Image, error) } -// ImageStore provides bookkeeping for information about Images. -type ImageStore interface { - ROImageStore - RWFileBasedStore - RWMetadataStore - RWImageBigDataStore - FlaggableStore +// rwImageStore provides bookkeeping for information about Images. +type rwImageStore interface { + roImageStore + rwMetadataStore + rwImageBigDataStore + flaggableStore + + // startWriting makes sure the store is fresh, and locks it for writing. + // If this succeeds, the caller MUST call stopWriting(). + startWriting() error + + // stopWriting releases locks obtained by startWriting. + stopWriting() // Create creates an image that has a specified ID (or a random one) and // optional names, using the specified layer as its topmost (hopefully // read-only) layer. That layer can be referenced by multiple images. Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error) - // SetNames replaces the list of names associated with an image with the - // supplied values. The values are expected to be valid normalized - // named image references. - // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. - SetNames(id string, names []string) error - - // AddNames adds the supplied values to the list of names associated with the image with - // the specified id. The values are expected to be valid normalized - // named image references. - AddNames(id string, names []string) error - - // RemoveNames removes the supplied values from the list of names associated with the image with - // the specified id. The values are expected to be valid normalized + // updateNames modifies names associated with an image based on (op, names). + // The values are expected to be valid normalized // named image references. - RemoveNames(id string, names []string) error + updateNames(id string, names []string, op updateNameOperation) error // Delete removes the record of the image. Delete(id string) error + addMappedTopLayer(id, layer string) error + removeMappedTopLayer(id, layer string) error + // Wipe removes records of all images. Wipe() error } type imageStore struct { - lockfile Locker + // The following fields are only set when constructing imageStore, and must never be modified afterwards. + // They are safe to access without any other locking. + lockfile *lockfile.LockFile // lockfile.IsReadWrite can be used to distinguish between read-write and read-only image stores. dir string - images []*Image - idindex *truncindex.TruncIndex - byid map[string]*Image - byname map[string]*Image - bydigest map[digest.Digest][]*Image - loadMut sync.Mutex + + inProcessLock sync.RWMutex // Can _only_ be obtained with lockfile held. + // The following fields can only be read/written with read/write ownership of inProcessLock, respectively. + // Almost all users should use startReading() or startWriting(). + lastWrite lockfile.LastWrite + images []*Image + idindex *truncindex.TruncIndex + byid map[string]*Image + byname map[string]*Image + bydigest map[digest.Digest][]*Image } func copyImage(i *Image) *Image { @@ -197,6 +203,191 @@ func copyImageSlice(slice []*Image) []*Image { return nil } +// startWritingWithReload makes sure the store is fresh if canReload, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +// +// This is an internal implementation detail of imageStore construction, every other caller +// should use startReading() instead. +func (r *imageStore) startWritingWithReload(canReload bool) error { + r.lockfile.Lock() + r.inProcessLock.Lock() + succeeded := false + defer func() { + if !succeeded { + r.inProcessLock.Unlock() + r.lockfile.Unlock() + } + }() + + if canReload { + if _, err := r.reloadIfChanged(true); err != nil { + return err + } + } + + succeeded = true + return nil +} + +// startWriting makes sure the store is fresh, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +func (r *imageStore) startWriting() error { + return r.startWritingWithReload(true) +} + +// stopWriting releases locks obtained by startWriting. +func (r *imageStore) stopWriting() { + r.inProcessLock.Unlock() + r.lockfile.Unlock() +} + +// startReadingWithReload makes sure the store is fresh if canReload, and locks it for reading. +// If this succeeds, the caller MUST call stopReading(). +// +// This is an internal implementation detail of imageStore construction, every other caller +// should use startReading() instead. +func (r *imageStore) startReadingWithReload(canReload bool) error { + // inProcessLocked calls the nested function with r.inProcessLock held for writing. + inProcessLocked := func(fn func() error) error { + r.inProcessLock.Lock() + defer r.inProcessLock.Unlock() + return fn() + } + + r.lockfile.RLock() + unlockFn := r.lockfile.Unlock // A function to call to clean up, or nil + defer func() { + if unlockFn != nil { + unlockFn() + } + }() + r.inProcessLock.RLock() + unlockFn = r.stopReading + + if canReload { + // If we are lucky, we can just hold the read locks, check that we are fresh, and continue. + _, modified, err := r.modified() + if err != nil { + return err + } + if modified { + // We are unlucky, and need to reload. + // NOTE: Multiple goroutines can get to this place approximately simultaneously. + r.inProcessLock.RUnlock() + unlockFn = r.lockfile.Unlock + + // r.lastWrite can change at this point if another goroutine reloads the store before us. That’s why we don’t unconditionally + // trigger a load below; we (lock and) reloadIfChanged() again. + + // First try reloading with r.lockfile held for reading. + // r.inProcessLock will serialize all goroutines that got here; + // each will re-check on-disk state vs. r.lastWrite, and the first one will actually reload the data. + var tryLockedForWriting bool + if err := inProcessLocked(func() error { + // We could optimize this further: The r.lockfile.GetLastWrite() value shouldn’t change as long as we hold r.lockfile, + // so if r.lastWrite was already updated, we don’t need to actually read the on-filesystem lock. + var err error + tryLockedForWriting, err = r.reloadIfChanged(false) + return err + }); err != nil { + if !tryLockedForWriting { + return err + } + // Not good enough, we need r.lockfile held for writing. So, let’s do that. + unlockFn() + unlockFn = nil + + r.lockfile.Lock() + unlockFn = r.lockfile.Unlock + if err := inProcessLocked(func() error { + _, err := r.reloadIfChanged(true) + return err + }); err != nil { + return err + } + unlockFn() + unlockFn = nil + + r.lockfile.RLock() + unlockFn = r.lockfile.Unlock + // We need to check for a reload once more because the on-disk state could have been modified + // after we released the lock. + // If that, _again_, finds inconsistent state, just give up. + // We could, plausibly, retry a few times, but that inconsistent state (duplicate image names) + // shouldn’t be saved (by correct implementations) in the first place. + if err := inProcessLocked(func() error { + _, err := r.reloadIfChanged(false) + return err + }); err != nil { + return fmt.Errorf("(even after successfully cleaning up once:) %w", err) + } + } + + // NOTE that we hold neither a read nor write inProcessLock at this point. That’s fine in ordinary operation, because + // the on-filesystem r.lockfile should protect us against (cooperating) writers, and any use of r.inProcessLock + // protects us against in-process writers modifying data. + // In presence of non-cooperating writers, we just ensure that 1) the in-memory data is not clearly out-of-date + // and 2) access to the in-memory data is not racy; + // but we can’t protect against those out-of-process writers modifying _files_ while we are assuming they are in a consistent state. + + r.inProcessLock.RLock() + } + } + + unlockFn = nil + return nil +} + +// startReading makes sure the store is fresh, and locks it for reading. +// If this succeeds, the caller MUST call stopReading(). +func (r *imageStore) startReading() error { + return r.startReadingWithReload(true) +} + +// stopReading releases locks obtained by startReading. +func (r *imageStore) stopReading() { + r.inProcessLock.RUnlock() + r.lockfile.Unlock() +} + +// modified returns true if the on-disk state has changed (i.e. if reloadIfChanged may need to modify the store), +// and a lockfile.LastWrite value for that update. +// +// The caller must hold r.lockfile for reading _or_ writing. +// The caller must hold r.inProcessLock for reading or writing. +func (r *imageStore) modified() (lockfile.LastWrite, bool, error) { + return r.lockfile.ModifiedSince(r.lastWrite) +} + +// reloadIfChanged reloads the contents of the store from disk if it is changed. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// reloadIfChanged() with lockedForWriting could succeed. +func (r *imageStore) reloadIfChanged(lockedForWriting bool) (bool, error) { + lastWrite, modified, err := r.modified() + if err != nil { + return false, err + } + // We require callers to always hold r.inProcessLock for WRITING, even if they might not end up calling r.load() + // and modify no fields, to ensure they see fresh data: + // r.lockfile.Modified() only returns true once per change. Without an exclusive lock, + // one goroutine might see r.lockfile.Modified() == true and decide to load, and in the meanwhile another one could + // see r.lockfile.Modified() == false and proceed to use in-memory data without noticing it is stale. + if modified { + if tryLockedForWriting, err := r.load(lockedForWriting); err != nil { + return tryLockedForWriting, err // r.lastWrite is unchanged, so we will load the next time again. + } + r.lastWrite = lastWrite + } + return false, nil +} + +// Requires startReading or startWriting. func (r *imageStore) Images() ([]Image, error) { images := make([]Image, len(r.images)) for i := range r.images { @@ -227,6 +418,7 @@ func bigDataNameIsManifest(name string) bool { // recomputeDigests takes a fixed digest and a name-to-digest map and builds a // list of the unique values that would identify the image. +// The caller must hold r.inProcessLock for writing. func (i *Image) recomputeDigests() error { validDigests := make([]digest.Digest, 0, len(i.BigDataDigests)+1) digests := make(map[digest.Digest]struct{}) @@ -257,65 +449,85 @@ func (i *Image) recomputeDigests() error { return nil } -func (r *imageStore) Load() error { - shouldSave := false +// load reloads the contents of the store from disk. +// +// Most callers should call reloadIfChanged() instead, to avoid overhead and to correctly +// manage r.lastWrite. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// retrying with lockedForWriting could succeed. +func (r *imageStore) load(lockedForWriting bool) (bool, error) { rpath := r.imagespath() data, err := os.ReadFile(rpath) if err != nil && !os.IsNotExist(err) { - return err + return false, err } + images := []*Image{} - idlist := []string{} + if len(data) != 0 { + if err := json.Unmarshal(data, &images); err != nil { + return false, fmt.Errorf("loading %q: %w", rpath, err) + } + } + idlist := make([]string, 0, len(images)) ids := make(map[string]*Image) names := make(map[string]*Image) digests := make(map[digest.Digest][]*Image) - if err = json.Unmarshal(data, &images); len(data) == 0 || err == nil { - idlist = make([]string, 0, len(images)) - for n, image := range images { - ids[image.ID] = images[n] - idlist = append(idlist, image.ID) - for _, name := range image.Names { - if conflict, ok := names[name]; ok { - r.removeName(conflict, name) - shouldSave = true - } + var errorToResolveBySaving error // == nil + for n, image := range images { + ids[image.ID] = images[n] + idlist = append(idlist, image.ID) + for _, name := range image.Names { + if conflict, ok := names[name]; ok { + r.removeName(conflict, name) + errorToResolveBySaving = ErrDuplicateImageNames } - // Compute the digest list. - err = image.recomputeDigests() - if err != nil { - return fmt.Errorf("computing digests for image with ID %q (%v): %w", image.ID, image.Names, err) - } - for _, name := range image.Names { - names[name] = image - } - for _, digest := range image.Digests { - list := digests[digest] - digests[digest] = append(list, image) - } - image.ReadOnly = !r.IsReadWrite() } + // Compute the digest list. + if err := image.recomputeDigests(); err != nil { + return false, fmt.Errorf("computing digests for image with ID %q (%v): %w", image.ID, image.Names, err) + } + for _, name := range image.Names { + names[name] = image + } + for _, digest := range image.Digests { + list := digests[digest] + digests[digest] = append(list, image) + } + image.ReadOnly = !r.lockfile.IsReadWrite() } - if shouldSave && (!r.IsReadWrite() || !r.Locked()) { - return ErrDuplicateImageNames + + if errorToResolveBySaving != nil { + if !r.lockfile.IsReadWrite() { + return false, errorToResolveBySaving + } + if !lockedForWriting { + return true, errorToResolveBySaving + } } 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.byname = names r.bydigest = digests - if shouldSave { - return r.Save() + if errorToResolveBySaving != nil { + return false, r.Save() } - return nil + return false, nil } +// Save saves the contents of the store to disk. +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for reading (but usually holds it for writing in order to make the desired changes). func (r *imageStore) Save() error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to modify the image store at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } - if !r.Locked() { - return errors.New("image store is not locked for writing") - } + r.lockfile.AssertLockedForWriting() rpath := r.imagespath() if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { return err @@ -324,55 +536,77 @@ func (r *imageStore) Save() error { if err != nil { return err } - defer r.Touch() - return ioutils.AtomicWriteFile(rpath, jdata, 0600) + if err := ioutils.AtomicWriteFile(rpath, jdata, 0600); err != nil { + return err + } + lw, err := r.lockfile.RecordWrite() + if err != nil { + return err + } + r.lastWrite = lw + return nil } -func newImageStore(dir string) (ImageStore, error) { +func newImageStore(dir string) (rwImageStore, error) { if err := os.MkdirAll(dir, 0700); err != nil { return nil, err } - lockfile, err := GetLockfile(filepath.Join(dir, "images.lock")) + lockfile, err := lockfile.GetLockFile(filepath.Join(dir, "images.lock")) if err != nil { return nil, err } - lockfile.Lock() - defer lockfile.Unlock() istore := imageStore{ lockfile: lockfile, dir: dir, + images: []*Image{}, byid: make(map[string]*Image), byname: make(map[string]*Image), bydigest: make(map[digest.Digest][]*Image), } - if err := istore.Load(); err != nil { + if err := istore.startWritingWithReload(false); err != nil { + return nil, err + } + defer istore.stopWriting() + istore.lastWrite, err = istore.lockfile.GetLastWrite() + if err != nil { + return nil, err + } + if _, err := istore.load(true); err != nil { return nil, err } return &istore, nil } -func newROImageStore(dir string) (ROImageStore, error) { - lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock")) +func newROImageStore(dir string) (roImageStore, error) { + lockfile, err := lockfile.GetROLockFile(filepath.Join(dir, "images.lock")) if err != nil { return nil, err } - lockfile.RLock() - defer lockfile.Unlock() istore := imageStore{ lockfile: lockfile, dir: dir, + images: []*Image{}, byid: make(map[string]*Image), byname: make(map[string]*Image), bydigest: make(map[digest.Digest][]*Image), } - if err := istore.Load(); err != nil { + if err := istore.startReadingWithReload(false); err != nil { + return nil, err + } + defer istore.stopReading() + istore.lastWrite, err = istore.lockfile.GetLastWrite() + if err != nil { + return nil, err + } + if _, err := istore.load(false); err != nil { return nil, err } return &istore, nil } +// Requires startReading or startWriting. func (r *imageStore) lookup(id string) (*Image, bool) { if image, ok := r.byid[id]; ok { return image, ok @@ -385,8 +619,9 @@ func (r *imageStore) lookup(id string) (*Image, bool) { return nil, false } +// Requires startWriting. func (r *imageStore) ClearFlag(id string, flag string) error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to clear flags on images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } image, ok := r.lookup(id) @@ -397,8 +632,9 @@ func (r *imageStore) ClearFlag(id string, flag string) error { return r.Save() } +// Requires startWriting. func (r *imageStore) SetFlag(id string, flag string, value interface{}) error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to set flags on images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } image, ok := r.lookup(id) @@ -412,8 +648,9 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error { return r.Save() } +// Requires startWriting. func (r *imageStore) Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (image *Image, err error) { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return nil, fmt.Errorf("not allowed to create new images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } if id == "" { @@ -455,7 +692,9 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c return nil, fmt.Errorf("validating digests for new image: %w", err) } 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 for _, name := range names { r.byname[name] = image @@ -469,6 +708,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c return image, err } +// Requires startWriting. func (r *imageStore) addMappedTopLayer(id, layer string) error { if image, ok := r.lookup(id); ok { image.MappedTopLayers = append(image.MappedTopLayers, layer) @@ -477,6 +717,7 @@ func (r *imageStore) addMappedTopLayer(id, layer string) error { return fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } +// Requires startWriting. func (r *imageStore) removeMappedTopLayer(id, layer string) error { if image, ok := r.lookup(id); ok { initialLen := len(image.MappedTopLayers) @@ -490,6 +731,7 @@ func (r *imageStore) removeMappedTopLayer(id, layer string) error { return fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } +// Requires startReading or startWriting. func (r *imageStore) Metadata(id string) (string, error) { if image, ok := r.lookup(id); ok { return image.Metadata, nil @@ -497,8 +739,9 @@ func (r *imageStore) Metadata(id string) (string, error) { return "", fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } +// Requires startWriting. func (r *imageStore) SetMetadata(id, metadata string) error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to modify image metadata at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } if image, ok := r.lookup(id); ok { @@ -508,29 +751,19 @@ func (r *imageStore) SetMetadata(id, metadata string) error { return fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } +// The caller must hold r.inProcessLock for writing. func (r *imageStore) removeName(image *Image, name string) { image.Names = stringSliceWithoutValue(image.Names, name) } +// The caller must hold r.inProcessLock for writing. func (i *Image) addNameToHistory(name string) { i.NamesHistory = dedupeNames(append([]string{name}, i.NamesHistory...)) } -// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. -func (r *imageStore) SetNames(id string, names []string) error { - return r.updateNames(id, names, setNames) -} - -func (r *imageStore) AddNames(id string, names []string) error { - return r.updateNames(id, names, addNames) -} - -func (r *imageStore) RemoveNames(id string, names []string) error { - return r.updateNames(id, names, removeNames) -} - +// Requires startWriting. func (r *imageStore) updateNames(id string, names []string, op updateNameOperation) error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to change image name assignments at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } image, ok := r.lookup(id) @@ -556,8 +789,9 @@ func (r *imageStore) updateNames(id string, names []string, op updateNameOperati return r.Save() } +// Requires startWriting. func (r *imageStore) Delete(id string) error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to delete images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } image, ok := r.lookup(id) @@ -572,7 +806,9 @@ func (r *imageStore) Delete(id string) error { } } 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 { delete(r.byname, name) } @@ -601,6 +837,7 @@ func (r *imageStore) Delete(id string) error { return nil } +// Requires startReading or startWriting. func (r *imageStore) Get(id string) (*Image, error) { if image, ok := r.lookup(id); ok { return copyImage(image), nil @@ -608,18 +845,13 @@ func (r *imageStore) Get(id string) (*Image, error) { 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) -} - +// Requires startReading or startWriting. func (r *imageStore) Exists(id string) bool { _, ok := r.lookup(id) return ok } +// Requires startReading or startWriting. func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) { if images, ok := r.bydigest[d]; ok { return copyImageSlice(images), nil @@ -627,6 +859,7 @@ func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) { return nil, fmt.Errorf("locating image with digest %q: %w", d, ErrImageUnknown) } +// Requires startReading or startWriting. func (r *imageStore) BigData(id, key string) ([]byte, error) { if key == "" { return nil, fmt.Errorf("can't retrieve image big data value for empty name: %w", ErrInvalidBigDataName) @@ -638,6 +871,7 @@ func (r *imageStore) BigData(id, key string) ([]byte, error) { return os.ReadFile(r.datapath(image.ID, key)) } +// Requires startReading or startWriting. func (r *imageStore) BigDataSize(id, key string) (int64, error) { if key == "" { return -1, fmt.Errorf("can't retrieve size of image big data with empty name: %w", ErrInvalidBigDataName) @@ -646,10 +880,7 @@ func (r *imageStore) BigDataSize(id, key string) (int64, error) { if !ok { return -1, fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } - if image.BigDataSizes == nil { - image.BigDataSizes = make(map[string]int64) - } - if size, ok := image.BigDataSizes[key]; ok { + if size, ok := image.BigDataSizes[key]; ok { // This is valid, and returns ok == false, for BigDataSizes == nil. return size, nil } if data, err := r.BigData(id, key); err == nil && data != nil { @@ -658,6 +889,7 @@ func (r *imageStore) BigDataSize(id, key string) (int64, error) { return -1, ErrSizeUnknown } +// Requires startReading or startWriting. func (r *imageStore) BigDataDigest(id, key string) (digest.Digest, error) { if key == "" { return "", fmt.Errorf("can't retrieve digest of image big data value with empty name: %w", ErrInvalidBigDataName) @@ -666,15 +898,13 @@ func (r *imageStore) BigDataDigest(id, key string) (digest.Digest, error) { if !ok { return "", fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } - if image.BigDataDigests == nil { - image.BigDataDigests = make(map[string]digest.Digest) - } - if d, ok := image.BigDataDigests[key]; ok { + if d, ok := image.BigDataDigests[key]; ok { // This is valid, and returns ok == false, for BigDataDigests == nil. return d, nil } return "", ErrDigestUnknown } +// Requires startReading or startWriting. func (r *imageStore) BigDataNames(id string) ([]string, error) { image, ok := r.lookup(id) if !ok { @@ -694,11 +924,12 @@ func imageSliceWithoutValue(slice []*Image, value *Image) []*Image { return modified } +// Requires startWriting. func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error { if key == "" { return fmt.Errorf("can't set empty name for image big data item: %w", ErrInvalidBigDataName) } - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to save data items associated with images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } image, ok := r.lookup(id) @@ -778,8 +1009,9 @@ func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func return err } +// Requires startWriting. func (r *imageStore) Wipe() error { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return fmt.Errorf("not allowed to delete images at %q: %w", r.imagespath(), ErrStoreIsReadOnly) } ids := make([]string, 0, len(r.byid)) @@ -793,50 +1025,3 @@ func (r *imageStore) Wipe() error { } return nil } - -func (r *imageStore) Lock() { - r.lockfile.Lock() -} - -func (r *imageStore) RecursiveLock() { - r.lockfile.RecursiveLock() -} - -func (r *imageStore) RLock() { - r.lockfile.RLock() -} - -func (r *imageStore) Unlock() { - r.lockfile.Unlock() -} - -func (r *imageStore) Touch() error { - return r.lockfile.Touch() -} - -func (r *imageStore) Modified() (bool, error) { - return r.lockfile.Modified() -} - -func (r *imageStore) IsReadWrite() bool { - return r.lockfile.IsReadWrite() -} - -func (r *imageStore) TouchedSince(when time.Time) bool { - return r.lockfile.TouchedSince(when) -} - -func (r *imageStore) Locked() bool { - return r.lockfile.Locked() -} - -func (r *imageStore) ReloadIfChanged() error { - r.loadMut.Lock() - defer r.loadMut.Unlock() - - modified, err := r.Modified() - if err == nil && modified { - return r.Load() - } - return err -} diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index c23f0b26..f14108be 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -18,6 +18,7 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/system" @@ -26,7 +27,7 @@ import ( multierror "github.com/hashicorp/go-multierror" "github.com/klauspost/pgzip" 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/vbatts/tar-split/archive/tar" "github.com/vbatts/tar-split/tar/asm" @@ -36,8 +37,28 @@ import ( const ( tarSplitSuffix = ".tar-split.gz" incompleteFlag = "incomplete" + // maxLayerStoreCleanupIterations is the number of times we try to clean up inconsistent layer store state + // in readers (which, for implementation reasons, gives other writers the opportunity to create more inconsistent state) + // until we just give up. + maxLayerStoreCleanupIterations = 3 ) +type layerLocations uint8 + +// The backing store is split in two json files, one (the volatile) +// that is written without fsync() meaning it isn't as robust to +// unclean shutdown +const ( + stableLayerLocation layerLocations = 1 << iota + volatileLayerLocation + + numLayerLocationIndex = iota +) + +func layerLocationFromIndex(index int) layerLocations { + return 1 << index +} + // A Layer is a record of a copy-on-write layer that's stored by the lower // level graph driver. type Layer struct { @@ -62,13 +83,26 @@ type Layer struct { MountLabel string `json:"mountlabel,omitempty"` // MountPoint is the path where the layer is mounted, or where it was most - // recently mounted. This can change between subsequent Unmount() and - // Mount() calls, so the caller should consult this value after Mount() - // succeeds to find the location of the container's root filesystem. + // recently mounted. + // + // WARNING: This field is a snapshot in time: (except for users inside c/storage that + // hold the mount lock) the true value can change between subsequent + // calls to c/storage API. + // + // Users that need to handle concurrent mount/unmount attempts should not access this + // field at all, and should only use the path returned by .Mount() (and that’s only + // assuming no other user will concurrently decide to unmount that mount point). MountPoint string `json:"-"` // MountCount is used as a reference count for the container's layer being // mounted at the mount point. + // + // WARNING: This field is a snapshot in time; (except for users inside c/storage that + // hold the mount lock) the true value can change between subsequent + // calls to c/storage API. + // + // In situations where concurrent mount/unmount attempts can happen, this field + // should not be used for any decisions, maybe apart from heuristic user warnings. MountCount int `json:"-"` // Created is the datestamp for when this layer was created. Older @@ -119,6 +153,9 @@ type Layer struct { // ReadOnly is true if this layer resides in a read-only layer store. ReadOnly bool `json:"-"` + // volatileStore is true if the container is from the volatile json file + volatileStore bool `json:"-"` + // BigDataNames is a list of names of data items that we keep for the // convenience of the caller. They can be large, and are only in // memory when being read from or written to disk. @@ -137,13 +174,19 @@ type DiffOptions struct { 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 // all known layers. -type ROLayerStore interface { - ROFileBasedStore - ROMetadataStore - ROLayerBigDataStore +type roLayerStore interface { + roMetadataStore + roLayerBigDataStore + + // startReading makes sure the store is fresh, and locks it for reading. + // If this succeeds, the caller MUST call stopReading(). + startReading() error + + // stopReading releases locks obtained by startReading. + stopReading() // Exists checks if a layer with the specified name or ID is known. Exists(id string) bool @@ -177,10 +220,6 @@ type ROLayerStore interface { // found, it returns an 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 // specified compressed digest value recorded for them. LayersByCompressedDigest(d digest.Digest) ([]Layer, error) @@ -193,15 +232,21 @@ type ROLayerStore interface { 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 // all known layers. -type LayerStore interface { - ROLayerStore - RWFileBasedStore - RWMetadataStore - FlaggableStore - RWLayerBigDataStore +type rwLayerStore interface { + roLayerStore + rwMetadataStore + flaggableStore + rwLayerBigDataStore + + // startWriting makes sure the store is fresh, and locks it for writing. + // If this succeeds, the caller MUST call stopWriting(). + startWriting() error + + // stopWriting releases locks obtained by startWriting. + stopWriting() // Create creates a new layer, optionally giving it a specified ID rather than // a randomly-generated one, either inheriting data from another specified @@ -218,18 +263,8 @@ type LayerStore interface { // Put combines the functions of CreateWithFlags and ApplyDiff. 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 replaces the list of names associated with a layer with the - // supplied values. - // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. - SetNames(id string, names []string) error - - // AddNames adds the supplied values to the list of names associated with the layer with the - // specified id. - AddNames(id string, names []string) error - - // RemoveNames remove the supplied values from the list of names associated with the layer with the - // specified id. - RemoveNames(id string, names []string) error + // updateNames modifies names associated with a layer based on (op, names). + updateNames(id string, names []string, op updateNameOperation) error // Delete deletes a layer with the specified name or ID. Delete(id string) error @@ -243,8 +278,15 @@ type LayerStore interface { // The mappings used by the container can be specified. Mount(id string, options drivers.MountOpts) (string, error) - // Unmount unmounts a layer when it is no longer in use. - Unmount(id string, force bool) (bool, error) + // unmount unmounts a layer when it is no longer in use. + // If conditional is set, it will fail with ErrLayerNotMounted if the layer is not mounted (without conditional, the caller is + // making a promise that the layer is actually mounted). + // If force is set, it will physically try to unmount it even if it is mounted multple times, or even if (!conditional and) + // there are no records of it being mounted in the first place. + // It returns whether the layer was still mounted at the time this function returned. + // WARNING: The return value may already be obsolete by the time it is available + // to the caller, so it can be used for heuristic sanity checks at best. It should almost always be ignored. + unmount(id string, force bool, conditional bool) (bool, error) // Mounted returns number of times the layer has been mounted. Mounted(id string) (int, error) @@ -270,33 +312,52 @@ type LayerStore interface { // DifferTarget gets the location where files are stored for the layer. 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 // store. // This API is experimental and can be changed without bumping the major version number. PutAdditionalLayer(id string, parentLayer *Layer, names []string, aLayer drivers.AdditionalLayer) (layer *Layer, err error) + + // Clean up unreferenced layers + GarbageCollect() error + + // supportsShifting() returns true if the driver.Driver.SupportsShifting(). + supportsShifting() bool } type layerStore struct { - lockfile Locker - mountsLockfile Locker - rundir string - driver drivers.Driver - layerdir string - layers []*Layer - idindex *truncindex.TruncIndex - byid map[string]*Layer - byname map[string]*Layer - bymount map[string]*Layer - bycompressedsum map[digest.Digest][]string - byuncompressedsum map[digest.Digest][]string - uidMap []idtools.IDMap - gidMap []idtools.IDMap - loadMut sync.Mutex - layerspathModified time.Time + // The following fields are only set when constructing layerStore, and must never be modified afterwards. + // They are safe to access without any other locking. + lockfile *lockfile.LockFile // lockfile.IsReadWrite can be used to distinguish between read-write and read-only layer stores. + mountsLockfile *lockfile.LockFile // Can _only_ be obtained with inProcessLock held. + rundir string + jsonPath [numLayerLocationIndex]string + layerdir string + + inProcessLock sync.RWMutex // Can _only_ be obtained with lockfile held. + // The following fields can only be read/written with read/write ownership of inProcessLock, respectively. + // Almost all users should use startReading() or startWriting(). + lastWrite lockfile.LastWrite + mountsLastWrite lockfile.LastWrite // Only valid if lockfile.IsReadWrite() + layers []*Layer + idindex *truncindex.TruncIndex + byid map[string]*Layer + byname map[string]*Layer + bymount map[string]*Layer + bycompressedsum map[digest.Digest][]string + byuncompressedsum map[digest.Digest][]string + layerspathsModified [numLayerLocationIndex]time.Time + + // FIXME: This field is only set when constructing layerStore, but locking rules of the driver + // interface itself are not documented here. + driver drivers.Driver +} + +// The caller must hold r.inProcessLock for reading. +func layerLocation(l *Layer) layerLocations { + if l.volatileStore { + return volatileLayerLocation + } + return stableLayerLocation } func copyLayer(l *Layer) *Layer { @@ -315,6 +376,7 @@ func copyLayer(l *Layer) *Layer { UncompressedSize: l.UncompressedSize, CompressionType: l.CompressionType, ReadOnly: l.ReadOnly, + volatileStore: l.volatileStore, BigDataNames: copyStringSlice(l.BigDataNames), Flags: copyStringInterfaceMap(l.Flags), UIDMap: copyIDMap(l.UIDMap), @@ -324,121 +386,478 @@ func copyLayer(l *Layer) *Layer { } } -func (r *layerStore) Layers() ([]Layer, error) { - layers := make([]Layer, len(r.layers)) - for i := range r.layers { - layers[i] = *copyLayer(r.layers[i]) +// startWritingWithReload makes sure the store is fresh if canReload, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +// +// This is an internal implementation detail of layerStore construction, every other caller +// should use startWriting() instead. +func (r *layerStore) startWritingWithReload(canReload bool) error { + r.lockfile.Lock() + r.inProcessLock.Lock() + succeeded := false + defer func() { + if !succeeded { + r.inProcessLock.Unlock() + r.lockfile.Unlock() + } + }() + + if canReload { + if _, err := r.reloadIfChanged(true); err != nil { + return err + } } - return layers, nil + + succeeded = true + return nil } -func (r *layerStore) mountspath() string { - return filepath.Join(r.rundir, "mountpoints.json") +// startWriting makes sure the store is fresh, and locks it for writing. +// If this succeeds, the caller MUST call stopWriting(). +func (r *layerStore) startWriting() error { + return r.startWritingWithReload(true) } -func (r *layerStore) layerspath() string { - return filepath.Join(r.layerdir, "layers.json") +// stopWriting releases locks obtained by startWriting. +func (r *layerStore) stopWriting() { + r.inProcessLock.Unlock() + r.lockfile.Unlock() } -func (r *layerStore) Load() error { - shouldSave := false - rpath := r.layerspath() - info, err := os.Stat(rpath) +// startReadingWithReload makes sure the store is fresh if canReload, and locks it for reading. +// If this succeeds, the caller MUST call stopReading(). +// +// This is an internal implementation detail of layerStore construction, every other caller +// should use startReading() instead. +func (r *layerStore) startReadingWithReload(canReload bool) error { + // inProcessLocked calls the nested function with r.inProcessLock held for writing. + inProcessLocked := func(fn func() error) error { + r.inProcessLock.Lock() + defer r.inProcessLock.Unlock() + return fn() + } + + r.lockfile.RLock() + unlockFn := r.lockfile.Unlock // A function to call to clean up, or nil + defer func() { + if unlockFn != nil { + unlockFn() + } + }() + r.inProcessLock.RLock() + unlockFn = r.stopReading + + if canReload { + // If we are lucky, we can just hold the read locks, check that we are fresh, and continue. + modified, err := r.modified() + if err != nil { + return err + } + if modified { + // We are unlucky, and need to reload. + // NOTE: Multiple goroutines can get to this place approximately simultaneously. + r.inProcessLock.RUnlock() + unlockFn = r.lockfile.Unlock + + cleanupsDone := 0 + for { + // First try reloading with r.lockfile held for reading. + // r.inProcessLock will serialize all goroutines that got here; + // each will re-check on-disk state vs. r.lastWrite, and the first one will actually reload the data. + var tryLockedForWriting bool + err := inProcessLocked(func() error { + var err error + tryLockedForWriting, err = r.reloadIfChanged(false) + return err + }) + if err == nil { + break + } + if !tryLockedForWriting { + return err + } + if cleanupsDone >= maxLayerStoreCleanupIterations { + return fmt.Errorf("(even after %d cleanup attempts:) %w", cleanupsDone, err) + } + // Not good enough, we need r.lockfile held for writing. So, let’s do that. + unlockFn() + unlockFn = nil + + r.lockfile.Lock() + unlockFn = r.lockfile.Unlock + if err := inProcessLocked(func() error { + _, err := r.reloadIfChanged(true) + return err + }); err != nil { + return err + } + unlockFn() + unlockFn = nil + + r.lockfile.RLock() + unlockFn = r.lockfile.Unlock + // We need to check for a reload again because the on-disk state could have been modified + // after we released the lock. + cleanupsDone++ + } + + // NOTE that we hold neither a read nor write inProcessLock at this point. That’s fine in ordinary operation, because + // the on-filesystem r.lockfile should protect us against (cooperating) writers, and any use of r.inProcessLock + // protects us against in-process writers modifying data. + // In presence of non-cooperating writers, we just ensure that 1) the in-memory data is not clearly out-of-date + // and 2) access to the in-memory data is not racy; + // but we can’t protect against those out-of-process writers modifying _files_ while we are assuming they are in a consistent state. + + r.inProcessLock.RLock() + } + } + + unlockFn = nil + return nil +} + +// startReading makes sure the store is fresh, and locks it for reading. +// If this succeeds, the caller MUST call stopReading(). +func (r *layerStore) startReading() error { + return r.startReadingWithReload(true) +} + +// stopReading releases locks obtained by startReading. +func (r *layerStore) stopReading() { + r.inProcessLock.RUnlock() + r.lockfile.Unlock() +} + +// modified returns true if the on-disk state (of layers or mounts) has changed (ie if reloadIcHanged may need to modify the store) +// +// Note that unlike containerStore.modified and imageStore.modified, this function is not directly used in layerStore.reloadIfChanged(); +// it exists only to help the reader ensure it has fresh enough state. +// +// The caller must hold r.lockfile for reading _or_ writing. +// The caller must hold r.inProcessLock for reading or writing. +func (r *layerStore) modified() (bool, error) { + _, m, err := r.layersModified() if err != nil { - if !os.IsNotExist(err) { + return false, err + } + if m { + return true, nil + } + if r.lockfile.IsReadWrite() { + // This means we get, release, and re-obtain, r.mountsLockfile if we actually need to do any kind of reload. + // That’s a bit expensive, but hopefully most callers will be read-only and see no changes. + // We can’t eliminate these mountsLockfile accesses given the current assumption that Layer objects have _some_ not-very-obsolete + // mount data. Maybe we can segregate the mount-dependent and mount-independent operations better... + r.mountsLockfile.RLock() + defer r.mountsLockfile.Unlock() + _, m, err := r.mountsModified() + if err != nil { + return false, err + } + if m { + return true, nil + } + } + return false, nil +} + +// layersModified() checks if the most recent writer to r.jsonPath[] was a party other than the +// last recorded writer. If so, it returns a lockfile.LastWrite value to record on a successful +// reload. +// It should only be called with the lock held. +// The caller must hold r.inProcessLock for reading. +func (r *layerStore) layersModified() (lockfile.LastWrite, bool, error) { + lastWrite, modified, err := r.lockfile.ModifiedSince(r.lastWrite) + if err != nil { + return lockfile.LastWrite{}, modified, err + } + if modified { + return lastWrite, true, nil + } + + // If the layers.json file or container-layers.json has been + // modified manually, then we have to reload the storage in + // any case. + for locationIndex := 0; locationIndex < numLayerLocationIndex; locationIndex++ { + info, err := os.Stat(r.jsonPath[locationIndex]) + if err != nil && !os.IsNotExist(err) { + return lockfile.LastWrite{}, false, fmt.Errorf("stat layers file: %w", err) + } + if info != nil && info.ModTime() != r.layerspathsModified[locationIndex] { + // In this case the LastWrite value is equal to r.lastWrite; writing it back doesn’t hurt. + return lastWrite, true, nil + } + } + + return lockfile.LastWrite{}, false, nil +} + +// reloadIfChanged reloads the contents of the store from disk if it is changed. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// reloadIfChanged() with lockedForWriting could succeed. +func (r *layerStore) reloadIfChanged(lockedForWriting bool) (bool, error) { + lastWrite, layersModified, err := r.layersModified() + if err != nil { + return false, err + } + if layersModified { + // r.load also reloads mounts data; so, on this path, we don’t need to call reloadMountsIfChanged. + if tryLockedForWriting, err := r.load(lockedForWriting); err != nil { + return tryLockedForWriting, err // r.lastWrite is unchanged, so we will load the next time again. + } + r.lastWrite = lastWrite + return false, nil + } + if r.lockfile.IsReadWrite() { + r.mountsLockfile.RLock() + defer r.mountsLockfile.Unlock() + if err := r.reloadMountsIfChanged(); err != nil { + return false, err + } + } + return false, nil +} + +// mountsModified returns true if the on-disk mount state has changed (i.e. if reloadMountsIfChanged may need to modify the store), +// and a lockfile.LastWrite value for that update. +// +// The caller must hold r.mountsLockfile for reading _or_ writing. +// The caller must hold r.inProcessLock for reading or writing. +func (r *layerStore) mountsModified() (lockfile.LastWrite, bool, error) { + return r.mountsLockfile.ModifiedSince(r.mountsLastWrite) +} + +// reloadMountsIfChanged reloads the contents of mountsPath from disk if it is changed. +// +// The caller must hold r.mountsLockFile for reading or writing. +func (r *layerStore) reloadMountsIfChanged() error { + lastWrite, modified, err := r.mountsModified() + if err != nil { + return err + } + if modified { + if err = r.loadMounts(); err != nil { return err } - } else { - r.layerspathModified = info.ModTime() + r.mountsLastWrite = lastWrite } - data, err := os.ReadFile(rpath) - if err != nil && !os.IsNotExist(err) { + return nil +} + +// Requires startReading or startWriting. +func (r *layerStore) Layers() ([]Layer, error) { + layers := make([]Layer, len(r.layers)) + for i := range r.layers { + layers[i] = *copyLayer(r.layers[i]) + } + return layers, nil +} + +// Requires startWriting. +func (r *layerStore) GarbageCollect() error { + layers, err := r.driver.ListLayers() + + if err != nil { + if errors.Is(err, drivers.ErrNotSupported) { + return nil + } return err } + + for _, id := range layers { + // Is the id still referenced + if r.byid[id] != nil { + continue + } + + // Remove layer and any related data of unreferenced id + if err := r.driver.Remove(id); err != nil { + return err + } + + os.Remove(r.tspath(id)) + os.RemoveAll(r.datadir(id)) + } + return nil +} + +func (r *layerStore) mountspath() string { + return filepath.Join(r.rundir, "mountpoints.json") +} + +// load reloads the contents of the store from disk. +// +// Most callers should call reloadIfChanged() instead, to avoid overhead and to correctly +// manage r.lastWrite. +// +// As a side effect, this sets r.mountsLastWrite. +// +// The caller must hold r.lockfile for reading _or_ writing; lockedForWriting is true +// if it is held for writing. +// The caller must hold r.inProcessLock for WRITING. +// +// If !lockedForWriting and this function fails, the return value indicates whether +// retrying with lockedForWriting could succeed. +func (r *layerStore) load(lockedForWriting bool) (bool, error) { + var modifiedLocations layerLocations + layers := []*Layer{} - idlist := []string{} ids := make(map[string]*Layer) - names := make(map[string]*Layer) - compressedsums := make(map[digest.Digest][]string) - uncompressedsums := make(map[digest.Digest][]string) - if r.IsReadWrite() { - label.ClearLabels() - } - if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil { - idlist = make([]string, 0, len(layers)) - for n, layer := range layers { - ids[layer.ID] = layers[n] - idlist = append(idlist, layer.ID) - for _, name := range layer.Names { - if conflict, ok := names[name]; ok { - r.removeName(conflict, name) - shouldSave = true - } - names[name] = layers[n] + + for locationIndex := 0; locationIndex < numLayerLocationIndex; locationIndex++ { + location := layerLocationFromIndex(locationIndex) + rpath := r.jsonPath[locationIndex] + info, err := os.Stat(rpath) + if err != nil { + if !os.IsNotExist(err) { + return false, err } - if layer.CompressedDigest != "" { - compressedsums[layer.CompressedDigest] = append(compressedsums[layer.CompressedDigest], layer.ID) + } else { + r.layerspathsModified[locationIndex] = info.ModTime() + } + data, err := os.ReadFile(rpath) + if err != nil && !os.IsNotExist(err) { + return false, err + } + + locationLayers := []*Layer{} + if len(data) != 0 { + if err := json.Unmarshal(data, &locationLayers); err != nil { + return false, fmt.Errorf("loading %q: %w", rpath, err) } - if layer.UncompressedDigest != "" { - uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID) + } + + for _, layer := range locationLayers { + // There should be no duplicated ids between json files, but lets check to be sure + if ids[layer.ID] != nil { + continue // skip invalid duplicated layer } - if layer.MountLabel != "" { - label.ReserveLabel(layer.MountLabel) + // Remember where the layer came from + if location == volatileLayerLocation { + layer.volatileStore = true } - layer.ReadOnly = !r.IsReadWrite() + layers = append(layers, layer) + ids[layer.ID] = layer + } + } + + idlist := make([]string, 0, len(layers)) + names := make(map[string]*Layer) + compressedsums := make(map[digest.Digest][]string) + uncompressedsums := make(map[digest.Digest][]string) + var errorToResolveBySaving error // == nil; if there are multiple errors, this is one of them. + if r.lockfile.IsReadWrite() { + selinux.ClearLabels() + } + for n, layer := range layers { + idlist = append(idlist, layer.ID) + for _, name := range layer.Names { + if conflict, ok := names[name]; ok { + r.removeName(conflict, name) + errorToResolveBySaving = ErrDuplicateLayerNames + modifiedLocations |= layerLocation(conflict) + } + names[name] = layers[n] + } + if layer.CompressedDigest != "" { + compressedsums[layer.CompressedDigest] = append(compressedsums[layer.CompressedDigest], layer.ID) + } + if layer.UncompressedDigest != "" { + uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID) + } + if layer.MountLabel != "" { + selinux.ReserveLabel(layer.MountLabel) + } + layer.ReadOnly = !r.lockfile.IsReadWrite() + // The r.lockfile.IsReadWrite() condition maintains past practice: + // Incomplete layers in a read-only store are not treated as a reason to refuse to use other layers from that store + // (OTOH creating child layers on top would probably lead to problems?). + // We do remove incomplete layers in read-write stores so that we don’t build on top of them. + if layerHasIncompleteFlag(layer) && r.lockfile.IsReadWrite() { + errorToResolveBySaving = errors.New("an incomplete layer exists and can't be cleaned up") } - err = nil } - if shouldSave && (!r.IsReadWrite() || !r.Locked()) { - return ErrDuplicateLayerNames + + if errorToResolveBySaving != nil { + if !r.lockfile.IsReadWrite() { + return false, errorToResolveBySaving + } + if !lockedForWriting { + return true, errorToResolveBySaving + } } 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.byname = names r.bycompressedsum = compressedsums r.byuncompressedsum = uncompressedsums // Load and merge information about which layers are mounted, and where. - if r.IsReadWrite() { + if r.lockfile.IsReadWrite() { r.mountsLockfile.RLock() defer r.mountsLockfile.Unlock() - if err = r.loadMounts(); err != nil { - return err + // We need to reload mounts unconditionally, becuause by creating r.layers from scratch, we have discarded the previous + // information, if any. So, obtain a fresh mountsLastWrite value so that we don’t unnecessarily reload the data + // afterwards. + mountsLastWrite, err := r.mountsLockfile.GetLastWrite() + if err != nil { + return false, err + } + if err := r.loadMounts(); err != nil { + return false, err } + r.mountsLastWrite = mountsLastWrite + // NOTE: We will release mountsLockfile when this function returns, so unlike most of the layer data, the + // r.layers[].MountPoint, r.layers[].MountCount, and r.bymount values might not reflect + // true on-filesystem state already by the time this function returns. + // Code that needs the state to be accurate must lock r.mountsLockfile again, + // and possibly loadMounts() again. + } - // Last step: as we’re writable, try to remove anything that a previous + if errorToResolveBySaving != nil { + if !r.lockfile.IsReadWrite() { + return false, fmt.Errorf("internal error: layerStore.load has shouldSave but !r.lockfile.IsReadWrite") + } + // Last step: try to remove anything that a previous // user of this storage area marked for deletion but didn't manage to // actually delete. - if r.Locked() { - for _, layer := range r.layers { - if layer.Flags == nil { - layer.Flags = make(map[string]interface{}) - } - if layerHasIncompleteFlag(layer) { - logrus.Warnf("Found incomplete layer %#v, deleting it", layer.ID) - err = r.deleteInternal(layer.ID) - if err != nil { - break - } - shouldSave = true + var incompleteDeletionErrors error // = nil + for _, layer := range r.layers { + if layer.Flags == nil { + layer.Flags = make(map[string]interface{}) + } + if layerHasIncompleteFlag(layer) { + logrus.Warnf("Found incomplete layer %#v, deleting it", layer.ID) + err := r.deleteInternal(layer.ID) + if err != nil { + // Don't return the error immediately, because deleteInternal does not saveLayers(); + // Even if deleting one incomplete layer fails, call saveLayers() so that other possible successfully + // deleted incomplete layers have their metadata correctly removed. + incompleteDeletionErrors = multierror.Append(incompleteDeletionErrors, + fmt.Errorf("deleting layer %#v: %w", layer.ID, err)) } + modifiedLocations |= layerLocation(layer) } } - if shouldSave { - return r.saveLayers() + if err := r.saveLayers(modifiedLocations); err != nil { + return false, err + } + if incompleteDeletionErrors != nil { + return false, incompleteDeletionErrors } } - - return err -} - -func (r *layerStore) LoadLocked() error { - r.lockfile.Lock() - defer r.lockfile.Unlock() - return r.Load() + return false, nil } +// The caller must hold r.mountsLockfile for reading or writing. +// The caller must hold r.inProcessLock for WRITING. func (r *layerStore) loadMounts() error { mounts := make(map[string]*Layer) mpath := r.mountspath() @@ -476,42 +895,77 @@ func (r *layerStore) loadMounts() error { return err } -func (r *layerStore) Save() error { +// save saves the contents of the store to disk. +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for WRITING. +func (r *layerStore) save(saveLocations layerLocations) error { r.mountsLockfile.Lock() defer r.mountsLockfile.Unlock() - defer r.mountsLockfile.Touch() - if err := r.saveLayers(); err != nil { + if err := r.saveLayers(saveLocations); err != nil { return err } return r.saveMounts() } -func (r *layerStore) saveLayers() error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to modify the layer store at %q: %w", r.layerspath(), ErrStoreIsReadOnly) - } - if !r.Locked() { - return errors.New("layer store is not locked for writing") +// saveFor saves the contents of the store relevant for modifiedLayer to disk. +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for WRITING. +func (r *layerStore) saveFor(modifiedLayer *Layer) error { + return r.save(layerLocation(modifiedLayer)) +} + +// The caller must hold r.lockfile locked for writing. +// The caller must hold r.inProcessLock for WRITING. +func (r *layerStore) saveLayers(saveLocations layerLocations) error { + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to modify the layer store at %q: %w", r.layerdir, ErrStoreIsReadOnly) } - rpath := r.layerspath() - if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { - return err + r.lockfile.AssertLockedForWriting() + + for locationIndex := 0; locationIndex < numLayerLocationIndex; locationIndex++ { + location := layerLocationFromIndex(locationIndex) + if location&saveLocations == 0 { + continue + } + rpath := r.jsonPath[locationIndex] + if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { + return err + } + subsetLayers := make([]*Layer, 0, len(r.layers)) + for _, layer := range r.layers { + if layerLocation(layer) == location { + subsetLayers = append(subsetLayers, layer) + } + } + + jldata, err := json.Marshal(&subsetLayers) + if err != nil { + return err + } + opts := ioutils.AtomicFileWriterOptions{} + if location == volatileLayerLocation { + opts.NoSync = true + } + if err := ioutils.AtomicWriteFileWithOpts(rpath, jldata, 0600, &opts); err != nil { + return err + } + r.layerspathsModified[locationIndex] = opts.ModTime } - jldata, err := json.Marshal(&r.layers) + lw, err := r.lockfile.RecordWrite() if err != nil { return err } - defer r.Touch() - return ioutils.AtomicWriteFile(rpath, jldata, 0600) + r.lastWrite = lw + return nil } +// The caller must hold r.mountsLockfile for writing. +// The caller must hold r.inProcessLock for WRITING. func (r *layerStore) saveMounts() error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to modify the layer store at %q: %w", r.layerspath(), ErrStoreIsReadOnly) - } - if !r.mountsLockfile.Locked() { - return errors.New("layer store mount information is not locked for writing") + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to modify the layer store at %q: %w", r.layerdir, ErrStoreIsReadOnly) } + r.mountsLockfile.AssertLockedForWriting() mpath := r.mountspath() if err := os.MkdirAll(filepath.Dir(mpath), 0700); err != nil { return err @@ -533,67 +987,107 @@ func (r *layerStore) saveMounts() error { if err = ioutils.AtomicWriteFile(mpath, jmdata, 0600); err != nil { return err } + lw, err := r.mountsLockfile.RecordWrite() + if err != nil { + return err + } + r.mountsLastWrite = lw 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, transient bool) (rwLayerStore, error) { if err := os.MkdirAll(rundir, 0700); err != nil { return nil, err } if err := os.MkdirAll(layerdir, 0700); err != nil { return nil, err } - lockfile, err := GetLockfile(filepath.Join(layerdir, "layers.lock")) + // Note: While the containers.lock file is in rundir for transient stores + // we don't want to do this here, because the non-transient layers in + // layers.json might be used externally as a read-only layer (using e.g. + // additionalimagestores), and that would look for the lockfile in the + // same directory + lockFile, err := lockfile.GetLockFile(filepath.Join(layerdir, "layers.lock")) if err != nil { return nil, err } - mountsLockfile, err := GetLockfile(filepath.Join(rundir, "mountpoints.lock")) + mountsLockfile, err := lockfile.GetLockFile(filepath.Join(rundir, "mountpoints.lock")) if err != nil { return nil, err } + volatileDir := layerdir + if transient { + volatileDir = rundir + } rlstore := layerStore{ - lockfile: lockfile, + lockfile: lockFile, mountsLockfile: mountsLockfile, - driver: driver, rundir: rundir, - layerdir: layerdir, - byid: make(map[string]*Layer), - bymount: make(map[string]*Layer), - byname: make(map[string]*Layer), - uidMap: copyIDMap(s.uidMap), - gidMap: copyIDMap(s.gidMap), - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.Load(); err != nil { + jsonPath: [numLayerLocationIndex]string{ + filepath.Join(layerdir, "layers.json"), + filepath.Join(volatileDir, "volatile-layers.json"), + }, + layerdir: layerdir, + + byid: make(map[string]*Layer), + byname: make(map[string]*Layer), + bymount: make(map[string]*Layer), + + driver: driver, + } + if err := rlstore.startWritingWithReload(false); err != nil { + return nil, err + } + defer rlstore.stopWriting() + lw, err := rlstore.lockfile.GetLastWrite() + if err != nil { + return nil, err + } + rlstore.lastWrite = lw + // rlstore.mountsLastWrite is initialized inside rlstore.load(). + if _, err := rlstore.load(true); err != nil { return nil, err } return &rlstore, nil } -func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (ROLayerStore, error) { - lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock")) +func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (roLayerStore, error) { + lockfile, err := lockfile.GetROLockFile(filepath.Join(layerdir, "layers.lock")) if err != nil { return nil, err } rlstore := layerStore{ lockfile: lockfile, mountsLockfile: nil, - driver: driver, rundir: rundir, - layerdir: layerdir, - byid: make(map[string]*Layer), - bymount: make(map[string]*Layer), - byname: make(map[string]*Layer), - } - rlstore.RLock() - defer rlstore.Unlock() - if err := rlstore.Load(); err != nil { + jsonPath: [numLayerLocationIndex]string{ + filepath.Join(layerdir, "layers.json"), + filepath.Join(layerdir, "volatile-layers.json"), + }, + layerdir: layerdir, + + byid: make(map[string]*Layer), + byname: make(map[string]*Layer), + bymount: make(map[string]*Layer), + + driver: driver, + } + if err := rlstore.startReadingWithReload(false); err != nil { + return nil, err + } + defer rlstore.stopReading() + lw, err := rlstore.lockfile.GetLastWrite() + if err != nil { + return nil, err + } + rlstore.lastWrite = lw + if _, err := rlstore.load(false); err != nil { return nil, err } return &rlstore, nil } +// Requires startReading or startWriting. func (r *layerStore) lookup(id string) (*Layer, bool) { if layer, ok := r.byid[id]; ok { return layer, ok @@ -606,6 +1100,7 @@ func (r *layerStore) lookup(id string) (*Layer, bool) { return nil, false } +// Requires startReading or startWriting. func (r *layerStore) Size(name string) (int64, error) { layer, ok := r.lookup(name) if !ok { @@ -620,21 +1115,23 @@ func (r *layerStore) Size(name string) (int64, error) { return -1, nil } +// Requires startWriting. func (r *layerStore) ClearFlag(id string, flag string) error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to clear flags on layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to clear flags on layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(id) if !ok { return ErrLayerUnknown } delete(layer.Flags, flag) - return r.Save() + return r.saveFor(layer) } +// Requires startWriting. func (r *layerStore) SetFlag(id string, flag string, value interface{}) error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to set flags on layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to set flags on layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(id) if !ok { @@ -644,13 +1141,14 @@ func (r *layerStore) SetFlag(id string, flag string, value interface{}) error { layer.Flags = make(map[string]interface{}) } layer.Flags[flag] = value - return r.Save() + return r.saveFor(layer) } func (r *layerStore) Status() ([][2]string, error) { return r.driver.Status(), nil } +// Requires startWriting. func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []string, aLayer drivers.AdditionalLayer) (layer *Layer, err error) { if duplicateLayer, idInUse := r.byid[id]; idInUse { return duplicateLayer, ErrDuplicateID @@ -685,7 +1183,9 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s // TODO: check if necessary fields are filled 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 for _, name := range names { // names got from the additional layer store won't be used r.byname[name] = layer @@ -696,16 +1196,19 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s if layer.UncompressedDigest != "" { r.byuncompressedsum[layer.UncompressedDigest] = append(r.byuncompressedsum[layer.UncompressedDigest], layer.ID) } - if err := r.Save(); err != nil { - r.driver.Remove(id) + if err := r.saveFor(layer); err != nil { + 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 copyLayer(layer), nil } +// Requires startWriting. func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error) { - if !r.IsReadWrite() { - return nil, -1, fmt.Errorf("not allowed to create new layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return nil, -1, fmt.Errorf("not allowed to create new layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } if err := os.MkdirAll(r.rundir, 0700); err != nil { return nil, -1, err @@ -770,7 +1273,7 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab parentMappings = &idtools.IDMappings{} } if mountLabel != "" { - label.ReserveLabel(mountLabel) + selinux.ReserveLabel(mountLabel) } // Before actually creating the layer, make a persistent record of it with incompleteFlag, @@ -793,9 +1296,12 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab UIDMap: copyIDMap(moreOptions.UIDMap), GIDMap: copyIDMap(moreOptions.GIDMap), BigDataNames: []string{}, + volatileStore: moreOptions.Volatile, } 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 for _, name := range names { r.byname[name] = layer @@ -820,7 +1326,7 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab } }() - err := r.Save() + err := r.saveFor(layer) if err != nil { cleanupFailureContext = "saving incomplete layer metadata" return nil, -1, err @@ -886,7 +1392,7 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab } } delete(layer.Flags, incompleteFlag) - err = r.Save() + err = r.saveFor(layer) if err != nil { cleanupFailureContext = "saving finished layer metadata" return nil, -1, err @@ -897,34 +1403,41 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab return layer, size, err } +// Requires startWriting. func (r *layerStore) 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) { layer, _, err = r.Put(id, parent, names, mountLabel, options, moreOptions, writeable, flags, nil) return layer, err } +// Requires startWriting. func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (layer *Layer, err error) { return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil) } +// Requires startReading or startWriting. func (r *layerStore) Mounted(id string) (int, error) { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return 0, fmt.Errorf("no mount information for layers at %q: %w", r.mountspath(), ErrStoreIsReadOnly) } - r.mountsLockfile.RLock() - defer r.mountsLockfile.Unlock() - if modified, err := r.mountsLockfile.Modified(); modified || err != nil { - if err = r.loadMounts(); err != nil { - return 0, err - } - } layer, ok := r.lookup(id) if !ok { return 0, ErrLayerUnknown } + // NOTE: The caller of this function is not holding (currently cannot hold) r.mountsLockfile, + // so the data is necessarily obsolete by the time this function returns. So, we don’t even + // try to reload it in this function, we just rely on r.load() that happened during + // r.startReading() or r.startWriting(). return layer.MountCount, nil } +// Requires startWriting. func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error) { + // LOCKING BUG: This is reachable via store.Diff → layerStore.Diff → layerStore.newFileGetter + // (with btrfs and zfs graph drivers) holding layerStore only locked for reading, while it modifies + // - r.layers[].MountCount (directly and via loadMounts / saveMounts) + // - r.layers[].MountPoint (directly and via loadMounts / saveMounts) + // - r.bymount (via loadMounts / saveMounts) + // check whether options include ro option hasReadOnlyOpt := func(opts []string) bool { for _, item := range opts { @@ -937,17 +1450,14 @@ func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error) // You are not allowed to mount layers from readonly stores if they // are not mounted read/only. - if !r.IsReadWrite() && !hasReadOnlyOpt(options.Options) { + if !r.lockfile.IsReadWrite() && !hasReadOnlyOpt(options.Options) { return "", fmt.Errorf("not allowed to update mount locations for layers at %q: %w", r.mountspath(), ErrStoreIsReadOnly) } r.mountsLockfile.Lock() defer r.mountsLockfile.Unlock() - if modified, err := r.mountsLockfile.Modified(); modified || err != nil { - if err = r.loadMounts(); err != nil { - return "", err - } + if err := r.reloadMountsIfChanged(); err != nil { + return "", err } - defer r.mountsLockfile.Touch() layer, ok := r.lookup(id) if !ok { return "", ErrLayerUnknown @@ -987,18 +1497,22 @@ func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error) return mountpoint, err } -func (r *layerStore) Unmount(id string, force bool) (bool, error) { - if !r.IsReadWrite() { +// Requires startWriting. +func (r *layerStore) unmount(id string, force bool, conditional bool) (bool, error) { + // LOCKING BUG: This is reachable via store.Diff → layerStore.Diff → layerStore.newFileGetter → simpleGetCloser.Close() + // (with btrfs and zfs graph drivers) holding layerStore only locked for reading, while it modifies + // - r.layers[].MountCount (directly and via loadMounts / saveMounts) + // - r.layers[].MountPoint (directly and via loadMounts / saveMounts) + // - r.bymount (via loadMounts / saveMounts) + + if !r.lockfile.IsReadWrite() { return false, fmt.Errorf("not allowed to update mount locations for layers at %q: %w", r.mountspath(), ErrStoreIsReadOnly) } r.mountsLockfile.Lock() defer r.mountsLockfile.Unlock() - if modified, err := r.mountsLockfile.Modified(); modified || err != nil { - if err = r.loadMounts(); err != nil { - return false, err - } + if err := r.reloadMountsIfChanged(); err != nil { + return false, err } - defer r.mountsLockfile.Touch() layer, ok := r.lookup(id) if !ok { layerByMount, ok := r.bymount[filepath.Clean(id)] @@ -1007,6 +1521,9 @@ func (r *layerStore) Unmount(id string, force bool) (bool, error) { } layer = layerByMount } + if conditional && layer.MountCount == 0 { + return false, ErrLayerNotMounted + } if force { layer.MountCount = 1 } @@ -1026,17 +1543,16 @@ func (r *layerStore) Unmount(id string, force bool) (bool, error) { return true, err } +// Requires startReading or startWriting. func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) { - if !r.IsReadWrite() { + if !r.lockfile.IsReadWrite() { return nil, nil, fmt.Errorf("no mount information for layers at %q: %w", r.mountspath(), ErrStoreIsReadOnly) } r.mountsLockfile.RLock() defer r.mountsLockfile.Unlock() - if modified, err := r.mountsLockfile.Modified(); modified || err != nil { - if err = r.loadMounts(); err != nil { - return nil, nil, err - } - } + // We are not checking r.mountsLockfile.Modified() and calling r.loadMounts here because the store + // is only locked for reading = we are not allowed to modify layer data. + // Holding r.mountsLockfile protects us against concurrent mount/unmount operations. layer, ok := r.lookup(id) if !ok { return nil, nil, ErrLayerUnknown @@ -1049,6 +1565,13 @@ func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) { // We don't know which directories to examine. return nil, nil, ErrLayerNotMounted } + // Holding r.mountsLockfile protects us against concurrent mount/unmount operations, but we didn’t + // hold it continuously since the time we loaded the mount data; so it’s possible the layer + // was unmounted in the meantime, or mounted elsewhere. Treat that as if we were run after the unmount, + // = a missing mount, not a filesystem error. + if _, err := system.Lstat(layer.MountPoint); errors.Is(err, os.ErrNotExist) { + return nil, nil, ErrLayerNotMounted + } rootuid, rootgid, err := idtools.GetRootUIDGID(layer.UIDMap, layer.GIDMap) if err != nil { return nil, nil, fmt.Errorf("reading root ID values for layer %q: %w", layer.ID, err) @@ -1097,26 +1620,15 @@ func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) { return uids, gids, nil } +// Requires startWriting. func (r *layerStore) removeName(layer *Layer, name string) { layer.Names = stringSliceWithoutValue(layer.Names, name) } -// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. -func (r *layerStore) SetNames(id string, names []string) error { - return r.updateNames(id, names, setNames) -} - -func (r *layerStore) AddNames(id string, names []string) error { - return r.updateNames(id, names, addNames) -} - -func (r *layerStore) RemoveNames(id string, names []string) error { - return r.updateNames(id, names, removeNames) -} - +// Requires startWriting. func (r *layerStore) updateNames(id string, names []string, op updateNameOperation) error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to change layer name assignments at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to change layer name assignments at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(id) if !ok { @@ -1137,7 +1649,7 @@ func (r *layerStore) updateNames(id string, names []string, op updateNameOperati r.byname[name] = layer } layer.Names = names - return r.Save() + return r.saveFor(layer) } func (r *layerStore) datadir(id string) string { @@ -1148,6 +1660,7 @@ func (r *layerStore) datapath(id, key string) string { return filepath.Join(r.datadir(id), makeBigDataBaseName(key)) } +// Requires startReading or startWriting. func (r *layerStore) BigData(id, key string) (io.ReadCloser, error) { if key == "" { return nil, fmt.Errorf("can't retrieve layer big data value for empty name: %w", ErrInvalidBigDataName) @@ -1159,12 +1672,13 @@ func (r *layerStore) BigData(id, key string) (io.ReadCloser, error) { return os.Open(r.datapath(layer.ID, key)) } +// Requires startWriting. func (r *layerStore) SetBigData(id, key string, data io.Reader) error { if key == "" { return fmt.Errorf("can't set empty name for layer big data item: %w", ErrInvalidBigDataName) } - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to save data items associated with layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to save data items associated with layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(id) if !ok { @@ -1201,11 +1715,12 @@ func (r *layerStore) SetBigData(id, key string, data io.Reader) error { } if addName { layer.BigDataNames = append(layer.BigDataNames, key) - return r.Save() + return r.saveFor(layer) } return nil } +// Requires startReading or startWriting. func (r *layerStore) BigDataNames(id string) ([]string, error) { layer, ok := r.lookup(id) if !ok { @@ -1214,6 +1729,7 @@ func (r *layerStore) BigDataNames(id string) ([]string, error) { return copyStringSlice(layer.BigDataNames), nil } +// Requires startReading or startWriting. func (r *layerStore) Metadata(id string) (string, error) { if layer, ok := r.lookup(id); ok { return layer.Metadata, nil @@ -1221,13 +1737,14 @@ func (r *layerStore) Metadata(id string) (string, error) { return "", ErrLayerUnknown } +// Requires startWriting. func (r *layerStore) SetMetadata(id, metadata string) error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to modify layer metadata at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to modify layer metadata at %q: %w", r.layerdir, ErrStoreIsReadOnly) } if layer, ok := r.lookup(id); ok { layer.Metadata = metadata - return r.Save() + return r.saveFor(layer) } return ErrLayerUnknown } @@ -1237,6 +1754,7 @@ func (r *layerStore) tspath(id string) string { } // layerHasIncompleteFlag returns true if layer.Flags contains an incompleteFlag set to true +// The caller must hold r.inProcessLock for reading. func layerHasIncompleteFlag(layer *Layer) bool { // layer.Flags[…] is defined to succeed and return ok == false if Flags == nil if flagValue, ok := layer.Flags[incompleteFlag]; ok { @@ -1247,9 +1765,10 @@ func layerHasIncompleteFlag(layer *Layer) bool { return false } +// Requires startWriting. func (r *layerStore) deleteInternal(id string) error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to delete layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to delete layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(id) if !ok { @@ -1261,15 +1780,14 @@ func (r *layerStore) deleteInternal(id string) error { layer.Flags = make(map[string]interface{}) } layer.Flags[incompleteFlag] = true - if err := r.Save(); err != nil { + if err := r.saveFor(layer); err != nil { return err } } // We never unset incompleteFlag; below, we remove the entire object from r.layers. id = layer.ID - err := r.driver.Remove(id) - if err != nil { + if err := r.driver.Remove(id); err != nil { return err } @@ -1279,7 +1797,9 @@ func (r *layerStore) deleteInternal(id string) error { for _, name := range layer.Names { 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 if layer.MountPoint != "" { delete(r.bymount, layer.MountPoint) @@ -1309,13 +1829,14 @@ func (r *layerStore) deleteInternal(id string) error { } } if !found { - label.ReleaseLabel(mountLabel) + selinux.ReleaseLabel(mountLabel) } } return nil } +// Requires startWriting. func (r *layerStore) deleteInDigestMap(id string) { for digest, layers := range r.bycompressedsum { for i, layerID := range layers { @@ -1337,6 +1858,7 @@ func (r *layerStore) deleteInDigestMap(id string) { } } +// Requires startWriting. func (r *layerStore) Delete(id string) error { layer, ok := r.lookup(id) if !ok { @@ -1346,37 +1868,28 @@ func (r *layerStore) Delete(id string) error { // The layer may already have been explicitly unmounted, but if not, we // should try to clean that up before we start deleting anything at the // driver level. - mountCount, err := r.Mounted(id) - if err != nil { - return fmt.Errorf("checking if layer %q is still mounted: %w", id, err) - } - for mountCount > 0 { - if _, err := r.Unmount(id, false); err != nil { - return err + for { + _, err := r.unmount(id, false, true) + if err == ErrLayerNotMounted { + break } - mountCount, err = r.Mounted(id) if err != nil { - return fmt.Errorf("checking if layer %q is still mounted: %w", id, err) + return err } } if err := r.deleteInternal(id); err != nil { return err } - 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 + return r.saveFor(layer) } +// Requires startReading or startWriting. func (r *layerStore) Exists(id string) bool { _, ok := r.lookup(id) return ok } +// Requires startReading or startWriting. func (r *layerStore) Get(id string) (*Layer, error) { if layer, ok := r.lookup(id); ok { return copyLayer(layer), nil @@ -1384,9 +1897,10 @@ func (r *layerStore) Get(id string) (*Layer, error) { return nil, ErrLayerUnknown } +// Requires startWriting. func (r *layerStore) Wipe() error { - if !r.IsReadWrite() { - return fmt.Errorf("not allowed to delete layers at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return fmt.Errorf("not allowed to delete layers at %q: %w", r.layerdir, ErrStoreIsReadOnly) } ids := make([]string, 0, len(r.byid)) for id := range r.byid { @@ -1403,6 +1917,7 @@ func (r *layerStore) Wipe() error { return nil } +// Requires startReading or startWriting. func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, fromLayer, toLayer *Layer, err error) { var ok bool toLayer, ok = r.lookup(to) @@ -1427,6 +1942,7 @@ func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID st return from, to, fromLayer, toLayer, nil } +// The caller must hold r.inProcessLock for reading. func (r *layerStore) layerMappings(layer *Layer) *idtools.IDMappings { if layer == nil { return &idtools.IDMappings{} @@ -1434,6 +1950,7 @@ func (r *layerStore) layerMappings(layer *Layer) *idtools.IDMappings { return idtools.NewIDMappingsFromMaps(layer.UIDMap, layer.GIDMap) } +// Requires startReading or startWriting. func (r *layerStore) Changes(from, to string) ([]archive.Change, error) { from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to) if err != nil { @@ -1452,11 +1969,13 @@ func (s *simpleGetCloser) Get(path string) (io.ReadCloser, error) { return os.Open(filepath.Join(s.path, path)) } +// LOCKING BUG: See the comments in layerStore.Diff func (s *simpleGetCloser) Close() error { - _, err := s.r.Unmount(s.id, false) + _, err := s.r.unmount(s.id, false, false) return err } +// LOCKING BUG: See the comments in layerStore.Diff func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) { if getter, ok := r.driver.(drivers.DiffGetterDriver); ok { return getter.DiffGetter(id) @@ -1472,6 +1991,25 @@ func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) { }, 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) +} + +// Requires startReading or startWriting. func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) { var metadata storage.Unpacker @@ -1503,12 +2041,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, preader.Close() return nil, err } - go func() { - defer pwriter.Close() - defer compressor.Close() - defer rc.Close() - io.Copy(compressor, rc) - }() + go writeCompressedDataGoroutine(pwriter, compressor, rc) return preader, nil } @@ -1588,6 +2121,8 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, metadata = storage.NewJSONUnpacker(decompressor) + // LOCKING BUG: With btrfs and zfs graph drivers), this uses r.Mount() and r.unmount() holding layerStore only locked for reading + // but they modify in-memory state. fgetter, err := r.newFileGetter(to) if err != nil { errs := multierror.Append(nil, fmt.Errorf("creating file-getter: %w", err)) @@ -1623,6 +2158,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, return maybeCompressReadCloser(rc) } +// Requires startReading or startWriting. func (r *layerStore) DiffSize(from, to string) (size int64, err error) { var fromLayer, toLayer *Layer from, to, fromLayer, toLayer, err = r.findParentAndLayer(from, to) @@ -1632,13 +2168,15 @@ func (r *layerStore) DiffSize(from, to string) (size int64, err error) { return r.driver.DiffSize(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel) } +// Requires startWriting. func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error) { return r.applyDiffWithOptions(to, nil, diff) } +// Requires startWriting. func (r *layerStore) applyDiffWithOptions(to string, layerOptions *LayerOptions, diff io.Reader) (size int64, err error) { - if !r.IsReadWrite() { - return -1, fmt.Errorf("not allowed to modify layer contents at %q: %w", r.layerspath(), ErrStoreIsReadOnly) + if !r.lockfile.IsReadWrite() { + return -1, fmt.Errorf("not allowed to modify layer contents at %q: %w", r.layerdir, ErrStoreIsReadOnly) } layer, ok := r.lookup(to) @@ -1724,13 +2262,11 @@ func (r *layerStore) applyDiffWithOptions(to string, layerOptions *LayerOptions, return -1, err } compressor.Close() - if err == nil { - if err := os.MkdirAll(filepath.Dir(r.tspath(layer.ID)), 0700); err != nil { - return -1, err - } - if err := ioutils.AtomicWriteFile(r.tspath(layer.ID), tsdata.Bytes(), 0600); err != nil { - return -1, err - } + if err := os.MkdirAll(filepath.Dir(r.tspath(layer.ID)), 0700); err != nil { + return -1, err + } + if err := ioutils.AtomicWriteFile(r.tspath(layer.ID), tsdata.Bytes(), 0600); err != nil { + return -1, err } if compressedDigester != nil { compressedDigest = compressedDigester.Digest() @@ -1779,11 +2315,12 @@ func (r *layerStore) applyDiffWithOptions(to string, layerOptions *LayerOptions, return layer.GIDs[i] < layer.GIDs[j] }) - err = r.Save() + err = r.saveFor(layer) return size, err } +// Requires (startReading or?) startWriting. func (r *layerStore) DifferTarget(id string) (string, error) { ddriver, ok := r.driver.(drivers.DriverWithDiffer) if !ok { @@ -1796,6 +2333,7 @@ func (r *layerStore) DifferTarget(id string) (string, error) { return ddriver.DifferTarget(layer.ID) } +// Requires startWriting. func (r *layerStore) ApplyDiffFromStagingDirectory(id, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error { ddriver, ok := r.driver.(drivers.DriverWithDiffer) if !ok { @@ -1820,18 +2358,21 @@ func (r *layerStore) ApplyDiffFromStagingDirectory(id, stagingDirectory string, layer.UncompressedDigest = diffOutput.UncompressedDigest layer.UncompressedSize = diffOutput.Size layer.Metadata = diffOutput.Metadata - if err = r.Save(); err != nil { + if err = r.saveFor(layer); err != nil { return err } for k, v := range diffOutput.BigData { 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 } +// Requires startWriting. func (r *layerStore) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error) { ddriver, ok := r.driver.(drivers.DriverWithDiffer) if !ok { @@ -1859,7 +2400,7 @@ func (r *layerStore) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOp } layer.UIDs = output.UIDs layer.GIDs = output.GIDs - err = r.Save() + err = r.saveFor(layer) return &output, err } @@ -1871,6 +2412,7 @@ func (r *layerStore) CleanupStagingDirectory(stagingDirectory string) error { return ddriver.CleanupStagingDirectory(stagingDirectory) } +// Requires startReading or startWriting. func (r *layerStore) layersByDigestMap(m map[digest.Digest][]string, d digest.Digest) ([]Layer, error) { var layers []Layer for _, layerID := range m[d] { @@ -1883,87 +2425,18 @@ func (r *layerStore) layersByDigestMap(m map[digest.Digest][]string, d digest.Di return layers, nil } +// Requires startReading or startWriting. func (r *layerStore) LayersByCompressedDigest(d digest.Digest) ([]Layer, error) { return r.layersByDigestMap(r.bycompressedsum, d) } +// Requires startReading or startWriting. func (r *layerStore) LayersByUncompressedDigest(d digest.Digest) ([]Layer, error) { return r.layersByDigestMap(r.byuncompressedsum, d) } -func (r *layerStore) Lock() { - r.lockfile.Lock() -} - -func (r *layerStore) RecursiveLock() { - r.lockfile.RecursiveLock() -} - -func (r *layerStore) RLock() { - r.lockfile.RLock() -} - -func (r *layerStore) Unlock() { - r.lockfile.Unlock() -} - -func (r *layerStore) Touch() error { - return r.lockfile.Touch() -} - -func (r *layerStore) Modified() (bool, error) { - var mmodified, tmodified bool - lmodified, err := r.lockfile.Modified() - if err != nil { - return lmodified, err - } - if r.IsReadWrite() { - r.mountsLockfile.RLock() - defer r.mountsLockfile.Unlock() - mmodified, err = r.mountsLockfile.Modified() - if err != nil { - return lmodified, err - } - } - - if lmodified || mmodified { - return true, nil - } - - // If the layers.json file has been modified manually, then we have to - // reload the storage in any case. - info, err := os.Stat(r.layerspath()) - if err != nil && !os.IsNotExist(err) { - return false, fmt.Errorf("stat layers file: %w", err) - } - if info != nil { - tmodified = info.ModTime() != r.layerspathModified - } - - return tmodified, nil -} - -func (r *layerStore) IsReadWrite() bool { - return r.lockfile.IsReadWrite() -} - -func (r *layerStore) TouchedSince(when time.Time) bool { - return r.lockfile.TouchedSince(when) -} - -func (r *layerStore) Locked() bool { - return r.lockfile.Locked() -} - -func (r *layerStore) ReloadIfChanged() error { - r.loadMut.Lock() - defer r.loadMut.Unlock() - - modified, err := r.Modified() - if err == nil && modified { - return r.Load() - } - return err +func (r *layerStore) supportsShifting() bool { + return r.driver.SupportsShifting() } func closeAll(closes ...func() error) (rErr error) { diff --git a/vendor/github.com/containers/storage/lockfile_compat.go b/vendor/github.com/containers/storage/lockfile_compat.go index 6fac2eba..64020388 100644 --- a/vendor/github.com/containers/storage/lockfile_compat.go +++ b/vendor/github.com/containers/storage/lockfile_compat.go @@ -4,12 +4,15 @@ import ( "github.com/containers/storage/pkg/lockfile" ) -type Locker = lockfile.Locker +// Deprecated: Use lockfile.*LockFile. +type Locker = lockfile.Locker //lint:ignore SA1019 // lockfile.Locker is deprecated +// Deprecated: Use lockfile.GetLockFile. func GetLockfile(path string) (lockfile.Locker, error) { return lockfile.GetLockfile(path) } +// Deprecated: Use lockfile.GetROLockFile. func GetROLockfile(path string) (lockfile.Locker, error) { return lockfile.GetROLockfile(path) } diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index 82c0adeb..6209205b 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -527,6 +527,9 @@ func (ta *tarAppender) addTarFile(path, name string) error { if err := ReadUserXattrToTarHeader(path, hdr); err != nil { return err } + if err := ReadFileFlagsToTarHeader(path, hdr); err != nil { + return err + } if ta.CopyPass { copyPassHeader(hdr) } @@ -770,6 +773,15 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L } + // We defer setting flags on directories until the end of + // Unpack or UnpackLayer in case setting them makes the + // directory immutable. + if hdr.Typeflag != tar.TypeDir { + if err := WriteFileFlagsFromTarHeader(path, hdr); err != nil { + return err + } + } + if len(errs) > 0 { logrus.WithFields(logrus.Fields{ "errors": errs, @@ -864,7 +876,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) rebaseName := options.RebaseNames[include] walkRoot := getWalkRoot(srcPath, include) - filepath.WalkDir(walkRoot, func(filePath string, d fs.DirEntry, err error) error { + if err := filepath.WalkDir(walkRoot, func(filePath string, d fs.DirEntry, err error) error { if err != nil { logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err) return nil @@ -874,7 +886,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) if err != nil || (!options.IncludeSourceDir && relFilePath == "." && d.IsDir()) { // Error getting relative path OR we are looking // at the source directory path. Skip in both situations. - return nil + return nil //nolint: nilerr } if options.IncludeSourceDir && include == "." && relFilePath != "." { @@ -891,8 +903,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) if include != relFilePath { matches, err := pm.IsMatch(relFilePath) if err != nil { - logrus.Errorf("Matching %s: %v", relFilePath, err) - return err + return fmt.Errorf("matching %s: %w", relFilePath, err) } skip = matches } @@ -955,7 +966,10 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) } } return nil - }) + }); err != nil { + logrus.Errorf("%s", err) + return + } } }() @@ -1099,6 +1113,9 @@ loop: if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil { return err } + if err := WriteFileFlagsFromTarHeader(path, hdr); err != nil { + return err + } } return nil } diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_110.go b/vendor/github.com/containers/storage/pkg/archive/archive_110.go index 7bc44a56..eab9da51 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive_110.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive_110.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package archive diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_19.go b/vendor/github.com/containers/storage/pkg/archive/archive_19.go index d19811fd..f591bf38 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive_19.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive_19.go @@ -1,3 +1,4 @@ +//go:build !go1.10 // +build !go1.10 package archive diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_bsd.go b/vendor/github.com/containers/storage/pkg/archive/archive_bsd.go new file mode 100644 index 00000000..4d362f07 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/archive/archive_bsd.go @@ -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) +} diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_freebsd.go b/vendor/github.com/containers/storage/pkg/archive/archive_freebsd.go deleted file mode 100644 index 36017c3b..00000000 --- a/vendor/github.com/containers/storage/pkg/archive/archive_freebsd.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_linux.go b/vendor/github.com/containers/storage/pkg/archive/archive_linux.go index 51fbd9a2..36e5d4bc 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive_linux.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive_linux.go @@ -189,3 +189,22 @@ func GetFileOwner(path string) (uint32, uint32, uint32, error) { } 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 +} diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_other.go b/vendor/github.com/containers/storage/pkg/archive/archive_other.go index 4b883444..2468ab3c 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive_other.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive_other.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package archive diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_unix.go b/vendor/github.com/containers/storage/pkg/archive/archive_unix.go index d0fb3306..f8a34c83 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive_unix.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive_unix.go @@ -1,5 +1,5 @@ -//go:build !windows && !freebsd -// +build !windows,!freebsd +//go:build !windows +// +build !windows package archive @@ -50,8 +50,8 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) ( // 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 + hdr.Devmajor = int64(major(uint64(s.Rdev))) //nolint: unconvert + hdr.Devminor = int64(minor(uint64(s.Rdev))) //nolint: unconvert } } @@ -101,25 +101,6 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { 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 func handleLLink(targetPath, path string) error { // Note: on Linux, the link syscall will not follow symlinks. diff --git a/vendor/github.com/containers/storage/pkg/archive/changes.go b/vendor/github.com/containers/storage/pkg/archive/changes.go index 6cd9e35e..fc705484 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes.go @@ -56,7 +56,7 @@ func (change *Change) String() string { return fmt.Sprintf("%s %s", change.Kind, change.Path) } -// for sort.Sort +// changesByPath implements sort.Interface. type changesByPath []Change func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path } diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_other.go b/vendor/github.com/containers/storage/pkg/archive/changes_other.go index 8769f229..c27930e9 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_other.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_other.go @@ -43,7 +43,12 @@ func collectFileInfoForChanges(oldDir, newDir string, oldIDMap, newIDMap *idtool func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInfo, error) { root := newRootFileInfo(idMappings) - err := filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, err error) error { + sourceStat, err := system.Lstat(sourceDir) + if err != nil { + return nil, err + } + + err = filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -86,8 +91,12 @@ func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInf if err != nil { return err } - info.stat = s + if s.Dev() != sourceStat.Dev() { + return filepath.SkipDir + } + + info.stat = s info.capability, _ = system.Lgetxattr(path, "security.capability") parent.children[info.name] = info diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_unix.go b/vendor/github.com/containers/storage/pkg/archive/changes_unix.go index 1cc1910f..6b2e5938 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_unix.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package archive @@ -29,6 +30,7 @@ func statDifferent(oldStat *system.StatT, oldInfo *FileInfo, newStat *system.Sta if oldStat.Mode() != newStat.Mode() || ownerChanged || oldStat.Rdev() != newStat.Rdev() || + oldStat.Flags() != newStat.Flags() || // Don't look at size for dirs, its not a good measure of change (oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR && (!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) { diff --git a/vendor/github.com/containers/storage/pkg/archive/copy_unix.go b/vendor/github.com/containers/storage/pkg/archive/copy_unix.go index e305b5e4..d6c5fd98 100644 --- a/vendor/github.com/containers/storage/pkg/archive/copy_unix.go +++ b/vendor/github.com/containers/storage/pkg/archive/copy_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package archive diff --git a/vendor/github.com/containers/storage/pkg/archive/diff.go b/vendor/github.com/containers/storage/pkg/archive/diff.go index 7e835d44..8fec5af3 100644 --- a/vendor/github.com/containers/storage/pkg/archive/diff.go +++ b/vendor/github.com/containers/storage/pkg/archive/diff.go @@ -145,6 +145,9 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, return nil } if _, exists := unpackedPaths[path]; !exists { + if err := resetImmutable(path, nil); err != nil { + return err + } err := os.RemoveAll(path) return err } @@ -156,6 +159,9 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, } else { originalBase := base[len(WhiteoutPrefix):] originalPath := filepath.Join(dir, originalBase) + if err := resetImmutable(originalPath, nil); err != nil { + return 0, err + } if err := os.RemoveAll(originalPath); err != nil { return 0, err } @@ -165,7 +171,15 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, // The only exception is when it is a directory *and* the file from // the layer is also a directory. Then we want to merge them (i.e. // just apply the metadata from the layer). + // + // We always reset the immutable flag (if present) to allow metadata + // changes and to allow directory modification. The flag will be + // re-applied based on the contents of hdr either at the end for + // directories or in createTarFile otherwise. if fi, err := os.Lstat(path); err == nil { + if err := resetImmutable(path, &fi); err != nil { + return 0, err + } if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { if err := os.RemoveAll(path); err != nil { return 0, err @@ -215,6 +229,9 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil { return 0, err } + if err := WriteFileFlagsFromTarHeader(path, hdr); err != nil { + return 0, err + } } return size, nil @@ -245,7 +262,9 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp if err != nil { 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 { layer, err = DecompressStream(layer) diff --git a/vendor/github.com/containers/storage/pkg/archive/fflags_bsd.go b/vendor/github.com/containers/storage/pkg/archive/fflags_bsd.go new file mode 100644 index 00000000..14661c41 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/archive/fflags_bsd.go @@ -0,0 +1,167 @@ +//go:build freebsd +// +build freebsd + +package archive + +import ( + "archive/tar" + "fmt" + "math/bits" + "os" + "strings" + "syscall" + + "github.com/containers/storage/pkg/system" +) + +const ( + paxSCHILYFflags = "SCHILY.fflags" +) + +var ( + flagNameToValue = map[string]uint32{ + "sappnd": system.SF_APPEND, + "sappend": system.SF_APPEND, + "arch": system.SF_ARCHIVED, + "archived": system.SF_ARCHIVED, + "schg": system.SF_IMMUTABLE, + "schange": system.SF_IMMUTABLE, + "simmutable": system.SF_IMMUTABLE, + "sunlnk": system.SF_NOUNLINK, + "sunlink": system.SF_NOUNLINK, + "snapshot": system.SF_SNAPSHOT, + "uappnd": system.UF_APPEND, + "uappend": system.UF_APPEND, + "uarch": system.UF_ARCHIVE, + "uarchive": system.UF_ARCHIVE, + "hidden": system.UF_HIDDEN, + "uhidden": system.UF_HIDDEN, + "uchg": system.UF_IMMUTABLE, + "uchange": system.UF_IMMUTABLE, + "uimmutable": system.UF_IMMUTABLE, + "uunlnk": system.UF_NOUNLINK, + "uunlink": system.UF_NOUNLINK, + "offline": system.UF_OFFLINE, + "uoffline": system.UF_OFFLINE, + "opaque": system.UF_OPAQUE, + "rdonly": system.UF_READONLY, + "urdonly": system.UF_READONLY, + "readonly": system.UF_READONLY, + "ureadonly": system.UF_READONLY, + "reparse": system.UF_REPARSE, + "ureparse": system.UF_REPARSE, + "sparse": system.UF_SPARSE, + "usparse": system.UF_SPARSE, + "system": system.UF_SYSTEM, + "usystem": system.UF_SYSTEM, + } + // Only include the short names for the reverse map + flagValueToName = map[uint32]string{ + system.SF_APPEND: "sappnd", + system.SF_ARCHIVED: "arch", + system.SF_IMMUTABLE: "schg", + system.SF_NOUNLINK: "sunlnk", + system.SF_SNAPSHOT: "snapshot", + system.UF_APPEND: "uappnd", + system.UF_ARCHIVE: "uarch", + system.UF_HIDDEN: "hidden", + system.UF_IMMUTABLE: "uchg", + system.UF_NOUNLINK: "uunlnk", + system.UF_OFFLINE: "offline", + system.UF_OPAQUE: "opaque", + system.UF_READONLY: "rdonly", + system.UF_REPARSE: "reparse", + system.UF_SPARSE: "sparse", + system.UF_SYSTEM: "system", + } +) + +func parseFileFlags(fflags string) (uint32, uint32, error) { + var set, clear uint32 = 0, 0 + for _, fflag := range strings.Split(fflags, ",") { + isClear := false + if strings.HasPrefix(fflag, "no") { + isClear = true + fflag = strings.TrimPrefix(fflag, "no") + } + if value, ok := flagNameToValue[fflag]; ok { + if isClear { + clear |= value + } else { + set |= value + } + } else { + return 0, 0, fmt.Errorf("parsing file flags, unrecognised token: %s", fflag) + } + } + return set, clear, nil +} + +func formatFileFlags(fflags uint32) (string, error) { + var res = []string{} + for fflags != 0 { + // Extract lowest set bit + fflag := uint32(1) << bits.TrailingZeros32(fflags) + if name, ok := flagValueToName[fflag]; ok { + res = append(res, name) + } else { + return "", fmt.Errorf("formatting file flags, unrecognised flag: %x", fflag) + } + fflags &= ^fflag + } + return strings.Join(res, ","), nil +} + +func ReadFileFlagsToTarHeader(path string, hdr *tar.Header) error { + st, err := system.Lstat(path) + if err != nil { + return err + } + fflags, err := formatFileFlags(st.Flags()) + if err != nil { + return err + } + if fflags != "" { + if hdr.PAXRecords == nil { + hdr.PAXRecords = map[string]string{} + } + hdr.PAXRecords[paxSCHILYFflags] = fflags + } + return nil +} + +func WriteFileFlagsFromTarHeader(path string, hdr *tar.Header) error { + if fflags, ok := hdr.PAXRecords[paxSCHILYFflags]; ok { + var set, clear uint32 + set, clear, err := parseFileFlags(fflags) + if err != nil { + return err + } + + // Apply the delta to the existing file flags + st, err := system.Lstat(path) + if err != nil { + return err + } + return system.Lchflags(path, (st.Flags() & ^clear)|set) + } + return nil +} + +func resetImmutable(path string, fi *os.FileInfo) error { + var flags uint32 + if fi != nil { + flags = (*fi).Sys().(*syscall.Stat_t).Flags + } else { + st, err := system.Lstat(path) + if err != nil { + return err + } + flags = st.Flags() + } + if flags&(system.SF_IMMUTABLE|system.UF_IMMUTABLE) != 0 { + flags &= ^(system.SF_IMMUTABLE | system.UF_IMMUTABLE) + return system.Lchflags(path, flags) + } + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/archive/fflags_unsupported.go b/vendor/github.com/containers/storage/pkg/archive/fflags_unsupported.go new file mode 100644 index 00000000..27a0bae2 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/archive/fflags_unsupported.go @@ -0,0 +1,21 @@ +//go:build !freebsd +// +build !freebsd + +package archive + +import ( + "archive/tar" + "os" +) + +func ReadFileFlagsToTarHeader(path string, hdr *tar.Header) error { + return nil +} + +func WriteFileFlagsFromTarHeader(path string, hdr *tar.Header) error { + return nil +} + +func resetImmutable(path string, fi *os.FileInfo) error { + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/archive/time_unsupported.go b/vendor/github.com/containers/storage/pkg/archive/time_unsupported.go index e85aac05..8db64e80 100644 --- a/vendor/github.com/containers/storage/pkg/archive/time_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/archive/time_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package archive diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go b/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go index b5d8961e..2de95f39 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go @@ -82,7 +82,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions } } - r := io.NopCloser(tarArchive) + r := tarArchive if decompress { decompressedArchive, err := archive.DecompressStream(tarArchive) if err != nil { diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/archive_darwin.go b/vendor/github.com/containers/storage/pkg/chrootarchive/archive_darwin.go index d257cc8e..42ee39f4 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/archive_darwin.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/archive_darwin.go @@ -6,11 +6,7 @@ import ( "github.com/containers/storage/pkg/archive" ) -func chroot(path string) error { - return nil -} - -func invokeUnpack(decompressedArchive io.ReadCloser, +func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions, root string) error { return archive.Unpack(decompressedArchive, dest, options) diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/archive_windows.go b/vendor/github.com/containers/storage/pkg/chrootarchive/archive_windows.go index 8a5c680b..1395ff8c 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/archive_windows.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/archive_windows.go @@ -12,7 +12,7 @@ func chroot(path string) error { return nil } -func invokeUnpack(decompressedArchive io.ReadCloser, +func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions, root string) error { // Windows is different to Linux here because Windows does not support diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/chroot_unix.go b/vendor/github.com/containers/storage/pkg/chrootarchive/chroot_unix.go index d5aedd00..b03e9746 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/chroot_unix.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/chroot_unix.go @@ -1,3 +1,4 @@ +//go:build !windows && !linux && !darwin // +build !windows,!linux,!darwin package chrootarchive diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/diff_darwin.go b/vendor/github.com/containers/storage/pkg/chrootarchive/diff_darwin.go index 52c677bc..7b4ea9e5 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/diff_darwin.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/diff_darwin.go @@ -27,13 +27,13 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions tmpDir, err := os.MkdirTemp(os.Getenv("temp"), "temp-storage-extract") if err != nil { - return 0, fmt.Errorf("ApplyLayer failed to create temp-storage-extract under %s. %s", dest, err) + return 0, fmt.Errorf("ApplyLayer failed to create temp-storage-extract under %s: %w", dest, err) } s, err := archive.UnpackLayer(dest, layer, options) os.RemoveAll(tmpDir) if err != nil { - return 0, fmt.Errorf("ApplyLayer %s failed UnpackLayer to %s: %s", layer, dest, err) + return 0, fmt.Errorf("ApplyLayer %s failed UnpackLayer to %s: %w", layer, dest, err) } return s, nil diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/jsoniter.go b/vendor/github.com/containers/storage/pkg/chrootarchive/jsoniter.go index 63f97045..40eec4dc 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/jsoniter.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/jsoniter.go @@ -1,3 +1,6 @@ +//go:build !windows && !darwin +// +build !windows,!darwin + package chrootarchive import jsoniter "github.com/json-iterator/go" diff --git a/vendor/github.com/containers/storage/pkg/chunked/compression.go b/vendor/github.com/containers/storage/pkg/chunked/compression.go index 8d4d3c4a..e828d479 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/compression.go +++ b/vendor/github.com/containers/storage/pkg/chunked/compression.go @@ -1,21 +1,10 @@ package chunked import ( - archivetar "archive/tar" - "bytes" - "encoding/binary" - "errors" - "fmt" "io" - "strconv" - "github.com/containerd/stargz-snapshotter/estargz" "github.com/containers/storage/pkg/chunked/compressor" "github.com/containers/storage/pkg/chunked/internal" - "github.com/klauspost/compress/zstd" - "github.com/klauspost/pgzip" - digest "github.com/opencontainers/go-digest" - "github.com/vbatts/tar-split/archive/tar" ) const ( @@ -29,247 +18,6 @@ const ( TypeSymlink = internal.TypeSymlink ) -var typesToTar = map[string]byte{ - TypeReg: tar.TypeReg, - TypeLink: tar.TypeLink, - TypeChar: tar.TypeChar, - TypeBlock: tar.TypeBlock, - TypeDir: tar.TypeDir, - TypeFifo: tar.TypeFifo, - TypeSymlink: tar.TypeSymlink, -} - -func typeToTarType(t string) (byte, error) { - r, found := typesToTar[t] - if !found { - return 0, fmt.Errorf("unknown type: %v", t) - } - return r, nil -} - -func isZstdChunkedFrameMagic(data []byte) bool { - if len(data) < 8 { - return false - } - return bytes.Equal(internal.ZstdChunkedFrameMagic, data[:8]) -} - -func readEstargzChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) { - // information on the format here https://github.com/containerd/stargz-snapshotter/blob/main/docs/stargz-estargz.md - footerSize := int64(51) - if blobSize <= footerSize { - return nil, 0, errors.New("blob too small") - } - chunk := ImageSourceChunk{ - Offset: uint64(blobSize - footerSize), - Length: uint64(footerSize), - } - parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) - if err != nil { - return nil, 0, err - } - var reader io.ReadCloser - select { - case r := <-parts: - reader = r - case err := <-errs: - return nil, 0, err - } - defer reader.Close() - footer := make([]byte, footerSize) - if _, err := io.ReadFull(reader, footer); err != nil { - return nil, 0, err - } - - /* Read the ToC offset: - - 10 bytes gzip header - - 2 bytes XLEN (length of Extra field) = 26 (4 bytes header + 16 hex digits + len("STARGZ")) - - 2 bytes Extra: SI1 = 'S', SI2 = 'G' - - 2 bytes Extra: LEN = 22 (16 hex digits + len("STARGZ")) - - 22 bytes Extra: subfield = fmt.Sprintf("%016xSTARGZ", offsetOfTOC) - - 5 bytes flate header: BFINAL = 1(last block), BTYPE = 0(non-compressed block), LEN = 0 - - 8 bytes gzip footer - */ - tocOffset, err := strconv.ParseInt(string(footer[16:16+22-6]), 16, 64) - if err != nil { - return nil, 0, fmt.Errorf("parse ToC offset: %w", err) - } - - size := int64(blobSize - footerSize - tocOffset) - // set a reasonable limit - if size > (1<<20)*50 { - return nil, 0, errors.New("manifest too big") - } - - chunk = ImageSourceChunk{ - Offset: uint64(tocOffset), - Length: uint64(size), - } - parts, errs, err = blobStream.GetBlobAt([]ImageSourceChunk{chunk}) - if err != nil { - return nil, 0, err - } - - var tocReader io.ReadCloser - select { - case r := <-parts: - tocReader = r - case err := <-errs: - return nil, 0, err - } - defer tocReader.Close() - - r, err := pgzip.NewReader(tocReader) - if err != nil { - return nil, 0, err - } - defer r.Close() - - aTar := archivetar.NewReader(r) - - header, err := aTar.Next() - if err != nil { - return nil, 0, err - } - // set a reasonable limit - if header.Size > (1<<20)*50 { - return nil, 0, errors.New("manifest too big") - } - - manifestUncompressed := make([]byte, header.Size) - if _, err := io.ReadFull(aTar, manifestUncompressed); err != nil { - return nil, 0, err - } - - manifestDigester := digest.Canonical.Digester() - manifestChecksum := manifestDigester.Hash() - if _, err := manifestChecksum.Write(manifestUncompressed); err != nil { - return nil, 0, err - } - - d, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation]) - if err != nil { - return nil, 0, err - } - if manifestDigester.Digest() != d { - return nil, 0, errors.New("invalid manifest checksum") - } - - return manifestUncompressed, tocOffset, nil -} - -// readZstdChunkedManifest reads the zstd:chunked manifest from the seekable stream blobStream. The blob total size must -// be specified. -// This function uses the io.containers.zstd-chunked. annotations when specified. -func readZstdChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) { - footerSize := int64(internal.FooterSizeSupported) - if blobSize <= footerSize { - return nil, 0, errors.New("blob too small") - } - - manifestChecksumAnnotation := annotations[internal.ManifestChecksumKey] - if manifestChecksumAnnotation == "" { - return nil, 0, fmt.Errorf("manifest checksum annotation %q not found", internal.ManifestChecksumKey) - } - - var offset, length, lengthUncompressed, manifestType uint64 - - if offsetMetadata := annotations[internal.ManifestInfoKey]; offsetMetadata != "" { - if _, err := fmt.Sscanf(offsetMetadata, "%d:%d:%d:%d", &offset, &length, &lengthUncompressed, &manifestType); err != nil { - return nil, 0, err - } - } else { - chunk := ImageSourceChunk{ - Offset: uint64(blobSize - footerSize), - Length: uint64(footerSize), - } - parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) - if err != nil { - return nil, 0, err - } - var reader io.ReadCloser - select { - case r := <-parts: - reader = r - case err := <-errs: - return nil, 0, err - } - footer := make([]byte, footerSize) - if _, err := io.ReadFull(reader, footer); err != nil { - return nil, 0, err - } - - offset = binary.LittleEndian.Uint64(footer[0:8]) - length = binary.LittleEndian.Uint64(footer[8:16]) - lengthUncompressed = binary.LittleEndian.Uint64(footer[16:24]) - manifestType = binary.LittleEndian.Uint64(footer[24:32]) - if !isZstdChunkedFrameMagic(footer[32:40]) { - return nil, 0, errors.New("invalid magic number") - } - } - - if manifestType != internal.ManifestTypeCRFS { - return nil, 0, errors.New("invalid manifest type") - } - - // set a reasonable limit - if length > (1<<20)*50 { - return nil, 0, errors.New("manifest too big") - } - if lengthUncompressed > (1<<20)*50 { - return nil, 0, errors.New("manifest too big") - } - - chunk := ImageSourceChunk{ - Offset: offset, - Length: length, - } - - parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) - if err != nil { - return nil, 0, err - } - var reader io.ReadCloser - select { - case r := <-parts: - reader = r - case err := <-errs: - return nil, 0, err - } - - manifest := make([]byte, length) - if _, err := io.ReadFull(reader, manifest); err != nil { - return nil, 0, err - } - - manifestDigester := digest.Canonical.Digester() - manifestChecksum := manifestDigester.Hash() - if _, err := manifestChecksum.Write(manifest); err != nil { - return nil, 0, err - } - - d, err := digest.Parse(manifestChecksumAnnotation) - if err != nil { - return nil, 0, err - } - if manifestDigester.Digest() != d { - return nil, 0, errors.New("invalid manifest checksum") - } - - decoder, err := zstd.NewReader(nil) - if err != nil { - return nil, 0, err - } - defer decoder.Close() - - b := make([]byte, 0, lengthUncompressed) - if decoded, err := decoder.DecodeAll(manifest, b); err == nil { - return decoded, int64(offset), nil - } - - return manifest, int64(offset), nil -} - // ZstdCompressor is a CompressorFunc for the zstd compression algorithm. // Deprecated: Use pkg/chunked/compressor.ZstdCompressor. func ZstdCompressor(r io.Writer, metadata map[string]string, level *int) (io.WriteCloser, error) { diff --git a/vendor/github.com/containers/storage/pkg/chunked/compression_linux.go b/vendor/github.com/containers/storage/pkg/chunked/compression_linux.go new file mode 100644 index 00000000..f6c7b77b --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/chunked/compression_linux.go @@ -0,0 +1,259 @@ +package chunked + +import ( + archivetar "archive/tar" + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "strconv" + + "github.com/containerd/stargz-snapshotter/estargz" + "github.com/containers/storage/pkg/chunked/internal" + "github.com/klauspost/compress/zstd" + "github.com/klauspost/pgzip" + digest "github.com/opencontainers/go-digest" + "github.com/vbatts/tar-split/archive/tar" +) + +var typesToTar = map[string]byte{ + TypeReg: tar.TypeReg, + TypeLink: tar.TypeLink, + TypeChar: tar.TypeChar, + TypeBlock: tar.TypeBlock, + TypeDir: tar.TypeDir, + TypeFifo: tar.TypeFifo, + TypeSymlink: tar.TypeSymlink, +} + +func typeToTarType(t string) (byte, error) { + r, found := typesToTar[t] + if !found { + return 0, fmt.Errorf("unknown type: %v", t) + } + return r, nil +} + +func isZstdChunkedFrameMagic(data []byte) bool { + if len(data) < 8 { + return false + } + return bytes.Equal(internal.ZstdChunkedFrameMagic, data[:8]) +} + +func readEstargzChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) { + // information on the format here https://github.com/containerd/stargz-snapshotter/blob/main/docs/stargz-estargz.md + footerSize := int64(51) + if blobSize <= footerSize { + return nil, 0, errors.New("blob too small") + } + chunk := ImageSourceChunk{ + Offset: uint64(blobSize - footerSize), + Length: uint64(footerSize), + } + parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) + if err != nil { + return nil, 0, err + } + var reader io.ReadCloser + select { + case r := <-parts: + reader = r + case err := <-errs: + return nil, 0, err + } + defer reader.Close() + footer := make([]byte, footerSize) + if _, err := io.ReadFull(reader, footer); err != nil { + return nil, 0, err + } + + /* Read the ToC offset: + - 10 bytes gzip header + - 2 bytes XLEN (length of Extra field) = 26 (4 bytes header + 16 hex digits + len("STARGZ")) + - 2 bytes Extra: SI1 = 'S', SI2 = 'G' + - 2 bytes Extra: LEN = 22 (16 hex digits + len("STARGZ")) + - 22 bytes Extra: subfield = fmt.Sprintf("%016xSTARGZ", offsetOfTOC) + - 5 bytes flate header: BFINAL = 1(last block), BTYPE = 0(non-compressed block), LEN = 0 + - 8 bytes gzip footer + */ + tocOffset, err := strconv.ParseInt(string(footer[16:16+22-6]), 16, 64) + if err != nil { + return nil, 0, fmt.Errorf("parse ToC offset: %w", err) + } + + size := int64(blobSize - footerSize - tocOffset) + // set a reasonable limit + if size > (1<<20)*50 { + return nil, 0, errors.New("manifest too big") + } + + chunk = ImageSourceChunk{ + Offset: uint64(tocOffset), + Length: uint64(size), + } + parts, errs, err = blobStream.GetBlobAt([]ImageSourceChunk{chunk}) + if err != nil { + return nil, 0, err + } + + var tocReader io.ReadCloser + select { + case r := <-parts: + tocReader = r + case err := <-errs: + return nil, 0, err + } + defer tocReader.Close() + + r, err := pgzip.NewReader(tocReader) + if err != nil { + return nil, 0, err + } + defer r.Close() + + aTar := archivetar.NewReader(r) + + header, err := aTar.Next() + if err != nil { + return nil, 0, err + } + // set a reasonable limit + if header.Size > (1<<20)*50 { + return nil, 0, errors.New("manifest too big") + } + + manifestUncompressed := make([]byte, header.Size) + if _, err := io.ReadFull(aTar, manifestUncompressed); err != nil { + return nil, 0, err + } + + manifestDigester := digest.Canonical.Digester() + manifestChecksum := manifestDigester.Hash() + if _, err := manifestChecksum.Write(manifestUncompressed); err != nil { + return nil, 0, err + } + + d, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation]) + if err != nil { + return nil, 0, err + } + if manifestDigester.Digest() != d { + return nil, 0, errors.New("invalid manifest checksum") + } + + return manifestUncompressed, tocOffset, nil +} + +// readZstdChunkedManifest reads the zstd:chunked manifest from the seekable stream blobStream. The blob total size must +// be specified. +// This function uses the io.github.containers.zstd-chunked. annotations when specified. +func readZstdChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) { + footerSize := int64(internal.FooterSizeSupported) + if blobSize <= footerSize { + return nil, 0, errors.New("blob too small") + } + + manifestChecksumAnnotation := annotations[internal.ManifestChecksumKey] + if manifestChecksumAnnotation == "" { + return nil, 0, fmt.Errorf("manifest checksum annotation %q not found", internal.ManifestChecksumKey) + } + + var offset, length, lengthUncompressed, manifestType uint64 + + if offsetMetadata := annotations[internal.ManifestInfoKey]; offsetMetadata != "" { + if _, err := fmt.Sscanf(offsetMetadata, "%d:%d:%d:%d", &offset, &length, &lengthUncompressed, &manifestType); err != nil { + return nil, 0, err + } + } else { + chunk := ImageSourceChunk{ + Offset: uint64(blobSize - footerSize), + Length: uint64(footerSize), + } + parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) + if err != nil { + return nil, 0, err + } + var reader io.ReadCloser + select { + case r := <-parts: + reader = r + case err := <-errs: + return nil, 0, err + } + footer := make([]byte, footerSize) + if _, err := io.ReadFull(reader, footer); err != nil { + return nil, 0, err + } + + offset = binary.LittleEndian.Uint64(footer[0:8]) + length = binary.LittleEndian.Uint64(footer[8:16]) + lengthUncompressed = binary.LittleEndian.Uint64(footer[16:24]) + manifestType = binary.LittleEndian.Uint64(footer[24:32]) + if !isZstdChunkedFrameMagic(footer[32:40]) { + return nil, 0, errors.New("invalid magic number") + } + } + + if manifestType != internal.ManifestTypeCRFS { + return nil, 0, errors.New("invalid manifest type") + } + + // set a reasonable limit + if length > (1<<20)*50 { + return nil, 0, errors.New("manifest too big") + } + if lengthUncompressed > (1<<20)*50 { + return nil, 0, errors.New("manifest too big") + } + + chunk := ImageSourceChunk{ + Offset: offset, + Length: length, + } + + parts, errs, err := blobStream.GetBlobAt([]ImageSourceChunk{chunk}) + if err != nil { + return nil, 0, err + } + var reader io.ReadCloser + select { + case r := <-parts: + reader = r + case err := <-errs: + return nil, 0, err + } + + manifest := make([]byte, length) + if _, err := io.ReadFull(reader, manifest); err != nil { + return nil, 0, err + } + + manifestDigester := digest.Canonical.Digester() + manifestChecksum := manifestDigester.Hash() + if _, err := manifestChecksum.Write(manifest); err != nil { + return nil, 0, err + } + + d, err := digest.Parse(manifestChecksumAnnotation) + if err != nil { + return nil, 0, err + } + if manifestDigester.Digest() != d { + return nil, 0, errors.New("invalid manifest checksum") + } + + decoder, err := zstd.NewReader(nil) + if err != nil { + return nil, 0, err + } + defer decoder.Close() + + b := make([]byte, 0, lengthUncompressed) + if decoded, err := decoder.DecodeAll(manifest, b); err == nil { + return decoded, int64(offset), nil + } + + return manifest, int64(offset), nil +} diff --git a/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go b/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go index 362c168d..2a9bdc67 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go +++ b/vendor/github.com/containers/storage/pkg/chunked/compressor/compressor.go @@ -33,11 +33,11 @@ const ( holesFinderStateEOF ) -// ReadByte reads a single byte from the underlying reader. +// readByte reads a single byte from the underlying reader. // If a single byte is read, the return value is (0, RAW-BYTE-VALUE, nil). // If there are at least f.THRESHOLD consecutive zeros, then the // return value is (N_CONSECUTIVE_ZEROS, '\x00'). -func (f *holesFinder) ReadByte() (int64, byte, error) { +func (f *holesFinder) readByte() (int64, byte, error) { for { switch f.state { // reading the file stream @@ -78,7 +78,7 @@ func (f *holesFinder) ReadByte() (int64, byte, error) { f.state = holesFinderStateFound } } else { - if f.reader.UnreadByte(); err != nil { + if err := f.reader.UnreadByte(); err != nil { return 0, 0, err } f.state = holesFinderStateRead @@ -95,7 +95,7 @@ func (f *holesFinder) ReadByte() (int64, byte, error) { return holeLen, 0, nil } if b != 0 { - if f.reader.UnreadByte(); err != nil { + if err := f.reader.UnreadByte(); err != nil { return 0, 0, err } f.state = holesFinderStateRead @@ -159,7 +159,7 @@ func (rc *rollingChecksumReader) Read(b []byte) (bool, int, error) { } for i := 0; i < len(b); i++ { - holeLen, n, err := rc.reader.ReadByte() + holeLen, n, err := rc.reader.readByte() if err != nil { if err == io.EOF { rc.closed = true @@ -429,7 +429,7 @@ func zstdChunkedWriterWithLevel(out io.Writer, metadata map[string]string, level go func() { 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() close(ch) }() diff --git a/vendor/github.com/containers/storage/pkg/chunked/internal/compression.go b/vendor/github.com/containers/storage/pkg/chunked/internal/compression.go index 3bb5286d..092b0353 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/internal/compression.go +++ b/vendor/github.com/containers/storage/pkg/chunked/internal/compression.go @@ -88,8 +88,8 @@ func GetType(t byte) (string, error) { } const ( - ManifestChecksumKey = "io.containers.zstd-chunked.manifest-checksum" - ManifestInfoKey = "io.containers.zstd-chunked.manifest-position" + ManifestChecksumKey = "io.github.containers.zstd-chunked.manifest-checksum" + ManifestInfoKey = "io.github.containers.zstd-chunked.manifest-position" // ManifestTypeCRFS is a manifest file compatible with the CRFS TOC file. ManifestTypeCRFS = 1 @@ -114,7 +114,7 @@ func appendZstdSkippableFrame(dest io.Writer, data []byte) error { return err } - var size []byte = make([]byte, 4) + size := make([]byte, 4) binary.LittleEndian.PutUint32(size, uint32(len(data))) if _, err := dest.Write(size); err != nil { return err @@ -168,7 +168,7 @@ func WriteZstdChunkedManifest(dest io.Writer, outMetadata map[string]string, off } // Store the offset to the manifest and its size in LE order - var manifestDataLE []byte = make([]byte, FooterSizeSupported) + manifestDataLE := make([]byte, FooterSizeSupported) binary.LittleEndian.PutUint64(manifestDataLE, manifestOffset) binary.LittleEndian.PutUint64(manifestDataLE[8:], uint64(len(compressedManifest))) binary.LittleEndian.PutUint64(manifestDataLE[16:], uint64(len(manifest))) diff --git a/vendor/github.com/containers/storage/pkg/chunked/storage.go b/vendor/github.com/containers/storage/pkg/chunked/storage.go index f0bd3627..752ee252 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/storage.go +++ b/vendor/github.com/containers/storage/pkg/chunked/storage.go @@ -17,7 +17,7 @@ type ImageSourceSeekable interface { } // ErrBadRequest is returned when the request is not valid -type ErrBadRequest struct { +type ErrBadRequest struct { //nolint: errname } func (e ErrBadRequest) Error() string { diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go index 082fb1ba..ead15c14 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_log.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go index 190d83d4..7baca812 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_deferred_remove.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_deferred_remove.go index 7f793c27..071f7f35 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_deferred_remove.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_deferred_remove.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !libdm_no_deferred_remove // +build linux,cgo,!libdm_no_deferred_remove package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_dynamic.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_dynamic.go index 7d845089..93dcc322 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_dynamic.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_dynamic.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !static_build // +build linux,cgo,!static_build package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go index a880fec8..91906f2e 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && libdm_no_deferred_remove // +build linux,cgo,libdm_no_deferred_remove package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_static.go b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_static.go index cf7f26a4..68ea48fe 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_static.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/devmapper_wrapper_static.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && static_build // +build linux,cgo,static_build package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/devicemapper/ioctl.go b/vendor/github.com/containers/storage/pkg/devicemapper/ioctl.go index 50ea7c48..90ffe2c3 100644 --- a/vendor/github.com/containers/storage/pkg/devicemapper/ioctl.go +++ b/vendor/github.com/containers/storage/pkg/devicemapper/ioctl.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper diff --git a/vendor/github.com/containers/storage/pkg/dmesg/dmesg_linux.go b/vendor/github.com/containers/storage/pkg/dmesg/dmesg_linux.go index 7df7f3d4..e883d25f 100644 --- a/vendor/github.com/containers/storage/pkg/dmesg/dmesg_linux.go +++ b/vendor/github.com/containers/storage/pkg/dmesg/dmesg_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package dmesg diff --git a/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go b/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go index abf6e2f8..e3cef48a 100644 --- a/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go +++ b/vendor/github.com/containers/storage/pkg/fileutils/fileutils.go @@ -321,14 +321,14 @@ func ReadSymlinkedDirectory(path string) (string, error) { var realPath string var err error if realPath, err = filepath.Abs(path); err != nil { - return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err) + return "", fmt.Errorf("unable to get absolute path for %s: %w", path, err) } if realPath, err = filepath.EvalSymlinks(realPath); err != nil { - return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err) + return "", fmt.Errorf("failed to canonicalise path for %s: %w", path, err) } realPathInfo, err := os.Stat(realPath) if err != nil { - return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err) + return "", fmt.Errorf("failed to stat target '%s' of '%s': %w", realPath, path, err) } if !realPathInfo.Mode().IsDir() { return "", fmt.Errorf("canonical path points to a file '%s'", realPath) diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go index 027db259..0883ee02 100644 --- a/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go @@ -1,3 +1,4 @@ +//go:build !linux && !darwin && !freebsd // +build !linux,!darwin,!freebsd package homedir diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go index 37dc9159..9976f19a 100644 --- a/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go @@ -63,7 +63,7 @@ func StickRuntimeDirContents(files []string) ([]string, error) { runtimeDir, err := GetRuntimeDir() if err != nil { // ignore error if runtimeDir is empty - return nil, nil + return nil, nil //nolint: nilerr } runtimeDir, err = filepath.Abs(runtimeDir) if err != nil { diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go index a5760906..4cec8b26 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -2,6 +2,7 @@ package idtools import ( "bufio" + "errors" "fmt" "os" "os/user" @@ -359,7 +360,8 @@ func parseSubidFile(path, username string) (ranges, error) { } func checkChownErr(err error, name string, uid, gid int) error { - if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL { + var e *os.PathError + if errors.As(err, &e) && e.Err == syscall.EINVAL { return fmt.Errorf("potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally and run podman-system-migrate: %w", uid, gid, name, err) } return err diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go index 84da1b76..78141fb8 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux || !libsubid || !cgo // +build !linux !libsubid !cgo package idtools diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go index 16be94f4..dc69c607 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package idtools diff --git a/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go index a467f41c..40e507f7 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go +++ b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go @@ -2,11 +2,12 @@ package idtools import ( "fmt" - "regexp" "sort" "strconv" "strings" "sync" + + "github.com/containers/storage/pkg/regexp" ) // add a user and/or group to Linux /etc/passwd, /etc/group using standard @@ -24,7 +25,7 @@ var ( "usermod": "-%s %d-%d %s", } - idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) + idOutRegexp = regexp.Delayed(`uid=([0-9]+).*gid=([0-9]+)`) // default length for a UID/GID subordinate range defaultRangeLen = 65536 defaultRangeStart = 100000 diff --git a/vendor/github.com/containers/storage/pkg/ioutils/fswriters.go b/vendor/github.com/containers/storage/pkg/ioutils/fswriters.go index a74893e8..231d1c47 100644 --- a/vendor/github.com/containers/storage/pkg/ioutils/fswriters.go +++ b/vendor/github.com/containers/storage/pkg/ioutils/fswriters.go @@ -4,6 +4,7 @@ import ( "io" "os" "path/filepath" + "time" ) // AtomicFileWriterOptions specifies options for creating the atomic file writer. @@ -13,9 +14,12 @@ type AtomicFileWriterOptions struct { // storage after it has been written and before it is moved to // the specified path. NoSync bool + // On successful return from Close() this is set to the mtime of the + // newly written file. + ModTime time.Time } -var defaultWriterOptions AtomicFileWriterOptions = AtomicFileWriterOptions{} +var defaultWriterOptions = AtomicFileWriterOptions{} // SetDefaultOptions overrides the default options used when creating an // atomic file writer. @@ -27,6 +31,13 @@ func SetDefaultOptions(opts AtomicFileWriterOptions) { // temporary file and closing it atomically changes the temporary file to // destination path. Writing and closing concurrently is not allowed. 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)) if err != nil { return nil, err @@ -54,28 +65,38 @@ func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, err } // AtomicWriteFile atomically writes data to a file named by filename. -func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { - f, err := NewAtomicFileWriter(filename, perm) +func AtomicWriteFileWithOpts(filename string, data []byte, perm os.FileMode, opts *AtomicFileWriterOptions) error { + f, err := newAtomicFileWriter(filename, perm, opts) if err != nil { return err } n, err := f.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite - f.(*atomicFileWriter).writeErr = err + f.writeErr = err } if err1 := f.Close(); err == nil { err = err1 } + + if opts != nil { + opts.ModTime = f.modTime + } + return err } +func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { + return AtomicWriteFileWithOpts(filename, data, perm, nil) +} + type atomicFileWriter struct { f *os.File fn string writeErr error perm os.FileMode noSync bool + modTime time.Time } func (w *atomicFileWriter) Write(dt []byte) (int, error) { @@ -98,9 +119,25 @@ func (w *atomicFileWriter) Close() (retErr error) { return err } } + + // fstat before closing the fd + info, statErr := w.f.Stat() + if statErr == nil { + w.modTime = info.ModTime() + } + // We delay error reporting until after the real call to close() + // to match the traditional linux close() behaviour that an fd + // is invalid (closed) even if close returns failure. While + // weird, this allows a well defined way to not leak open fds. + if err := w.f.Close(); err != nil { return err } + + if statErr != nil { + return statErr + } + if err := os.Chmod(w.f.Name(), w.perm); err != nil { return err } diff --git a/vendor/github.com/containers/storage/pkg/ioutils/fswriters_unsupported.go b/vendor/github.com/containers/storage/pkg/ioutils/fswriters_unsupported.go index 79a09403..63548928 100644 --- a/vendor/github.com/containers/storage/pkg/ioutils/fswriters_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/ioutils/fswriters_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package ioutils diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go index d3f4df09..ec25f8a9 100644 --- a/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go @@ -10,6 +10,8 @@ import ( // A Locker represents a file lock where the file is used to cache an // identifier of the last party that made changes to whatever's being protected // by the lock. +// +// Deprecated: Refer directly to *LockFile, the provided implementation, instead. type Locker interface { // Acquire a writer lock. // The default unix implementation panics if: @@ -17,10 +19,6 @@ type Locker interface { // - tried to lock a read-only lock-file Lock() - // Acquire a writer lock recursively, allowing for recursive acquisitions - // within the same process space. - RecursiveLock() - // Unlock the lock. // The default unix implementation panics if: // - unlocking an unlocked lock @@ -32,10 +30,13 @@ type Locker interface { // Touch records, for others sharing the lock, that the caller was the // last writer. It should only be called with the lock held. + // + // Deprecated: Use *LockFile.RecordWrite. Touch() error // Modified() checks if the most recent writer was a party other than the // last recorded writer. It should only be called with the lock held. + // Deprecated: Use *LockFile.ModifiedSince. Modified() (bool, error) // TouchedSince() checks if the most recent writer modified the file (likely using Touch()) after the specified time. @@ -44,63 +45,86 @@ type Locker interface { // IsReadWrite() checks if the lock file is read-write IsReadWrite() bool - // Locked() checks if lock is locked for writing by a thread in this process - Locked() bool + // AssertLocked() can be used by callers that _know_ that they hold the lock (for reading or writing), for sanity checking. + // It might do nothing at all, or it may panic if the caller is not the owner of this lock. + AssertLocked() + + // AssertLockedForWriting() can be used by callers that _know_ that they hold the lock locked for writing, for sanity checking. + // It might do nothing at all, or it may panic if the caller is not the owner of this lock for writing. + AssertLockedForWriting() } var ( - lockfiles map[string]Locker - lockfilesLock sync.Mutex + lockFiles map[string]*LockFile + lockFilesLock sync.Mutex ) +// GetLockFile opens a read-write lock file, creating it if necessary. The +// *LockFile object may already be locked if the path has already been requested +// by the current process. +func GetLockFile(path string) (*LockFile, error) { + return getLockfile(path, false) +} + // GetLockfile opens a read-write lock file, creating it if necessary. The // Locker object may already be locked if the path has already been requested // by the current process. +// +// Deprecated: Use GetLockFile func GetLockfile(path string) (Locker, error) { - return getLockfile(path, false) + return GetLockFile(path) +} + +// GetROLockFile opens a read-only lock file, creating it if necessary. The +// *LockFile object may already be locked if the path has already been requested +// by the current process. +func GetROLockFile(path string) (*LockFile, error) { + return getLockfile(path, true) } // GetROLockfile opens a read-only lock file, creating it if necessary. The // Locker object may already be locked if the path has already been requested // by the current process. +// +// Deprecated: Use GetROLockFile func GetROLockfile(path string) (Locker, error) { - return getLockfile(path, true) + return GetROLockFile(path) } -// getLockfile returns a Locker object, possibly (depending on the platform) +// getLockFile returns a *LockFile object, possibly (depending on the platform) // working inter-process, and associated with the specified path. // -// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the // “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, -// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation. // // WARNING: // - The lock may or MAY NOT be inter-process. // - There may or MAY NOT be an actual object on the filesystem created for the specified path. // - Even if ro, the lock MAY be exclusive. -func getLockfile(path string, ro bool) (Locker, error) { - lockfilesLock.Lock() - defer lockfilesLock.Unlock() - if lockfiles == nil { - lockfiles = make(map[string]Locker) +func getLockfile(path string, ro bool) (*LockFile, error) { + lockFilesLock.Lock() + defer lockFilesLock.Unlock() + if lockFiles == nil { + lockFiles = make(map[string]*LockFile) } cleanPath, err := filepath.Abs(path) if err != nil { return nil, fmt.Errorf("ensuring that path %q is an absolute path: %w", path, err) } - if locker, ok := lockfiles[cleanPath]; ok { - if ro && locker.IsReadWrite() { + if lockFile, ok := lockFiles[cleanPath]; ok { + if ro && lockFile.IsReadWrite() { return nil, fmt.Errorf("lock %q is not a read-only lock", cleanPath) } - if !ro && !locker.IsReadWrite() { + if !ro && !lockFile.IsReadWrite() { return nil, fmt.Errorf("lock %q is not a read-write lock", cleanPath) } - return locker, nil + return lockFile, nil } - locker, err := createLockerForPath(cleanPath, ro) // platform-dependent locker + lockFile, err := createLockFileForPath(cleanPath, ro) // platform-dependent LockFile if err != nil { return nil, err } - lockfiles[cleanPath] = locker - return locker, nil + lockFiles[cleanPath] = lockFile + return lockFile, nil } diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go index 3c242016..33598091 100644 --- a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go @@ -18,28 +18,48 @@ import ( "golang.org/x/sys/unix" ) -type lockfile struct { +// *LockFile represents a file lock where the file is used to cache an +// identifier of the last party that made changes to whatever's being protected +// by the lock. +// +// It MUST NOT be created manually. Use GetLockFile or GetROLockFile instead. +type LockFile struct { + // The following fields are only set when constructing *LockFile, and must never be modified afterwards. + // They are safe to access without any other locking. + file string + ro bool + // rwMutex serializes concurrent reader-writer acquisitions in the same process space rwMutex *sync.RWMutex // stateMutex is used to synchronize concurrent accesses to the state below stateMutex *sync.Mutex counter int64 - file string - fd uintptr - lw []byte // "last writer"-unique value valid as of the last .Touch() or .Modified(), generated by newLastWriterID() + lw LastWrite // A global value valid as of the last .Touch() or .Modified() locktype int16 locked bool - ro bool - recursive bool + // The following fields are only modified on transitions between counter == 0 / counter != 0. + // Thus, they can be safely accessed by users _that currently hold the LockFile_ without locking. + // In other cases, they need to be protected using stateMutex. + fd uintptr +} + +// LastWrite is an opaque identifier of the last write to some *LockFile. +// It can be used by users of a *LockFile to determine if the lock indicates changes +// since the last check. +// +// Never construct a LastWrite manually; only accept it from *LockFile methods, and pass it back. +type LastWrite struct { + // Never modify fields of a LastWrite object; it has value semantics. + state []byte // Contents of the lock file. } const lastWriterIDSize = 64 // This must be the same as len(stringid.GenerateRandomID) var lastWriterIDCounter uint64 // Private state for newLastWriterID -// newLastWriterID returns a new "last writer" ID. +// newLastWrite returns a new "last write" ID. // The value must be different on every call, and also differ from values // generated by other processes. -func newLastWriterID() []byte { +func newLastWrite() LastWrite { // The ID is (PID, time, per-process counter, random) // PID + time represents both a unique process across reboots, // and a specific time within the process; the per-process counter @@ -61,7 +81,38 @@ func newLastWriterID() []byte { panic(err) // This shouldn't happen } - return res + return LastWrite{ + state: res, + } +} + +// newLastWriteFromData returns a LastWrite corresponding to data that came from a previous LastWrite.serialize +func newLastWriteFromData(serialized []byte) LastWrite { + if serialized == nil { + panic("newLastWriteFromData with nil data") + } + return LastWrite{ + state: serialized, + } +} + +// serialize returns bytes to write to the lock file to represent the specified write. +func (lw LastWrite) serialize() []byte { + if lw.state == nil { + panic("LastWrite.serialize on an uninitialized object") + } + return lw.state +} + +// Equals returns true if lw matches other +func (lw LastWrite) equals(other LastWrite) bool { + if lw.state == nil { + panic("LastWrite.equals on an uninitialized object") + } + if other.state == nil { + panic("LastWrite.equals with an uninitialized counterparty") + } + return bytes.Equal(lw.state, other.state) } // openLock opens the file at path and returns the corresponding file @@ -79,13 +130,13 @@ func openLock(path string, ro bool) (fd int, err error) { } fd, err = unix.Open(path, flags, 0o644) if err == nil { - return + return fd, nil } // the directory of the lockfile seems to be removed, try to create it if os.IsNotExist(err) { if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { - return fd, fmt.Errorf("creating locker directory: %w", err) + return fd, fmt.Errorf("creating lock file directory: %w", err) } return openLock(path, ro) @@ -94,20 +145,20 @@ func openLock(path string, ro bool) (fd int, err error) { return fd, &os.PathError{Op: "open", Path: path, Err: err} } -// createLockerForPath returns a Locker object, possibly (depending on the platform) +// createLockFileForPath returns new *LockFile object, possibly (depending on the platform) // working inter-process and associated with the specified path. // // This function will be called at most once for each path value within a single process. // -// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the // “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, -// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation. // // WARNING: // - The lock may or MAY NOT be inter-process. // - There may or MAY NOT be an actual object on the filesystem created for the specified path. // - Even if ro, the lock MAY be exclusive. -func createLockerForPath(path string, ro bool) (Locker, error) { +func createLockFileForPath(path string, ro bool) (*LockFile, error) { // Check if we can open the lock. fd, err := openLock(path, ro) if err != nil { @@ -119,22 +170,24 @@ func createLockerForPath(path string, ro bool) (Locker, error) { if ro { locktype = unix.F_RDLCK } - return &lockfile{ - stateMutex: &sync.Mutex{}, + return &LockFile{ + file: path, + ro: ro, + rwMutex: &sync.RWMutex{}, - file: path, - lw: newLastWriterID(), + stateMutex: &sync.Mutex{}, + lw: newLastWrite(), // For compatibility, the first call of .Modified() will always report a change. locktype: int16(locktype), locked: false, - ro: ro}, nil + }, nil } // lock locks the lockfile via FCTNL(2) based on the specified type and // command. -func (l *lockfile) lock(lType int16, recursive bool) { +func (l *LockFile) lock(lType int16) { lk := unix.Flock_t{ Type: lType, - Whence: int16(os.SEEK_SET), + Whence: int16(unix.SEEK_SET), Start: 0, Len: 0, } @@ -142,13 +195,7 @@ func (l *lockfile) lock(lType int16, recursive bool) { case unix.F_RDLCK: l.rwMutex.RLock() 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: panic(fmt.Sprintf("attempted to acquire a file lock of unrecognized type %d", lType)) } @@ -171,39 +218,27 @@ func (l *lockfile) lock(lType int16, recursive bool) { } l.locktype = lType l.locked = true - l.recursive = recursive l.counter++ } // Lock locks the lockfile as a writer. Panic if the lock is a read-only one. -func (l *lockfile) Lock() { +func (l *LockFile) Lock() { if l.ro { panic("can't take write lock on read-only lock file") } else { - l.lock(unix.F_WRLCK, false) - } -} - -// 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) + l.lock(unix.F_WRLCK) } } // LockRead locks the lockfile as a reader. -func (l *lockfile) RLock() { - l.lock(unix.F_RDLCK, false) +func (l *LockFile) RLock() { + l.lock(unix.F_RDLCK) } // Unlock unlocks the lockfile. -func (l *lockfile) Unlock() { +func (l *LockFile) Unlock() { l.stateMutex.Lock() - if l.locked == false { + if !l.locked { // Panic when unlocking an unlocked lock. That's a violation // of the lock semantics and will reveal such. panic("calling Unlock on unlocked lock") @@ -224,7 +259,7 @@ func (l *lockfile) Unlock() { // file lock. unix.Close(int(l.fd)) } - if l.locktype == unix.F_RDLCK || l.recursive { + if l.locktype == unix.F_RDLCK { l.rwMutex.RUnlock() } else { l.rwMutex.Unlock() @@ -232,59 +267,157 @@ func (l *lockfile) Unlock() { l.stateMutex.Unlock() } -// Locked checks if lockfile is locked for writing by a thread in this process. -func (l *lockfile) Locked() bool { - l.stateMutex.Lock() - defer l.stateMutex.Unlock() - return l.locked && (l.locktype == unix.F_WRLCK) +func (l *LockFile) AssertLocked() { + // DO NOT provide a variant that returns the value of l.locked. + // + // If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and + // we can’t tell the difference. + // + // Hence, this “AssertLocked” method, which exists only for sanity checks. + + // Don’t even bother with l.stateMutex: The caller is expected to hold the lock, and in that case l.locked is constant true + // with no possible writers. + // If the caller does not hold the lock, we are violating the locking/memory model anyway, and accessing the data + // without the lock is more efficient for callers, and potentially more visible to lock analysers for incorrect callers. + if !l.locked { + panic("internal error: lock is not held by the expected owner") + } } -// Touch updates the lock file with the UID of the user. -func (l *lockfile) Touch() error { - l.stateMutex.Lock() - if !l.locked || (l.locktype != unix.F_WRLCK) { - panic("attempted to update last-writer in lockfile without the write lock") +func (l *LockFile) AssertLockedForWriting() { + // DO NOT provide a variant that returns the current lock state. + // + // The same caveats as for AssertLocked apply equally. + + l.AssertLocked() + // Like AssertLocked, don’t even bother with l.stateMutex. + if l.locktype != unix.F_WRLCK { + panic("internal error: lock is not held for writing") } - defer l.stateMutex.Unlock() - l.lw = newLastWriterID() - n, err := unix.Pwrite(int(l.fd), l.lw, 0) +} + +// GetLastWrite returns a LastWrite value corresponding to current state of the lock. +// This is typically called before (_not after_) loading the state when initializing a consumer +// of the data protected by the lock. +// During the lifetime of the consumer, the consumer should usually call ModifiedSince instead. +// +// The caller must hold the lock (for reading or writing). +func (l *LockFile) GetLastWrite() (LastWrite, error) { + l.AssertLocked() + contents := make([]byte, lastWriterIDSize) + n, err := unix.Pread(int(l.fd), contents, 0) + if err != nil { + return LastWrite{}, err + } + // It is important to handle the partial read case, because + // the initial size of the lock file is zero, which is a valid + // state (no writes yet) + contents = contents[:n] + return newLastWriteFromData(contents), nil +} + +// RecordWrite updates the lock with a new LastWrite value, and returns the new value. +// +// If this function fails, the LastWriter value of the lock is indeterminate; +// the caller should keep using the previously-recorded LastWrite value, +// and possibly detecting its own modification as an external one: +// +// lw, err := state.lock.RecordWrite() +// if err != nil { /* fail */ } +// state.lastWrite = lw +// +// The caller must hold the lock for writing. +func (l *LockFile) RecordWrite() (LastWrite, error) { + l.AssertLockedForWriting() + lw := newLastWrite() + lockContents := lw.serialize() + n, err := unix.Pwrite(int(l.fd), lockContents, 0) + if err != nil { + return LastWrite{}, err + } + if n != len(lockContents) { + return LastWrite{}, unix.ENOSPC + } + return lw, nil +} + +// ModifiedSince checks if the lock has been changed since a provided LastWrite value, +// and returns the one to record instead. +// +// If ModifiedSince reports no modification, the previous LastWrite value +// is still valid and can continue to be used. +// +// If this function fails, the LastWriter value of the lock is indeterminate; +// the caller should fail and keep using the previously-recorded LastWrite value, +// so that it continues failing until the situation is resolved. Similarly, +// it should only update the recorded LastWrite value after processing the update: +// +// lw2, modified, err := state.lock.ModifiedSince(state.lastWrite) +// if err != nil { /* fail */ } +// state.lastWrite = lw2 +// if modified { +// if err := reload(); err != nil { /* fail */ } +// state.lastWrite = lw2 +// } +// +// The caller must hold the lock (for reading or writing). +func (l *LockFile) ModifiedSince(previous LastWrite) (LastWrite, bool, error) { + l.AssertLocked() + currentLW, err := l.GetLastWrite() + if err != nil { + return LastWrite{}, false, err + } + modified := !previous.equals(currentLW) + return currentLW, modified, nil +} + +// Touch updates the lock file with to record that the current lock holder has modified the lock-protected data. +// +// Deprecated: Use *LockFile.RecordWrite. +func (l *LockFile) Touch() error { + lw, err := l.RecordWrite() if err != nil { return err } - if n != len(l.lw) { - return unix.ENOSPC + l.stateMutex.Lock() + if !l.locked || (l.locktype != unix.F_WRLCK) { + panic("attempted to update last-writer in lockfile without the write lock") } + defer l.stateMutex.Unlock() + l.lw = lw return nil } // Modified indicates if the lockfile has been updated since the last time it // was loaded. -func (l *lockfile) Modified() (bool, error) { +// NOTE: Unlike ModifiedSince, this returns true the first time it is called on a *LockFile. +// Callers cannot, in general, rely on this, because that might have happened for some other +// owner of the same *LockFile who created it previously. +// +// Deprecated: Use *LockFile.ModifiedSince. +func (l *LockFile) Modified() (bool, error) { l.stateMutex.Lock() if !l.locked { panic("attempted to check last-writer in lockfile without locking it first") } defer l.stateMutex.Unlock() - currentLW := make([]byte, len(l.lw)) - n, err := unix.Pread(int(l.fd), currentLW, 0) + oldLW := l.lw + // Note that this is called with stateMutex held; that’s fine because ModifiedSince doesn’t need to lock it. + currentLW, modified, err := l.ModifiedSince(oldLW) if err != nil { return true, err } - if n != len(l.lw) { - return true, nil - } - oldLW := l.lw l.lw = currentLW - return !bytes.Equal(currentLW, oldLW), nil + return modified, nil } // IsReadWriteLock indicates if the lock file is a read-write lock. -func (l *lockfile) IsReadWrite() bool { +func (l *LockFile) IsReadWrite() bool { return !l.ro } // TouchedSince indicates if the lock file has been touched since the specified time -func (l *lockfile) TouchedSince(when time.Time) bool { +func (l *LockFile) TouchedSince(when time.Time) bool { st, err := system.Fstat(int(l.fd)) if err != nil { return true diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go index 82bd91db..09f2aca5 100644 --- a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package lockfile @@ -8,65 +9,140 @@ import ( "time" ) -// createLockerForPath returns a Locker object, possibly (depending on the platform) +// createLockFileForPath returns a *LockFile object, possibly (depending on the platform) // working inter-process and associated with the specified path. // // This function will be called at most once for each path value within a single process. // -// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// If ro, the lock is a read-write lock and the returned *LockFile should correspond to the // “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, -// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// or a read-write lock and *LockFile should correspond to the “lock for writing” (exclusive) operation. // // WARNING: // - The lock may or MAY NOT be inter-process. // - There may or MAY NOT be an actual object on the filesystem created for the specified path. // - Even if ro, the lock MAY be exclusive. -func createLockerForPath(path string, ro bool) (Locker, error) { - return &lockfile{locked: false}, nil +func createLockFileForPath(path string, ro bool) (*LockFile, error) { + return &LockFile{locked: false}, nil } -type lockfile struct { +// *LockFile represents a file lock where the file is used to cache an +// identifier of the last party that made changes to whatever's being protected +// by the lock. +// +// It MUST NOT be created manually. Use GetLockFile or GetROLockFile instead. +type LockFile struct { mu sync.Mutex file string locked bool } -func (l *lockfile) Lock() { - l.mu.Lock() - l.locked = true +// LastWrite is an opaque identifier of the last write to some *LockFile. +// It can be used by users of a *LockFile to determine if the lock indicates changes +// since the last check. +// A default-initialized LastWrite never matches any last write, i.e. it always indicates changes. +type LastWrite struct { + // Nothing: The Windows “implementation” does not actually track writes. } -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) Lock() { + l.mu.Lock() + l.locked = true } -func (l *lockfile) RLock() { +func (l *LockFile) RLock() { l.mu.Lock() l.locked = true } -func (l *lockfile) Unlock() { +func (l *LockFile) Unlock() { l.locked = false l.mu.Unlock() } -func (l *lockfile) Locked() bool { - return l.locked +func (l *LockFile) AssertLocked() { + // DO NOT provide a variant that returns the value of l.locked. + // + // If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and + // we can’t tell the difference. + // + // Hence, this “AssertLocked” method, which exists only for sanity checks. + if !l.locked { + panic("internal error: lock is not held by the expected owner") + } +} + +func (l *LockFile) AssertLockedForWriting() { + // DO NOT provide a variant that returns the current lock state. + // + // The same caveats as for AssertLocked apply equally. + l.AssertLocked() // The current implementation does not distinguish between read and write locks. +} + +// GetLastWrite() returns a LastWrite value corresponding to current state of the lock. +// This is typically called before (_not after_) loading the state when initializing a consumer +// of the data protected by the lock. +// During the lifetime of the consumer, the consumer should usually call ModifiedSince instead. +// +// The caller must hold the lock (for reading or writing) before this function is called. +func (l *LockFile) GetLastWrite() (LastWrite, error) { + l.AssertLocked() + return LastWrite{}, nil } -func (l *lockfile) Modified() (bool, error) { +// RecordWrite updates the lock with a new LastWrite value, and returns the new value. +// +// If this function fails, the LastWriter value of the lock is indeterminate; +// the caller should keep using the previously-recorded LastWrite value, +// and possibly detecting its own modification as an external one: +// +// lw, err := state.lock.RecordWrite() +// if err != nil { /* fail */ } +// state.lastWrite = lw +// +// The caller must hold the lock for writing. +func (l *LockFile) RecordWrite() (LastWrite, error) { + return LastWrite{}, nil +} + +// ModifiedSince checks if the lock has been changed since a provided LastWrite value, +// and returns the one to record instead. +// +// If ModifiedSince reports no modification, the previous LastWrite value +// is still valid and can continue to be used. +// +// If this function fails, the LastWriter value of the lock is indeterminate; +// the caller should fail and keep using the previously-recorded LastWrite value, +// so that it continues failing until the situation is resolved. Similarly, +// it should only update the recorded LastWrite value after processing the update: +// +// lw2, modified, err := state.lock.ModifiedSince(state.lastWrite) +// if err != nil { /* fail */ } +// state.lastWrite = lw2 +// if modified { +// if err := reload(); err != nil { /* fail */ } +// state.lastWrite = lw2 +// } +// +// The caller must hold the lock (for reading or writing). +func (l *LockFile) ModifiedSince(previous LastWrite) (LastWrite, bool, error) { + return LastWrite{}, false, nil +} + +// Deprecated: Use *LockFile.ModifiedSince. +func (l *LockFile) Modified() (bool, error) { return false, nil } -func (l *lockfile) Touch() error { + +// Deprecated: Use *LockFile.RecordWrite. +func (l *LockFile) Touch() error { return nil } -func (l *lockfile) IsReadWrite() bool { +func (l *LockFile) IsReadWrite() bool { return false } -func (l *lockfile) TouchedSince(when time.Time) bool { +func (l *LockFile) TouchedSince(when time.Time) bool { stat, err := os.Stat(l.file) if err != nil { return true diff --git a/vendor/github.com/containers/storage/pkg/loopback/attach_loopback.go b/vendor/github.com/containers/storage/pkg/loopback/attach_loopback.go index 6f072650..de10e332 100644 --- a/vendor/github.com/containers/storage/pkg/loopback/attach_loopback.go +++ b/vendor/github.com/containers/storage/pkg/loopback/attach_loopback.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package loopback diff --git a/vendor/github.com/containers/storage/pkg/loopback/ioctl.go b/vendor/github.com/containers/storage/pkg/loopback/ioctl.go index ea684195..da2ba46f 100644 --- a/vendor/github.com/containers/storage/pkg/loopback/ioctl.go +++ b/vendor/github.com/containers/storage/pkg/loopback/ioctl.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package loopback diff --git a/vendor/github.com/containers/storage/pkg/loopback/loop_wrapper.go b/vendor/github.com/containers/storage/pkg/loopback/loop_wrapper.go index a50de7f0..21a98100 100644 --- a/vendor/github.com/containers/storage/pkg/loopback/loop_wrapper.go +++ b/vendor/github.com/containers/storage/pkg/loopback/loop_wrapper.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package loopback diff --git a/vendor/github.com/containers/storage/pkg/loopback/loopback.go b/vendor/github.com/containers/storage/pkg/loopback/loopback.go index c9be0577..ec428724 100644 --- a/vendor/github.com/containers/storage/pkg/loopback/loopback.go +++ b/vendor/github.com/containers/storage/pkg/loopback/loopback.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package loopback diff --git a/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go b/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go index 1d1afeee..a2a1d407 100644 --- a/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go +++ b/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go @@ -1,16 +1,29 @@ +//go:build !windows // +build !windows package mount -import "golang.org/x/sys/unix" +import ( + "time" + + "golang.org/x/sys/unix" +) func unmount(target string, flags int) error { - err := unix.Unmount(target, flags) - if err == nil || err == unix.EINVAL { - // Ignore "not mounted" error here. Note the same error - // can be returned if flags are invalid, so this code - // assumes that the flags value is always correct. - return nil + var err error + 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 + // can be returned if flags are invalid, so this code + // assumes that the flags value is always correct. + return nil + } + break } return &mountError{ diff --git a/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go b/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go index eebc4ab8..d3a0cf51 100644 --- a/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package mount diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel.go index 7738fc74..6406cb14 100644 --- a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel.go +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // Package kernel provides helper function to get, parse and compare kernel diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go index 20d67f78..645790da 100644 --- a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_darwin.go @@ -43,7 +43,7 @@ func getRelease() (string, error) { prettyNames, err := shellwords.Parse(content[1]) 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 { diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_unix.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_unix.go index 7a68bc39..ed8cca2c 100644 --- a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_unix.go +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd || solaris || openbsd // +build linux freebsd solaris openbsd // Package kernel provides helper function to get, parse and compare kernel diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_windows.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_windows.go index 3d382923..b30da9fa 100644 --- a/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_windows.go +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/kernel_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package kernel diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_freebsd.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_freebsd.go new file mode 100644 index 00000000..e913fad0 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_freebsd.go @@ -0,0 +1,17 @@ +package kernel + +import "golang.org/x/sys/unix" + +// Utsname represents the system name structure. +// It is passthrough for unix.Utsname in order to make it portable with +// other platforms where it is not available. +type Utsname unix.Utsname + +func uname() (*unix.Utsname, error) { + uts := &unix.Utsname{} + + if err := unix.Uname(uts); err != nil { + return nil, err + } + return uts, nil +} diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported.go index 1da3f239..12671db5 100644 --- a/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported.go @@ -1,18 +1,14 @@ -// +build !linux,!solaris +//go:build openbsd +// +build openbsd package kernel import ( - "errors" + "fmt" + "runtime" ) -// Utsname represents the system name structure. -// It is defined here to make it portable as it is available on linux but not -// on windows. -type Utsname struct { - Release [65]byte -} - +// A stub called by kernel_unix.go . func uname() (*Utsname, error) { - return nil, errors.New("Kernel version detection is available only on linux") + return nil, fmt.Errorf("Kernel version detection is not available on %s", runtime.GOOS) } diff --git a/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported_type.go b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported_type.go new file mode 100644 index 00000000..f515500c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/parsers/kernel/uname_unsupported_type.go @@ -0,0 +1,11 @@ +//go:build !linux && !solaris && !freebsd +// +build !linux,!solaris,!freebsd + +package kernel + +// Utsname represents the system name structure. +// It is defined here to make it portable as it is available on linux but not +// on windows. +type Utsname struct { + Release [65]byte +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go b/vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go index 6f63ae99..32d6a9f4 100644 --- a/vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go +++ b/vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go @@ -1,3 +1,4 @@ +//go:build freebsd // +build freebsd package reexec diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_linux.go b/vendor/github.com/containers/storage/pkg/reexec/command_linux.go index d3dd86d3..87b43ed9 100644 --- a/vendor/github.com/containers/storage/pkg/reexec/command_linux.go +++ b/vendor/github.com/containers/storage/pkg/reexec/command_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package reexec diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go b/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go index 5b3605f3..77c93b4a 100644 --- a/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd && !solaris && !darwin // +build !linux,!windows,!freebsd,!solaris,!darwin package reexec diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_windows.go b/vendor/github.com/containers/storage/pkg/reexec/command_windows.go index d8685647..c46125eb 100644 --- a/vendor/github.com/containers/storage/pkg/reexec/command_windows.go +++ b/vendor/github.com/containers/storage/pkg/reexec/command_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package reexec diff --git a/vendor/github.com/containers/storage/pkg/regexp/regexp.go b/vendor/github.com/containers/storage/pkg/regexp/regexp.go new file mode 100644 index 00000000..ec879710 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/regexp/regexp.go @@ -0,0 +1,214 @@ +package regexp + +import ( + "io" + "regexp" + "sync" +) + +// Regexp is a wrapper struct used for wrapping MustCompile regex expressions +// used as global variables. Using this stucture helps speed the startup time +// of apps that want to use global regex variables. This library initializes them on +// first use as opposed to the start of the executable. +type Regexp struct { + once sync.Once + regexp *regexp.Regexp + val string +} + +func Delayed(val string) Regexp { + re := Regexp{ + val: val, + } + if precompile { + re.regexp = regexp.MustCompile(re.val) + } + return re +} + +func (re *Regexp) compile() { + if precompile { + return + } + re.once.Do(func() { + re.regexp = regexp.MustCompile(re.val) + }) +} + +func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte { + re.compile() + return re.regexp.Expand(dst, template, src, match) +} + +func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte { + re.compile() + return re.regexp.ExpandString(dst, template, src, match) +} +func (re *Regexp) Find(b []byte) []byte { + re.compile() + return re.regexp.Find(b) +} + +func (re *Regexp) FindAll(b []byte, n int) [][]byte { + re.compile() + return re.regexp.FindAll(b, n) +} + +func (re *Regexp) FindAllIndex(b []byte, n int) [][]int { + re.compile() + return re.regexp.FindAllIndex(b, n) +} + +func (re *Regexp) FindAllString(s string, n int) []string { + re.compile() + return re.regexp.FindAllString(s, n) +} + +func (re *Regexp) FindAllStringIndex(s string, n int) [][]int { + re.compile() + return re.regexp.FindAllStringIndex(s, n) +} + +func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string { + re.compile() + return re.regexp.FindAllStringSubmatch(s, n) +} + +func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { + re.compile() + return re.regexp.FindAllStringSubmatchIndex(s, n) +} + +func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte { + re.compile() + return re.regexp.FindAllSubmatch(b, n) +} + +func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int { + re.compile() + return re.regexp.FindAllSubmatchIndex(b, n) +} + +func (re *Regexp) FindIndex(b []byte) (loc []int) { + re.compile() + return re.regexp.FindIndex(b) +} + +func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) { + re.compile() + return re.regexp.FindReaderIndex(r) +} + +func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int { + re.compile() + return re.regexp.FindReaderSubmatchIndex(r) +} + +func (re *Regexp) FindString(s string) string { + re.compile() + return re.regexp.FindString(s) +} + +func (re *Regexp) FindStringIndex(s string) (loc []int) { + re.compile() + return re.regexp.FindStringIndex(s) +} + +func (re *Regexp) FindStringSubmatch(s string) []string { + re.compile() + return re.regexp.FindStringSubmatch(s) +} + +func (re *Regexp) FindStringSubmatchIndex(s string) []int { + re.compile() + return re.regexp.FindStringSubmatchIndex(s) +} + +func (re *Regexp) FindSubmatch(b []byte) [][]byte { + re.compile() + return re.regexp.FindSubmatch(b) +} + +func (re *Regexp) FindSubmatchIndex(b []byte) []int { + re.compile() + return re.regexp.FindSubmatchIndex(b) +} + +func (re *Regexp) LiteralPrefix() (prefix string, complete bool) { + re.compile() + return re.regexp.LiteralPrefix() +} + +func (re *Regexp) Longest() { + re.compile() + re.regexp.Longest() +} + +func (re *Regexp) Match(b []byte) bool { + re.compile() + return re.regexp.Match(b) +} + +func (re *Regexp) MatchReader(r io.RuneReader) bool { + re.compile() + return re.regexp.MatchReader(r) +} +func (re *Regexp) MatchString(s string) bool { + re.compile() + return re.regexp.MatchString(s) +} + +func (re *Regexp) NumSubexp() int { + re.compile() + return re.regexp.NumSubexp() +} + +func (re *Regexp) ReplaceAll(src, repl []byte) []byte { + re.compile() + return re.regexp.ReplaceAll(src, repl) +} + +func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte { + re.compile() + return re.regexp.ReplaceAllFunc(src, repl) +} + +func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte { + re.compile() + return re.regexp.ReplaceAllLiteral(src, repl) +} + +func (re *Regexp) ReplaceAllLiteralString(src, repl string) string { + re.compile() + return re.regexp.ReplaceAllLiteralString(src, repl) +} + +func (re *Regexp) ReplaceAllString(src, repl string) string { + re.compile() + return re.regexp.ReplaceAllString(src, repl) +} + +func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string { + re.compile() + return re.regexp.ReplaceAllStringFunc(src, repl) +} + +func (re *Regexp) Split(s string, n int) []string { + re.compile() + return re.regexp.Split(s, n) +} + +func (re *Regexp) String() string { + re.compile() + return re.regexp.String() +} + +func (re *Regexp) SubexpIndex(name string) int { + re.compile() + return re.regexp.SubexpIndex(name) +} + +func (re *Regexp) SubexpNames() []string { + re.compile() + return re.regexp.SubexpNames() +} diff --git a/vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go b/vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go new file mode 100644 index 00000000..834dd943 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go @@ -0,0 +1,6 @@ +//go:build !regexp_precompile +// +build !regexp_precompile + +package regexp + +const precompile = false diff --git a/vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go b/vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go new file mode 100644 index 00000000..a5fe0dbc --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go @@ -0,0 +1,6 @@ +//go:build regexp_precompile +// +build regexp_precompile + +package regexp + +const precompile = true diff --git a/vendor/github.com/containers/storage/pkg/stringid/stringid.go b/vendor/github.com/containers/storage/pkg/stringid/stringid.go index 4c434f0e..3ae44fd8 100644 --- a/vendor/github.com/containers/storage/pkg/stringid/stringid.go +++ b/vendor/github.com/containers/storage/pkg/stringid/stringid.go @@ -9,18 +9,19 @@ import ( "math" "math/big" "math/rand" - "regexp" "strconv" "strings" "sync" "time" + + "github.com/containers/storage/pkg/regexp" ) const shortLen = 12 var ( - validShortID = regexp.MustCompile("^[a-f0-9]{12}$") - validHex = regexp.MustCompile(`^[a-f0-9]{64}$`) + validShortID = regexp.Delayed("^[a-f0-9]{12}$") + validHex = regexp.Delayed(`^[a-f0-9]{64}$`) rngLock sync.Mutex rng *rand.Rand // A RNG with seeding properties we control. It can only be accessed with randLock held. diff --git a/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go b/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go index 09d58bcb..a208a6b5 100644 --- a/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go b/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go index 45428c14..bfb621dc 100644 --- a/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go +++ b/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/init.go b/vendor/github.com/containers/storage/pkg/system/init.go index 17935088..05642f60 100644 --- a/vendor/github.com/containers/storage/pkg/system/init.go +++ b/vendor/github.com/containers/storage/pkg/system/init.go @@ -6,7 +6,7 @@ import ( "unsafe" ) -// Used by chtimes +// maxTime is used by chtimes. var maxTime time.Time func init() { diff --git a/vendor/github.com/containers/storage/pkg/system/lchflags_bsd.go b/vendor/github.com/containers/storage/pkg/system/lchflags_bsd.go new file mode 100644 index 00000000..4eaeb5d6 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lchflags_bsd.go @@ -0,0 +1,56 @@ +//go:build freebsd +// +build freebsd + +package system + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +// Flag values from +const ( + /* + * Definitions of flags stored in file flags word. + * + * Super-user and owner changeable flags. + */ + UF_SETTABLE uint32 = 0x0000ffff /* mask of owner changeable flags */ + UF_NODUMP uint32 = 0x00000001 /* do not dump file */ + UF_IMMUTABLE uint32 = 0x00000002 /* file may not be changed */ + UF_APPEND uint32 = 0x00000004 /* writes to file may only append */ + UF_OPAQUE uint32 = 0x00000008 /* directory is opaque wrt. union */ + UF_NOUNLINK uint32 = 0x00000010 /* file may not be removed or renamed */ + + UF_SYSTEM uint32 = 0x00000080 /* Windows system file bit */ + UF_SPARSE uint32 = 0x00000100 /* sparse file */ + UF_OFFLINE uint32 = 0x00000200 /* file is offline */ + UF_REPARSE uint32 = 0x00000400 /* Windows reparse point file bit */ + UF_ARCHIVE uint32 = 0x00000800 /* file needs to be archived */ + UF_READONLY uint32 = 0x00001000 /* Windows readonly file bit */ + /* This is the same as the MacOS X definition of UF_HIDDEN. */ + UF_HIDDEN uint32 = 0x00008000 /* file is hidden */ + + /* + * Super-user changeable flags. + */ + SF_SETTABLE uint32 = 0xffff0000 /* mask of superuser changeable flags */ + SF_ARCHIVED uint32 = 0x00010000 /* file is archived */ + SF_IMMUTABLE uint32 = 0x00020000 /* file may not be changed */ + SF_APPEND uint32 = 0x00040000 /* writes to file may only append */ + SF_NOUNLINK uint32 = 0x00100000 /* file may not be removed or renamed */ + SF_SNAPSHOT uint32 = 0x00200000 /* snapshot inode */ +) + +func Lchflags(path string, flags uint32) error { + p, err := unix.BytePtrFromString(path) + if err != nil { + return err + } + _, _, e1 := unix.Syscall(unix.SYS_LCHFLAGS, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + return e1 + } + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/lcow_unix.go b/vendor/github.com/containers/storage/pkg/system/lcow_unix.go index cff33bb4..42658c8b 100644 --- a/vendor/github.com/containers/storage/pkg/system/lcow_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/lcow_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/lstat_unix.go b/vendor/github.com/containers/storage/pkg/system/lstat_unix.go index e9d301f0..9b13e614 100644 --- a/vendor/github.com/containers/storage/pkg/system/lstat_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/lstat_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system @@ -14,7 +15,7 @@ import ( func Lstat(path string) (*StatT, error) { s := &syscall.Stat_t{} if err := syscall.Lstat(path, s); err != nil { - return nil, &os.PathError{"Lstat", path, err} + return nil, &os.PathError{Op: "Lstat", Path: path, Err: err} } return fromStatT(s) } diff --git a/vendor/github.com/containers/storage/pkg/system/mknod_windows.go b/vendor/github.com/containers/storage/pkg/system/mknod_windows.go index 2e863c02..c35b1b34 100644 --- a/vendor/github.com/containers/storage/pkg/system/mknod_windows.go +++ b/vendor/github.com/containers/storage/pkg/system/mknod_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/path_unix.go b/vendor/github.com/containers/storage/pkg/system/path_unix.go index f3762e69..ff01143e 100644 --- a/vendor/github.com/containers/storage/pkg/system/path_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/path_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/process_unix.go b/vendor/github.com/containers/storage/pkg/system/process_unix.go index a9a0dd75..7ee59d92 100644 --- a/vendor/github.com/containers/storage/pkg/system/process_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/process_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd || solaris || darwin // +build linux freebsd solaris darwin package system diff --git a/vendor/github.com/containers/storage/pkg/system/rm.go b/vendor/github.com/containers/storage/pkg/system/rm.go index 5d63dc74..60c7d8bd 100644 --- a/vendor/github.com/containers/storage/pkg/system/rm.go +++ b/vendor/github.com/containers/storage/pkg/system/rm.go @@ -1,6 +1,7 @@ package system import ( + "errors" "fmt" "os" "syscall" @@ -35,14 +36,24 @@ func EnsureRemoveAll(dir string) error { } for { - if err := resetFileFlags(dir); err != nil { - return fmt.Errorf("resetting file flags: %w", err) - } err := os.RemoveAll(dir) if err == nil { return nil } + // If the RemoveAll fails with a permission error, we + // may have immutable files so try to remove the + // immutable flag and redo the RemoveAll. + if errors.Is(err, syscall.EPERM) { + if err = resetFileFlags(dir); err != nil { + return fmt.Errorf("resetting file flags: %w", err) + } + err = os.RemoveAll(dir) + if err == nil { + return nil + } + } + pe, ok := err.(*os.PathError) if !ok { return err @@ -65,7 +76,7 @@ func EnsureRemoveAll(dir string) error { continue } - if pe.Err != syscall.EBUSY { + if !IsEBUSY(pe.Err) { return err } diff --git a/vendor/github.com/containers/storage/pkg/system/rm_freebsd.go b/vendor/github.com/containers/storage/pkg/system/rm_freebsd.go index 35896c11..39a5de7a 100644 --- a/vendor/github.com/containers/storage/pkg/system/rm_freebsd.go +++ b/vendor/github.com/containers/storage/pkg/system/rm_freebsd.go @@ -3,28 +3,13 @@ package system import ( "io/fs" "path/filepath" - "unsafe" - - "golang.org/x/sys/unix" ) -func lchflags(path string, flags int) (err error) { - p, err := unix.BytePtrFromString(path) - if err != nil { - return err - } - _, _, e1 := unix.Syscall(unix.SYS_LCHFLAGS, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - return e1 - } - return nil -} - // Reset file flags in a directory tree. This allows EnsureRemoveAll // to delete trees which have the immutable flag set. func resetFileFlags(dir string) error { return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { - if err := lchflags(path, 0); err != nil { + if err := Lchflags(path, 0); err != nil { return err } return nil diff --git a/vendor/github.com/containers/storage/pkg/system/stat_common.go b/vendor/github.com/containers/storage/pkg/system/stat_common.go new file mode 100644 index 00000000..e965c54c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_common.go @@ -0,0 +1,13 @@ +//go:build !freebsd +// +build !freebsd + +package system + +type platformStatT struct { +} + +// Flags return file flags if supported or zero otherwise +func (s StatT) Flags() uint32 { + _ = s.platformStatT // Silence warnings that StatT.platformStatT is unused (on these platforms) + return 0 +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go b/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go index 715f05b9..9c510468 100644 --- a/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go +++ b/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go @@ -2,12 +2,25 @@ package system import "syscall" +type platformStatT struct { + flags uint32 +} + +// Flags return file flags if supported or zero otherwise +func (s StatT) Flags() uint32 { + return s.flags +} + // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { - return &StatT{size: s.Size, + st := &StatT{size: s.Size, mode: uint32(s.Mode), uid: s.Uid, gid: s.Gid, rdev: uint64(s.Rdev), - mtim: s.Mtimespec}, nil + mtim: s.Mtimespec, + dev: s.Dev} + st.flags = s.Flags + st.dev = s.Dev + return st, nil } diff --git a/vendor/github.com/containers/storage/pkg/system/stat_linux.go b/vendor/github.com/containers/storage/pkg/system/stat_linux.go index af7af20f..e5dcba82 100644 --- a/vendor/github.com/containers/storage/pkg/system/stat_linux.go +++ b/vendor/github.com/containers/storage/pkg/system/stat_linux.go @@ -9,7 +9,8 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { uid: s.Uid, gid: s.Gid, rdev: uint64(s.Rdev), - mtim: s.Mtim}, nil + mtim: s.Mtim, + dev: uint64(s.Dev)}, nil } // FromStatT converts a syscall.Stat_t type to a system.Stat_t type diff --git a/vendor/github.com/containers/storage/pkg/system/stat_unix.go b/vendor/github.com/containers/storage/pkg/system/stat_unix.go index 2fac918b..47ae899f 100644 --- a/vendor/github.com/containers/storage/pkg/system/stat_unix.go +++ b/vendor/github.com/containers/storage/pkg/system/stat_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system @@ -17,6 +18,8 @@ type StatT struct { rdev uint64 size int64 mtim syscall.Timespec + dev uint64 + platformStatT } // Mode returns file's permission mode. @@ -49,6 +52,11 @@ func (s StatT) Mtim() syscall.Timespec { return s.mtim } +// Dev returns a unique identifier for owning filesystem +func (s StatT) Dev() uint64 { + return s.dev +} + // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. // diff --git a/vendor/github.com/containers/storage/pkg/system/stat_windows.go b/vendor/github.com/containers/storage/pkg/system/stat_windows.go index d3063605..81edaadb 100644 --- a/vendor/github.com/containers/storage/pkg/system/stat_windows.go +++ b/vendor/github.com/containers/storage/pkg/system/stat_windows.go @@ -11,6 +11,7 @@ type StatT struct { mode os.FileMode size int64 mtim time.Time + platformStatT } // Size returns file's size. @@ -42,6 +43,11 @@ func (s StatT) GID() uint32 { return 0 } +// Dev returns a unique identifier for owning filesystem +func (s StatT) Dev() uint64 { + return 0 +} + // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. // diff --git a/vendor/github.com/containers/storage/pkg/system/umask.go b/vendor/github.com/containers/storage/pkg/system/umask.go index 5a10eda5..ad0337db 100644 --- a/vendor/github.com/containers/storage/pkg/system/umask.go +++ b/vendor/github.com/containers/storage/pkg/system/umask.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/umask_windows.go b/vendor/github.com/containers/storage/pkg/system/umask_windows.go index 13f1de17..9497596a 100644 --- a/vendor/github.com/containers/storage/pkg/system/umask_windows.go +++ b/vendor/github.com/containers/storage/pkg/system/umask_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package system diff --git a/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go b/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go index 13971454..843ecdc5 100644 --- a/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !freebsd // +build !linux,!freebsd package system diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go index 221eb78b..8bd7acf1 100644 --- a/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !darwin // +build !linux,!darwin package system diff --git a/vendor/github.com/containers/storage/pkg/truncindex/truncindex.go b/vendor/github.com/containers/storage/pkg/truncindex/truncindex.go index 74776e65..8f073265 100644 --- a/vendor/github.com/containers/storage/pkg/truncindex/truncindex.go +++ b/vendor/github.com/containers/storage/pkg/truncindex/truncindex.go @@ -14,7 +14,7 @@ import ( var ( // ErrEmptyPrefix is an error returned if the prefix was empty. - ErrEmptyPrefix = errors.New("Prefix can't be empty") + ErrEmptyPrefix = errors.New("prefix can't be empty") // ErrIllegalChar is returned when a space is in the ID ErrIllegalChar = errors.New("illegal character: ' '") @@ -25,7 +25,7 @@ var ( // ErrAmbiguousPrefix is returned if the prefix was ambiguous // (multiple ids for the prefix). -type ErrAmbiguousPrefix struct { +type ErrAmbiguousPrefix struct { //nolint: errname prefix string } @@ -42,6 +42,7 @@ type TruncIndex struct { } // NewTruncIndex creates a new TruncIndex and initializes with a list of IDs. +// Invalid IDs are _silently_ ignored. func NewTruncIndex(ids []string) (idx *TruncIndex) { idx = &TruncIndex{ ids: make(map[string]struct{}), @@ -51,7 +52,7 @@ func NewTruncIndex(ids []string) (idx *TruncIndex) { trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)), } for _, id := range ids { - idx.addID(id) + _ = idx.addID(id) // Ignore invalid IDs. Duplicate IDs are not a problem. } return } @@ -132,7 +133,8 @@ func (idx *TruncIndex) Get(s string) (string, error) { func (idx *TruncIndex) Iterate(handler func(id string)) { idx.Lock() 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)) return nil }) diff --git a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go index 4f441c32..08dbc661 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go +++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package unshare diff --git a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go index a5005403..25054810 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go +++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go @@ -1,3 +1,4 @@ +//go:build linux && !cgo // +build linux,!cgo package unshare diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare.go b/vendor/github.com/containers/storage/pkg/unshare/unshare.go index c854fdf5..00f397f3 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.go @@ -5,18 +5,12 @@ import ( "os" "os/user" "sync" - - "github.com/sirupsen/logrus" ) var ( homeDirOnce sync.Once homeDirErr error homeDir string - - hasCapSysAdminOnce sync.Once - hasCapSysAdminRet bool - hasCapSysAdminErr error ) // HomeDir returns the home directory for the current user. @@ -36,14 +30,3 @@ func HomeDir() (string, error) { }) return homeDir, homeDirErr } - -func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname - if err != nil { - if format != "" { - logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err) - } else { - logrus.Errorf("%v", err) - } - os.Exit(1) - } -} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go index 01cf33bd..86ac12ec 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go @@ -1,3 +1,4 @@ +//go:build darwin // +build darwin package unshare diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go index 39bff651..21a43d38 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && gccgo // +build linux,cgo,gccgo package unshare diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go index 3fc36201..86922846 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go @@ -400,6 +400,12 @@ func hasFullUsersMappings() (bool, error) { return bytes.Contains(content, []byte("4294967295")), nil } +var ( + hasCapSysAdminOnce sync.Once + hasCapSysAdminRet bool + hasCapSysAdminErr error +) + // IsRootless tells us if we are running in rootless mode func IsRootless() bool { isRootlessOnce.Do(func() { @@ -445,10 +451,21 @@ type Runnable interface { Run() error } +func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname + if err != nil { + if format != "" { + logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err) + } else { + logrus.Errorf("%v", err) + } + os.Exit(1) + } +} + // MaybeReexecUsingUserNamespace re-exec the process in a new namespace func MaybeReexecUsingUserNamespace(evenForRoot bool) { // If we've already been through this once, no need to try again. - if os.Geteuid() == 0 && IsRootless() { + if os.Geteuid() == 0 && GetRootlessUID() > 0 { return } diff --git a/vendor/github.com/containers/storage/storage.conf b/vendor/github.com/containers/storage/storage.conf index 1294f6a9..6c419a95 100644 --- a/vendor/github.com/containers/storage/storage.conf +++ b/vendor/github.com/containers/storage/storage.conf @@ -32,6 +32,10 @@ graphroot = "/var/lib/containers/storage" # # rootless_storage_path = "$HOME/.local/share/containers/storage" +# Transient store mode makes all container metadata be saved in temporary storage +# (i.e. runroot above). This is faster, but doesn't persist across reboots. +# transient_store = true + [storage.options] # Storage options to be passed to underlying storage drivers diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index fb1faaa1..d208e0bf 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -20,6 +20,7 @@ import ( "github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/parsers" "github.com/containers/storage/pkg/stringutils" "github.com/containers/storage/pkg/system" @@ -27,6 +28,7 @@ import ( "github.com/hashicorp/go-multierror" digest "github.com/opencontainers/go-digest" "github.com/opencontainers/selinux/go-selinux/label" + "github.com/sirupsen/logrus" ) type updateNameOperation int @@ -49,57 +51,27 @@ var ( storesLock sync.Mutex ) -// ROFileBasedStore wraps up the methods of the various types of file-based -// data stores that we implement which are needed for both read-only and -// read-write files. -type ROFileBasedStore interface { - Locker - - // Load reloads the contents of the store from disk. It should be called - // with the lock held. - Load() error - - // ReloadIfChanged reloads the contents of the store from disk if it is changed. - ReloadIfChanged() error -} - -// RWFileBasedStore wraps up the methods of various types of file-based data -// stores that we implement using read-write files. -type RWFileBasedStore interface { - // Save saves the contents of the store to disk. It should be called with - // the lock held, and Touch() should be called afterward before releasing the - // lock. - Save() error -} - -// FileBasedStore wraps up the common methods of various types of file-based -// data stores that we implement. -type FileBasedStore interface { - ROFileBasedStore - RWFileBasedStore -} - -// ROMetadataStore wraps a method for reading metadata associated with an ID. -type ROMetadataStore interface { +// roMetadataStore wraps a method for reading metadata associated with an ID. +type roMetadataStore interface { // Metadata reads metadata associated with an item with the specified ID. Metadata(id string) (string, error) } -// RWMetadataStore wraps a method for setting metadata associated with an ID. -type RWMetadataStore interface { +// rwMetadataStore wraps a method for setting metadata associated with an ID. +type rwMetadataStore interface { // SetMetadata updates the metadata associated with the item with the specified ID. SetMetadata(id, metadata string) error } -// MetadataStore wraps up methods for getting and setting metadata associated with IDs. -type MetadataStore interface { - ROMetadataStore - RWMetadataStore +// metadataStore wraps up methods for getting and setting metadata associated with IDs. +type metadataStore interface { + roMetadataStore + rwMetadataStore } -// An ROBigDataStore wraps up the read-only big-data related methods of the +// An roBigDataStore wraps up the read-only big-data related methods of the // various types of file-based lookaside stores that we implement. -type ROBigDataStore interface { +type roBigDataStore interface { // BigData retrieves a (potentially large) piece of data associated with // this ID, if it has previously been set. BigData(id, key string) ([]byte, error) @@ -117,8 +89,8 @@ type ROBigDataStore interface { BigDataNames(id string) ([]string, error) } -// A RWImageBigDataStore wraps up how we store big-data associated with images. -type RWImageBigDataStore interface { +// A rwImageBigDataStore wraps up how we store big-data associated with images. +type rwImageBigDataStore interface { // SetBigData stores a (potentially large) piece of data associated // with this ID. // Pass github.com/containers/image/manifest.Digest as digestManifest @@ -126,16 +98,16 @@ type RWImageBigDataStore interface { SetBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error } -// A ContainerBigDataStore wraps up how we store big-data associated with containers. -type ContainerBigDataStore interface { - ROBigDataStore +// A containerBigDataStore wraps up how we store big-data associated with containers. +type containerBigDataStore interface { + roBigDataStore // SetBigData stores a (potentially large) piece of data associated // with this ID. SetBigData(id, key string, data []byte) error } -// A ROLayerBigDataStore wraps up how we store RO big-data associated with layers. -type ROLayerBigDataStore interface { +// A roLayerBigDataStore wraps up how we store RO big-data associated with layers. +type roLayerBigDataStore interface { // SetBigData stores a (potentially large) piece of data associated // with this ID. BigData(id, key string) (io.ReadCloser, error) @@ -145,21 +117,15 @@ type ROLayerBigDataStore interface { BigDataNames(id string) ([]string, error) } -// A RWLayerBigDataStore wraps up how we store big-data associated with layers. -type RWLayerBigDataStore interface { +// A rwLayerBigDataStore wraps up how we store big-data associated with layers. +type rwLayerBigDataStore interface { // SetBigData stores a (potentially large) piece of data associated // with this ID. SetBigData(id, key string, data io.Reader) error } -// A LayerBigDataStore wraps up how we store big-data associated with layers. -type LayerBigDataStore interface { - ROLayerBigDataStore - RWLayerBigDataStore -} - -// A FlaggableStore can have flags set and cleared on items which it manages. -type FlaggableStore interface { +// A flaggableStore can have flags set and cleared on items which it manages. +type flaggableStore interface { // ClearFlag removes a named flag from an item in the store. ClearFlag(id string, flag string) error @@ -176,6 +142,7 @@ type Store interface { // settings that were passed to GetStore() when the object was created. RunRoot() string GraphRoot() string + TransientStore() bool GraphDriverName() string GraphOptions() []string PullOptions() map[string]string @@ -280,6 +247,8 @@ type Store interface { // Unmount attempts to unmount an image, given an ID. // Returns whether or not the layer is still mounted. + // WARNING: The return value may already be obsolete by the time it is available + // to the caller, so it can be used for heuristic sanity checks at best. It should almost always be ignored. UnmountImage(id string, force bool) (bool, error) // Mount attempts to mount a layer, image, or container for access, and @@ -298,9 +267,15 @@ type Store interface { // Unmount attempts to unmount a layer, image, or container, given an ID, a // name, or a mount path. Returns whether or not the layer is still mounted. + // WARNING: The return value may already be obsolete by the time it is available + // to the caller, so it can be used for heuristic sanity checks at best. It should almost always be ignored. Unmount(id string, force bool) (bool, error) // Mounted returns number of times the layer has been mounted. + // + // WARNING: This value might already be obsolete by the time it is returned; + // In situations where concurrent mount/unmount attempts can happen, this field + // should not be used for any decisions, maybe apart from heuristic user warnings. Mounted(id string) (int, error) // Changes returns a summary of the changes which would need to be made @@ -537,6 +512,11 @@ type Store interface { // Releasing AdditionalLayer handler is caller's responsibility. // This API is experimental and can be changed without bumping the major version number. LookupAdditionalLayer(d digest.Digest, imageref string) (AdditionalLayer, error) + + // Tries to clean up remainders of previous containers or layers that are not + // references in the json files. These can happen in the case of unclean + // shutdowns or regular restarts in transient store mode. + GarbageCollect() error } // AdditionalLayer reprents a layer that is contained in the additional layer store @@ -580,6 +560,8 @@ type LayerOptions struct { // and reliably known by the caller. // Use the default "" if this fields is not applicable or the value is not known. UncompressedDigest digest.Digest + // True is the layer info can be treated as volatile + Volatile bool } // ImageOptions is used for passing options to a Store's CreateImage() method. @@ -606,29 +588,57 @@ type ContainerOptions struct { } type store struct { - lastLoaded time.Time - runRoot string - graphLock Locker - usernsLock Locker + // # Locking hierarchy: + // These locks do not all need to be held simultaneously, but if some code does need to lock more than one, it MUST do so in this order: + // - graphLock + // - layerStore.start{Reading,Writing} + // - roLayerStores[].startReading (in the order of the items of the roLayerStores array) + // - imageStore.start{Reading,Writing} + // - roImageStores[].startReading (in the order of the items of the roImageStores array) + // - containerStore.start{Reading,Writing} + + // The following fields are only set when constructing store, and must never be modified afterwards. + // They are safe to access without any other locking. + runRoot string + graphDriverName string // Initially set to the user-requested value, possibly ""; updated during store construction, and does not change afterwards. + graphDriverPriority []string + // graphLock: + // - Ensures that we always reload graphDriver, and the primary layer store, after any process does store.Shutdown. This is necessary + // because (??) the Shutdown may forcibly unmount and clean up, affecting graph driver state in a way only a graph driver + // and layer store reinitialization can notice. + // - Ensures that store.Shutdown is exclusive with mount operations. This is necessary at because some + // graph drivers call mount.MakePrivate() during initialization, the mount operations require that, and the driver’s Cleanup() method + // may undo that. So, holding graphLock is required throughout the duration of Shutdown(), and the duration of any mount + // (but not unmount) calls. + // - Within this store object, protects access to some related in-memory state. + graphLock *lockfile.LockFile + usernsLock *lockfile.LockFile graphRoot string - graphDriverName string graphOptions []string pullOptions map[string]string uidMap []idtools.IDMap gidMap []idtools.IDMap autoUsernsUser string - additionalUIDs *idSet // Set by getAvailableIDs() - additionalGIDs *idSet // Set by getAvailableIDs() autoNsMinSize uint32 autoNsMaxSize uint32 - graphDriver drivers.Driver - layerStore LayerStore - roLayerStores []ROLayerStore - imageStore ImageStore - roImageStores []ROImageStore - containerStore ContainerStore + imageStore rwImageStore + roImageStores []roImageStore + containerStore rwContainerStore digestLockRoot string disableVolatile bool + transientStore bool + + // The following fields can only be accessed with graphLock held. + graphLockLastWrite lockfile.LastWrite + // FIXME: This field is only set when holding graphLock, but locking rules of the driver + // interface itself are not documented here. It is extensively used without holding graphLock. + graphDriver drivers.Driver + layerStoreUseGetters rwLayerStore // Almost all users should use the provided accessors instead of accessing this field directly. + roLayerStoresUseGetters []roLayerStore // Almost all users should use the provided accessors instead of accessing this field directly. + + // FIXME: The following fields need locking, and don’t have it. + additionalUIDs *idSet // Set by getAvailableIDs() + additionalGIDs *idSet // Set by getAvailableIDs() } // GetStore attempts to find an already-created Store object matching the @@ -699,18 +709,16 @@ func GetStore(options types.StoreOptions) (Store, error) { if err := os.MkdirAll(options.GraphRoot, 0700); err != nil { return nil, err } - for _, subdir := range []string{"mounts", "tmp", options.GraphDriverName} { - if err := os.MkdirAll(filepath.Join(options.GraphRoot, subdir), 0700); err != nil { - return nil, err - } + if err := os.MkdirAll(filepath.Join(options.GraphRoot, options.GraphDriverName), 0700); err != nil { + return nil, err } - graphLock, err := GetLockfile(filepath.Join(options.GraphRoot, "storage.lock")) + graphLock, err := lockfile.GetLockFile(filepath.Join(options.GraphRoot, "storage.lock")) if err != nil { return nil, err } - usernsLock, err := GetLockfile(filepath.Join(options.GraphRoot, "userns.lock")) + usernsLock, err := lockfile.GetLockFile(filepath.Join(options.GraphRoot, "userns.lock")) if err != nil { return nil, err } @@ -724,21 +732,24 @@ func GetStore(options types.StoreOptions) (Store, error) { autoNsMaxSize = AutoUserNsMaxSize } s := &store{ - runRoot: options.RunRoot, - graphLock: graphLock, - graphRoot: options.GraphRoot, - graphDriverName: options.GraphDriverName, - graphOptions: options.GraphDriverOptions, - uidMap: copyIDMap(options.UIDMap), - gidMap: copyIDMap(options.GIDMap), - autoUsernsUser: options.RootAutoNsUser, - autoNsMinSize: autoNsMinSize, - autoNsMaxSize: autoNsMaxSize, - additionalUIDs: nil, - additionalGIDs: nil, - usernsLock: usernsLock, - disableVolatile: options.DisableVolatile, - pullOptions: options.PullOptions, + runRoot: options.RunRoot, + graphDriverName: options.GraphDriverName, + graphDriverPriority: options.GraphDriverPriority, + graphLock: graphLock, + usernsLock: usernsLock, + graphRoot: options.GraphRoot, + graphOptions: options.GraphDriverOptions, + pullOptions: options.PullOptions, + uidMap: copyIDMap(options.UIDMap), + gidMap: copyIDMap(options.GIDMap), + autoUsernsUser: options.RootAutoNsUser, + autoNsMinSize: autoNsMinSize, + autoNsMaxSize: autoNsMaxSize, + disableVolatile: options.DisableVolatile, + transientStore: options.TransientStore, + + additionalUIDs: nil, + additionalGIDs: nil, } if err := s.load(); err != nil { return nil, err @@ -785,6 +796,10 @@ func (s *store) GraphRoot() string { return s.graphRoot } +func (s *store) TransientStore() bool { + return s.transientStore +} + func (s *store) GraphOptions() []string { return s.graphOptions } @@ -805,13 +820,27 @@ func (s *store) GIDMap() []idtools.IDMap { return copyIDMap(s.gidMap) } +// This must only be called when constructing store; it writes to fields that are assumed to be constant after constrution. func (s *store) load() error { - driver, err := s.GraphDriver() - if err != nil { + var driver drivers.Driver + if err := func() error { // A scope for defer + s.graphLock.Lock() + defer s.graphLock.Unlock() + lastWrite, err := s.graphLock.GetLastWrite() + if err != nil { + return err + } + s.graphLockLastWrite = lastWrite + driver, err = s.createGraphDriverLocked() + if err != nil { + return err + } + s.graphDriver = driver + s.graphDriverName = driver.String() + return nil + }(); err != nil { return err } - s.graphDriver = driver - s.graphDriverName = driver.String() driverPrefix := s.graphDriverName + "-" gipath := filepath.Join(s.graphRoot, driverPrefix+"images") @@ -823,22 +852,21 @@ func (s *store) load() error { return err } s.imageStore = ris - if _, err := s.ROImageStores(); err != nil { - return err - } gcpath := filepath.Join(s.graphRoot, driverPrefix+"containers") if err := os.MkdirAll(gcpath, 0700); err != nil { return err } - rcs, err := newContainerStore(gcpath) - if err != nil { - return err - } rcpath := filepath.Join(s.runRoot, driverPrefix+"containers") if err := os.MkdirAll(rcpath, 0700); err != nil { return err } + + rcs, err := newContainerStore(gcpath, rcpath, s.transientStore) + if err != nil { + return err + } + s.containerStore = rcs for _, store := range driver.AdditionalImageStores() { @@ -860,57 +888,86 @@ func (s *store) load() error { // GetDigestLock returns a digest-specific Locker. func (s *store) GetDigestLock(d digest.Digest) (Locker, error) { - return GetLockfile(filepath.Join(s.digestLockRoot, d.String())) + return lockfile.GetLockFile(filepath.Join(s.digestLockRoot, d.String())) } -func (s *store) getGraphDriver() (drivers.Driver, error) { - if s.graphDriver != nil { - return s.graphDriver, nil +// startUsingGraphDriver obtains s.graphLock and ensures that s.graphDriver is set and fresh. +// It only intended to be used on a fully-constructed store. +// If this succeeds, the caller MUST call stopUsingGraphDriver(). +func (s *store) startUsingGraphDriver() error { + s.graphLock.Lock() + succeeded := false + defer func() { + if !succeeded { + s.graphLock.Unlock() + } + }() + + lastWrite, modified, err := s.graphLock.ModifiedSince(s.graphLockLastWrite) + if err != nil { + return err } + if modified { + driver, err := s.createGraphDriverLocked() + if err != nil { + return err + } + // Our concurrency design requires s.graphDriverName not to be modified after + // store is constructed. + // It’s fine for driver.String() not to match the requested graph driver name + // (e.g. if the user asks for overlay2 and gets overlay), but it must be an idempotent + // mapping: + // driver1 := drivers.New(userInput, config) + // name1 := driver1.String() + // name2 := drivers.New(name1, config).String() + // assert(name1 == name2) + if s.graphDriverName != driver.String() { + return fmt.Errorf("graph driver name changed from %q to %q during reload", + s.graphDriverName, driver.String()) + } + s.graphDriver = driver + s.layerStoreUseGetters = nil + s.graphLockLastWrite = lastWrite + } + + succeeded = true + return nil +} + +// stopUsingGraphDriver releases graphLock obtained by startUsingGraphDriver. +func (s *store) stopUsingGraphDriver() { + s.graphLock.Unlock() +} + +// createGraphDriverLocked creates a new instance of graph driver for s, and returns it. +// Almost all users should use startUsingGraphDriver instead. +// The caller must hold s.graphLock. +func (s *store) createGraphDriverLocked() (drivers.Driver, error) { config := drivers.Options{ - Root: s.graphRoot, - RunRoot: s.runRoot, - DriverOptions: s.graphOptions, - UIDMaps: s.uidMap, - GIDMaps: s.gidMap, + Root: s.graphRoot, + RunRoot: s.runRoot, + DriverPriority: s.graphDriverPriority, + DriverOptions: s.graphOptions, + UIDMaps: s.uidMap, + GIDMaps: s.gidMap, } - driver, err := drivers.New(s.graphDriverName, config) - if err != nil { - return nil, err - } - s.graphDriver = driver - s.graphDriverName = driver.String() - return driver, nil + return drivers.New(s.graphDriverName, config) } func (s *store) GraphDriver() (drivers.Driver, error) { - s.graphLock.Lock() - defer s.graphLock.Unlock() - if s.graphLock.TouchedSince(s.lastLoaded) { - s.graphDriver = nil - s.layerStore = nil - s.lastLoaded = time.Now() + if err := s.startUsingGraphDriver(); err != nil { + return nil, err } - return s.getGraphDriver() + defer s.stopUsingGraphDriver() + return s.graphDriver, nil } -// LayerStore obtains and returns a handle to the writeable layer store object -// used by the Store. Accessing this store directly will bypass locking and -// synchronization, so it is not a part of the exported Store interface. -func (s *store) LayerStore() (LayerStore, error) { - s.graphLock.Lock() - defer s.graphLock.Unlock() - if s.graphLock.TouchedSince(s.lastLoaded) { - s.graphDriver = nil - s.layerStore = nil - s.lastLoaded = time.Now() - } - if s.layerStore != nil { - return s.layerStore, nil - } - driver, err := s.getGraphDriver() - if err != nil { - return nil, err +// getLayerStoreLocked obtains and returns a handle to the writeable layer store object +// used by the Store. +// It must be called with s.graphLock held. +func (s *store) getLayerStoreLocked() (rwLayerStore, error) { + if s.layerStoreUseGetters != nil { + return s.layerStoreUseGetters, nil } driverPrefix := s.graphDriverName + "-" rlpath := filepath.Join(s.runRoot, driverPrefix+"layers") @@ -921,76 +978,238 @@ func (s *store) LayerStore() (LayerStore, error) { if err := os.MkdirAll(glpath, 0700); err != nil { return nil, err } - rls, err := s.newLayerStore(rlpath, glpath, driver) + rls, err := s.newLayerStore(rlpath, glpath, s.graphDriver, s.transientStore) if err != nil { return nil, err } - s.layerStore = rls - return s.layerStore, nil + s.layerStoreUseGetters = rls + return s.layerStoreUseGetters, nil } -// ROLayerStores obtains additional read/only layer store objects used by the -// Store. Accessing these stores directly will bypass locking and -// synchronization, so it is not part of the exported Store interface. -func (s *store) ROLayerStores() ([]ROLayerStore, error) { - s.graphLock.Lock() - defer s.graphLock.Unlock() - if s.roLayerStores != nil { - return s.roLayerStores, nil - } - driver, err := s.getGraphDriver() - if err != nil { +// getLayerStore obtains and returns a handle to the writeable layer store object +// used by the store. +// It must be called WITHOUT s.graphLock held. +func (s *store) getLayerStore() (rwLayerStore, error) { + if err := s.startUsingGraphDriver(); err != nil { return nil, err } + defer s.stopUsingGraphDriver() + return s.getLayerStoreLocked() +} + +// getROLayerStoresLocked obtains additional read/only layer store objects used by the +// Store. +// It must be called with s.graphLock held. +func (s *store) getROLayerStoresLocked() ([]roLayerStore, error) { + if s.roLayerStoresUseGetters != nil { + return s.roLayerStoresUseGetters, nil + } driverPrefix := s.graphDriverName + "-" rlpath := filepath.Join(s.runRoot, driverPrefix+"layers") if err := os.MkdirAll(rlpath, 0700); err != nil { return nil, err } - for _, store := range driver.AdditionalImageStores() { + for _, store := range s.graphDriver.AdditionalImageStores() { glpath := filepath.Join(store, driverPrefix+"layers") - rls, err := newROLayerStore(rlpath, glpath, driver) + rls, err := newROLayerStore(rlpath, glpath, s.graphDriver) if err != nil { return nil, err } - s.roLayerStores = append(s.roLayerStores, rls) + s.roLayerStoresUseGetters = append(s.roLayerStoresUseGetters, rls) + } + return s.roLayerStoresUseGetters, nil +} + +// bothLayerStoreKindsLocked returns the primary, and additional read-only, layer store objects used by the store. +// It must be called with s.graphLock held. +func (s *store) bothLayerStoreKindsLocked() (rwLayerStore, []roLayerStore, error) { + primary, err := s.getLayerStoreLocked() + if err != nil { + return nil, nil, fmt.Errorf("loading primary layer store data: %w", err) + } + additional, err := s.getROLayerStoresLocked() + if err != nil { + return nil, nil, fmt.Errorf("loading additional layer stores: %w", err) + } + return primary, additional, nil +} + +// bothLayerStoreKinds returns the primary, and additional read-only, layer store objects used by the store. +// It must be called with s.graphLock held. +func (s *store) bothLayerStoreKinds() (rwLayerStore, []roLayerStore, error) { + if err := s.startUsingGraphDriver(); err != nil { + return nil, nil, err + } + defer s.stopUsingGraphDriver() + return s.bothLayerStoreKindsLocked() +} + +// allLayerStores returns a list of all layer store objects used by the Store. +// This is a convenience method for read-only users of the Store. +// It must be called with s.graphLock held. +func (s *store) allLayerStoresLocked() ([]roLayerStore, error) { + primary, additional, err := s.bothLayerStoreKindsLocked() + if err != nil { + return nil, err + } + return append([]roLayerStore{primary}, additional...), nil +} + +// allLayerStores returns a list of all layer store objects used by the Store. +// This is a convenience method for read-only users of the Store. +// It must be called WITHOUT s.graphLock held. +func (s *store) allLayerStores() ([]roLayerStore, error) { + if err := s.startUsingGraphDriver(); err != nil { + return nil, err + } + defer s.stopUsingGraphDriver() + return s.allLayerStoresLocked() +} + +// readAllLayerStores processes allLayerStores() in order: +// It locks the store for reading, checks for updates, and calls +// +// (done, err) := fn(store) +// +// until the callback returns done == true, and returns the data from the callback. +// +// If reading any layer store fails, it immediately returns (true, err). +// +// If all layer stores are processed without setting done == true, it returns (false, nil). +// +// Typical usage: +// +// var res T = failureValue +// if done, err := s.readAllLayerStores(store, func(…) { +// … +// }; done { +// return res, err +// } +func (s *store) readAllLayerStores(fn func(store roLayerStore) (bool, error)) (bool, error) { + layerStores, err := s.allLayerStores() + if err != nil { + return true, err + } + for _, s := range layerStores { + store := s + if err := store.startReading(); err != nil { + return true, err + } + defer store.stopReading() + if done, err := fn(store); done { + return true, err + } + } + return false, nil +} + +// writeToLayerStore is a helper for working with store.getLayerStore(): +// It locks the store for writing, checks for updates, and calls fn() +// It returns the return value of fn, or its own error initializing the store. +func (s *store) writeToLayerStore(fn func(store rwLayerStore) error) error { + store, err := s.getLayerStore() + if err != nil { + return err } - return s.roLayerStores, nil + + if err := store.startWriting(); err != nil { + return err + } + defer store.stopWriting() + return fn(store) +} + +// allImageStores returns a list of all image store objects used by the Store. +// This is a convenience method for read-only users of the Store. +func (s *store) allImageStores() []roImageStore { + return append([]roImageStore{s.imageStore}, s.roImageStores...) } -// ImageStore obtains and returns a handle to the writable image store object -// used by the Store. Accessing this store directly will bypass locking and -// synchronization, so it is not a part of the exported Store interface. -func (s *store) ImageStore() (ImageStore, error) { - if s.imageStore != nil { - return s.imageStore, nil +// readAllImageStores processes allImageStores() in order: +// It locks the store for reading, checks for updates, and calls +// +// (done, err) := fn(store) +// +// until the callback returns done == true, and returns the data from the callback. +// +// If reading any Image store fails, it immediately returns (true, err). +// +// If all Image stores are processed without setting done == true, it returns (false, nil). +// +// Typical usage: +// +// var res T = failureValue +// if done, err := s.readAllImageStores(store, func(…) { +// … +// }; done { +// return res, err +// } +func (s *store) readAllImageStores(fn func(store roImageStore) (bool, error)) (bool, error) { + for _, s := range s.allImageStores() { + store := s + if err := store.startReading(); err != nil { + return true, err + } + defer store.stopReading() + if done, err := fn(store); done { + return true, err + } } - return nil, ErrLoadError + return false, nil } -// ROImageStores obtains additional read/only image store objects used by the -// Store. Accessing these stores directly will bypass locking and -// synchronization, so it is not a part of the exported Store interface. -func (s *store) ROImageStores() ([]ROImageStore, error) { - if s.imageStore == nil { - return nil, ErrLoadError +// writeToImageStore is a convenience helper for working with store.getImageStore(): +// It locks the store for writing, checks for updates, and calls fn(), which can then access store.imageStore. +// It returns the return value of fn, or its own error initializing the store. +func (s *store) writeToImageStore(fn func() error) error { + if err := s.imageStore.startWriting(); err != nil { + return err } + defer s.imageStore.stopWriting() + return fn() +} - return s.roImageStores, nil +// writeToContainerStore is a convenience helper for working with store.getContainerStore(): +// It locks the store for writing, checks for updates, and calls fn(), which can then access store.containerStore. +// It returns the return value of fn, or its own error initializing the store. +func (s *store) writeToContainerStore(fn func() error) error { + if err := s.containerStore.startWriting(); err != nil { + return err + } + defer s.containerStore.stopWriting() + return fn() } -// ContainerStore obtains and returns a handle to the container store object -// used by the Store. Accessing this store directly will bypass locking and -// synchronization, so it is not a part of the exported Store interface. -func (s *store) ContainerStore() (ContainerStore, error) { - if s.containerStore != nil { - return s.containerStore, nil +// writeToAllStores is a convenience helper for writing to all three stores: +// It locks the stores for writing, checks for updates, and calls fn(), which can then access the provided layer store, +// s.imageStore and s.containerStore. +// It returns the return value of fn, or its own error initializing the stores. +func (s *store) writeToAllStores(fn func(rlstore rwLayerStore) error) error { + rlstore, err := s.getLayerStore() + if err != nil { + return err + } + + if err := rlstore.startWriting(); err != nil { + return err + } + defer rlstore.stopWriting() + if err := s.imageStore.startWriting(); err != nil { + return err + } + defer s.imageStore.stopWriting() + if err := s.containerStore.startWriting(); err != nil { + return err } - return nil, ErrLoadError + defer s.containerStore.stopWriting() + + return fn(rlstore) } -func (s *store) canUseShifting(uidmap, gidmap []idtools.IDMap) bool { - if s.graphDriver == nil || !s.graphDriver.SupportsShifting() { +// canUseShifting returns ??? +// store must be locked for writing. +func canUseShifting(store rwLayerStore, uidmap, gidmap []idtools.IDMap) bool { + if !store.supportsShifting() { return false } if uidmap != nil && !idtools.IsContiguous(uidmap) { @@ -1004,28 +1223,18 @@ func (s *store) canUseShifting(uidmap, gidmap []idtools.IDMap) bool { func (s *store) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions, diff io.Reader) (*Layer, int64, error) { var parentLayer *Layer - rlstore, err := s.LayerStore() - if err != nil { - return nil, -1, err - } - rlstores, err := s.ROLayerStores() + rlstore, rlstores, err := s.bothLayerStoreKinds() if err != nil { return nil, -1, err } - rcstore, err := s.ContainerStore() - if err != nil { - return nil, -1, err - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + if err := rlstore.startWriting(); err != nil { return nil, -1, err } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + defer rlstore.stopWriting() + if err := s.containerStore.startWriting(); err != nil { return nil, -1, err } + defer s.containerStore.stopWriting() if options == nil { options = &LayerOptions{} } @@ -1039,14 +1248,13 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w gidMap := options.GIDMap if parent != "" { var ilayer *Layer - for _, l := range append([]ROLayerStore{rlstore}, rlstores...) { + for _, l := range append([]roLayerStore{rlstore}, rlstores...) { lstore := l if lstore != rlstore { - lstore.RLock() - defer lstore.Unlock() - if err := lstore.ReloadIfChanged(); err != nil { + if err := lstore.startReading(); err != nil { return nil, -1, err } + defer lstore.stopReading() } if l, err := lstore.Get(parent); err == nil && l != nil { ilayer = l @@ -1058,7 +1266,7 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w return nil, -1, ErrLayerUnknown } parentLayer = ilayer - containers, err := rcstore.Containers() + containers, err := s.containerStore.Containers() if err != nil { return nil, -1, err } @@ -1085,7 +1293,7 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w OriginalDigest: options.OriginalDigest, UncompressedDigest: options.UncompressedDigest, } - if s.canUseShifting(uidMap, gidMap) { + if canUseShifting(rlstore, uidMap, gidMap) { layerOptions.IDMappingOptions = types.IDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil} } else { layerOptions.IDMappingOptions = types.IDMappingOptions{ @@ -1105,27 +1313,17 @@ func (s *store) CreateLayer(id, parent string, names []string, mountLabel string func (s *store) CreateImage(id string, names []string, layer, metadata string, options *ImageOptions) (*Image, error) { if layer != "" { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() + layerStores, err := s.allLayerStores() if err != nil { return nil, err } var ilayer *Layer - for _, s := range append([]ROLayerStore{lstore}, lstores...) { + for _, s := range layerStores { store := s - if store == lstore { - store.Lock() - } else { - store.RLock() - } - defer store.Unlock() - err := store.ReloadIfChanged() - if err != nil { + if err := store.startReading(); err != nil { return nil, err } + defer store.stopReading() ilayer, err = store.Get(layer) if err == nil { break @@ -1137,28 +1335,30 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o layer = ilayer.ID } - ristore, err := s.ImageStore() - if err != nil { - return nil, err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return nil, err - } - - creationDate := time.Now().UTC() - if options != nil && !options.CreationDate.IsZero() { - creationDate = options.CreationDate - } + var res *Image + err := s.writeToImageStore(func() error { + creationDate := time.Now().UTC() + if options != nil && !options.CreationDate.IsZero() { + creationDate = options.CreationDate + } - return ristore.Create(id, names, layer, metadata, creationDate, options.Digest) + var err error + res, err = s.imageStore.Create(id, names, layer, metadata, creationDate, options.Digest) + return err + }) + return res, err } -func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, createMappedLayer bool, rlstore LayerStore, lstores []ROLayerStore, options types.IDMappingOptions) (*Layer, error) { +// imageTopLayerForMapping does ??? +// On entry: +// - ristore must be locked EITHER for reading or writing +// - s.imageStore must be locked for writing; it might be identical to ristore. +// - rlstore must be locked for writing +// - lstores must all be locked for reading +func (s *store) imageTopLayerForMapping(image *Image, ristore roImageStore, rlstore rwLayerStore, lstores []roLayerStore, options types.IDMappingOptions) (*Layer, error) { layerMatchesMappingOptions := func(layer *Layer, options types.IDMappingOptions) bool { // If the driver supports shifting and the layer has no mappings, we can use it. - if s.canUseShifting(options.UIDMap, options.GIDMap) && len(layer.UIDMap) == 0 && len(layer.GIDMap) == 0 { + if canUseShifting(rlstore, options.UIDMap, options.GIDMap) && len(layer.UIDMap) == 0 && len(layer.GIDMap) == 0 { return true } // If we want host mapping, and the layer uses mappings, it's not the best match. @@ -1172,17 +1372,11 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, crea return reflect.DeepEqual(layer.UIDMap, options.UIDMap) && reflect.DeepEqual(layer.GIDMap, options.GIDMap) } var layer, parentLayer *Layer - allStores := append([]ROLayerStore{rlstore}, lstores...) + allStores := append([]roLayerStore{rlstore}, lstores...) // Locate the image's top layer and its parent, if it has one. + createMappedLayer := ristore == s.imageStore for _, s := range allStores { store := s - if store != rlstore { - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } - } // Walk the top layer list. for _, candidate := range append([]string{image.TopLayer}, image.MappedTopLayers...) { if cLayer, err := store.Get(candidate); err == nil { @@ -1228,44 +1422,41 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, crea return layer, nil } // The top layer's mappings don't match the ones we want, and it's in an image store - // that lets us edit image metadata... - if istore, ok := ristore.(*imageStore); ok { - // ... so create a duplicate of the layer with the desired mappings, and - // register it as an alternate top layer in the image. - var layerOptions LayerOptions - if s.canUseShifting(options.UIDMap, options.GIDMap) { - layerOptions = LayerOptions{ - IDMappingOptions: types.IDMappingOptions{ - HostUIDMapping: true, - HostGIDMapping: true, - UIDMap: nil, - GIDMap: nil, - }, - } - } else { - layerOptions = LayerOptions{ - IDMappingOptions: types.IDMappingOptions{ - HostUIDMapping: options.HostUIDMapping, - HostGIDMapping: options.HostGIDMapping, - UIDMap: copyIDMap(options.UIDMap), - GIDMap: copyIDMap(options.GIDMap), - }, - } + // that lets us edit image metadata, so create a duplicate of the layer with the desired + // mappings, and register it as an alternate top layer in the image. + var layerOptions LayerOptions + if canUseShifting(rlstore, options.UIDMap, options.GIDMap) { + layerOptions = LayerOptions{ + IDMappingOptions: types.IDMappingOptions{ + HostUIDMapping: true, + HostGIDMapping: true, + UIDMap: nil, + GIDMap: nil, + }, } - layerOptions.TemplateLayer = layer.ID - mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, nil) - if err != nil { - return nil, fmt.Errorf("creating an ID-mapped copy of layer %q: %w", layer.ID, err) + } else { + layerOptions = LayerOptions{ + IDMappingOptions: types.IDMappingOptions{ + HostUIDMapping: options.HostUIDMapping, + HostGIDMapping: options.HostGIDMapping, + UIDMap: copyIDMap(options.UIDMap), + GIDMap: copyIDMap(options.GIDMap), + }, } - if err = istore.addMappedTopLayer(image.ID, mappedLayer.ID); err != nil { - if err2 := rlstore.Delete(mappedLayer.ID); err2 != nil { - err = fmt.Errorf("deleting layer %q: %v: %w", mappedLayer.ID, err2, err) - } - return nil, fmt.Errorf("registering ID-mapped layer with image %q: %w", image.ID, err) + } + layerOptions.TemplateLayer = layer.ID + mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, nil) + if err != nil { + return nil, fmt.Errorf("creating an ID-mapped copy of layer %q: %w", layer.ID, err) + } + // By construction, createMappedLayer can only be true if ristore == s.imageStore. + if err = s.imageStore.addMappedTopLayer(image.ID, mappedLayer.ID); err != nil { + if err2 := rlstore.Delete(mappedLayer.ID); err2 != nil { + err = fmt.Errorf("deleting layer %q: %v: %w", mappedLayer.ID, err2, err) } - layer = mappedLayer + return nil, fmt.Errorf("registering ID-mapped layer with image %q: %w", image.ID, err) } - return layer, nil + return mappedLayer, nil } func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) { @@ -1278,7 +1469,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat if options.HostGIDMapping { options.GIDMap = nil } - rlstore, err := s.LayerStore() + rlstore, lstores, err := s.bothLayerStoreKinds() // lstores will be locked read-only if image != "" if err != nil { return nil, err } @@ -1295,45 +1486,41 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat defer s.usernsLock.Unlock() } - var imageHomeStore ROImageStore - var istore ImageStore - var istores []ROImageStore - var lstores []ROLayerStore - var cimage *Image + var imageHomeStore roImageStore // Set if image != "" + // s.imageStore is locked read-write, if image != "" + // s.roImageStores are NOT NECESSARILY ALL locked read-only if image != "" + var cimage *Image // Set if image != "" if image != "" { - var err error - lstores, err = s.ROLayerStores() - if err != nil { - return nil, err - } - istore, err = s.ImageStore() - if err != nil { + if err := rlstore.startWriting(); err != nil { return nil, err } - istores, err = s.ROImageStores() - if err != nil { - return nil, err - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { + defer rlstore.stopWriting() + for _, s := range lstores { store := s - if store == istore { - store.Lock() - } else { - store.RLock() - } - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { + if err := store.startReading(); err != nil { return nil, err } - cimage, err = store.Get(image) - if err == nil { - imageHomeStore = store - break + defer store.stopReading() + } + if err := s.imageStore.startWriting(); err != nil { + return nil, err + } + defer s.imageStore.stopWriting() + cimage, err = s.imageStore.Get(image) + if err == nil { + imageHomeStore = s.imageStore + } else { + for _, s := range s.roImageStores { + store := s + if err := store.startReading(); err != nil { + return nil, err + } + defer store.stopReading() + cimage, err = store.Get(image) + if err == nil { + imageHomeStore = store + break + } } } if cimage == nil { @@ -1344,7 +1531,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat if options.AutoUserNs { var err error - options.UIDMap, options.GIDMap, err = s.getAutoUserNS(&options.AutoUserNsOpts, cimage) + options.UIDMap, options.GIDMap, err = s.getAutoUserNS(&options.AutoUserNsOpts, cimage, rlstore, lstores) if err != nil { return nil, err } @@ -1356,8 +1543,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat idMappingsOptions := options.IDMappingOptions if image != "" { if cimage.TopLayer != "" { - createMappedLayer := imageHomeStore == istore - ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, createMappedLayer, rlstore, lstores, idMappingsOptions) + ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, rlstore, lstores, idMappingsOptions) if err != nil { return nil, err } @@ -1371,11 +1557,10 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat } } } else { - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + if err := rlstore.startWriting(); err != nil { return nil, err } + defer rlstore.stopWriting() if !options.HostUIDMapping && len(options.UIDMap) == 0 { uidMap = s.uidMap } @@ -1383,25 +1568,28 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat gidMap = s.gidMap } } - var layerOptions *LayerOptions - if s.canUseShifting(uidMap, gidMap) { - layerOptions = &LayerOptions{ - IDMappingOptions: types.IDMappingOptions{ + layerOptions := &LayerOptions{ + // Normally layers for containers are volatile only if the container is. + // But in transient store mode, all container layers are volatile. + Volatile: options.Volatile || s.transientStore, + } + if canUseShifting(rlstore, uidMap, gidMap) { + layerOptions.IDMappingOptions = + types.IDMappingOptions{ HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil, - }, - } + } } else { - layerOptions = &LayerOptions{ - IDMappingOptions: types.IDMappingOptions{ + layerOptions.IDMappingOptions = + types.IDMappingOptions{ HostUIDMapping: idMappingsOptions.HostUIDMapping, HostGIDMapping: idMappingsOptions.HostGIDMapping, UIDMap: copyIDMap(uidMap), GIDMap: copyIDMap(gidMap), - }, - } + } + } if options.Flags == nil { options.Flags = make(map[string]interface{}) @@ -1427,223 +1615,146 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat return nil, err } layer = clayer.ID - rcstore, err := s.ContainerStore() - if err != nil { - return nil, err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return nil, err - } - options.IDMappingOptions = types.IDMappingOptions{ - HostUIDMapping: len(options.UIDMap) == 0, - HostGIDMapping: len(options.GIDMap) == 0, - UIDMap: copyIDMap(options.UIDMap), - GIDMap: copyIDMap(options.GIDMap), - } - container, err := rcstore.Create(id, names, imageID, layer, metadata, options) - if err != nil || container == nil { - rlstore.Delete(layer) + + // Normally only `--rm` containers are volatile, but in transient store mode all containers are volatile + if s.transientStore { + options.Volatile = true } + + var container *Container + err = s.writeToContainerStore(func() error { + options.IDMappingOptions = types.IDMappingOptions{ + HostUIDMapping: len(options.UIDMap) == 0, + HostGIDMapping: len(options.GIDMap) == 0, + UIDMap: copyIDMap(options.UIDMap), + GIDMap: copyIDMap(options.GIDMap), + } + var err error + container, err = s.containerStore.Create(id, names, imageID, layer, metadata, options) + if err != nil || container == nil { + if err2 := rlstore.Delete(layer); err2 != nil { + if err == nil { + err = fmt.Errorf("deleting layer %#v: %w", layer, err2) + } else { + logrus.Errorf("While recovering from a failure to create a container, error deleting layer %#v: %v", layer, err2) + } + } + } + return err + }) return container, err } func (s *store) SetMetadata(id, metadata string) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - ristore, err := s.ImageStore() - if err != nil { - return err - } - rcstore, err := s.ContainerStore() - if err != nil { - return err - } + return s.writeToAllStores(func(rlstore rwLayerStore) error { + if rlstore.Exists(id) { + return rlstore.SetMetadata(id, metadata) + } + if s.imageStore.Exists(id) { + return s.imageStore.SetMetadata(id, metadata) + } + if s.containerStore.Exists(id) { + return s.containerStore.SetMetadata(id, metadata) + } + return ErrNotAnID + }) +} - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err +func (s *store) Metadata(id string) (string, error) { + var res string + + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if store.Exists(id) { + var err error + res, err = store.Metadata(id) + return true, err + } + return false, nil + }); done { + return res, err } - if rlstore.Exists(id) { - return rlstore.SetMetadata(id, metadata) + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if store.Exists(id) { + var err error + res, err = store.Metadata(id) + return true, err + } + return false, nil + }); done { + return res, err } - if ristore.Exists(id) { - return ristore.SetMetadata(id, metadata) + + if err := s.containerStore.startReading(); err != nil { + return "", err } - if rcstore.Exists(id) { - return rcstore.SetMetadata(id, metadata) + defer s.containerStore.stopReading() + if s.containerStore.Exists(id) { + return s.containerStore.Metadata(id) } - return ErrNotAnID -} - -func (s *store) Metadata(id string) (string, error) { - lstore, err := s.LayerStore() - if err != nil { - return "", err - } - lstores, err := s.ROLayerStores() - if err != nil { - return "", err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return "", err - } - if store.Exists(id) { - return store.Metadata(id) - } - } - - istore, err := s.ImageStore() - if err != nil { - return "", err - } - istores, err := s.ROImageStores() - if err != nil { - return "", err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return "", err - } - if store.Exists(id) { - return store.Metadata(id) - } - } - - cstore, err := s.ContainerStore() - if err != nil { - return "", err - } - cstore.RLock() - defer cstore.Unlock() - if err := cstore.ReloadIfChanged(); err != nil { - return "", err - } - if cstore.Exists(id) { - return cstore.Metadata(id) - } - return "", ErrNotAnID + return "", ErrNotAnID } func (s *store) ListImageBigData(id string) ([]string, error) { - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res []string + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { bigDataNames, err := store.BigDataNames(id) if err == nil { - return bigDataNames, err + res = bigDataNames + return true, nil } + return false, nil + }); done { + return res, err } return nil, fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } func (s *store) ImageBigDataSize(id, key string) (int64, error) { - istore, err := s.ImageStore() - if err != nil { - return -1, err - } - istores, err := s.ROImageStores() - if err != nil { - return -1, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return -1, err - } + var res int64 = -1 + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { size, err := store.BigDataSize(id, key) if err == nil { - return size, nil + res = size + return true, nil } + return false, nil + }); done { + return res, err } return -1, ErrSizeUnknown } func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) { - ristore, err := s.ImageStore() - if err != nil { - return "", err - } - stores, err := s.ROImageStores() - if err != nil { - return "", err - } - stores = append([]ROImageStore{ristore}, stores...) - for _, r := range stores { - ristore := r - ristore.RLock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return "", err - } + var res digest.Digest + if done, err := s.readAllImageStores(func(ristore roImageStore) (bool, error) { d, err := ristore.BigDataDigest(id, key) if err == nil && d.Validate() == nil { - return d, nil + res = d + return true, nil } + return false, nil + }); done { + return res, err } return "", ErrDigestUnknown } func (s *store) ImageBigData(id, key string) ([]byte, error) { - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } foundImage := false - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res []byte + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { data, err := store.BigData(id, key) if err == nil { - return data, nil + res = data + return true, nil } if store.Exists(id) { foundImage = true } + return false, nil + }); done { + return res, err } if foundImage { return nil, fmt.Errorf("locating item named %q for image with ID %q (consider removing the image to resolve the issue): %w", key, id, os.ErrNotExist) @@ -1654,29 +1765,20 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) { // ListLayerBigData retrieves a list of the (possibly large) chunks of // named data associated with an layer. func (s *store) ListLayerBigData(id string) ([]string, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } foundLayer := false - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res []string + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { data, err := store.BigDataNames(id) if err == nil { - return data, nil + res = data + return true, nil } if store.Exists(id) { foundLayer = true } + return false, nil + }); done { + return res, err } if foundLayer { return nil, fmt.Errorf("locating big data for layer with ID %q: %w", id, os.ErrNotExist) @@ -1687,29 +1789,20 @@ func (s *store) ListLayerBigData(id string) ([]string, error) { // LayerBigData retrieves a (possibly large) chunk of named data // associated with a layer. func (s *store) LayerBigData(id, key string) (io.ReadCloser, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } foundLayer := false - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res io.ReadCloser + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { data, err := store.BigData(id, key) if err == nil { - return data, nil + res = data + return true, nil } if store.Exists(id) { foundLayer = true } + return false, nil + }); done { + return res, err } if foundLayer { return nil, fmt.Errorf("locating item named %q for layer with ID %q: %w", key, id, os.ErrNotExist) @@ -1720,72 +1813,39 @@ func (s *store) LayerBigData(id, key string) (io.ReadCloser, error) { // SetLayerBigData stores a (possibly large) chunk of named data // associated with a layer. func (s *store) SetLayerBigData(id, key string, data io.Reader) error { - store, err := s.LayerStore() - if err != nil { - return err - } - - store.Lock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return err - } - return store.SetBigData(id, key, data) + return s.writeToLayerStore(func(store rwLayerStore) error { + return store.SetBigData(id, key, data) + }) } func (s *store) SetImageBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error { - ristore, err := s.ImageStore() - if err != nil { - return err - } - - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - - return ristore.SetBigData(id, key, data, digestManifest) + return s.writeToImageStore(func() error { + return s.imageStore.SetBigData(id, key, data, digestManifest) + }) } func (s *store) ImageSize(id string) (int64, error) { - var image *Image - - lstore, err := s.LayerStore() + layerStores, err := s.allLayerStores() if err != nil { - return -1, fmt.Errorf("loading primary layer store data: %w", err) - } - lstores, err := s.ROLayerStores() - if err != nil { - return -1, fmt.Errorf("loading additional layer stores: %w", err) + return -1, err } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { + for _, s := range layerStores { store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { + if err := store.startReading(); err != nil { return -1, err } - } - - var imageStore ROBigDataStore - istore, err := s.ImageStore() - if err != nil { - return -1, fmt.Errorf("loading primary image store data: %w", err) - } - istores, err := s.ROImageStores() - if err != nil { - return -1, fmt.Errorf("loading additional image stores: %w", err) + defer store.stopReading() } // Look for the image's record. - for _, s := range append([]ROImageStore{istore}, istores...) { + var imageStore roBigDataStore + var image *Image + for _, s := range s.allImageStores() { store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { + if err := store.startReading(); err != nil { return -1, err } + defer store.stopReading() if image, err = store.Get(id); err == nil { imageStore = store break @@ -1813,9 +1873,9 @@ func (s *store) ImageSize(id string) (int64, error) { } visited[layerID] = struct{}{} // Look for the layer and the store that knows about it. - var layerStore ROLayerStore + var layerStore roLayerStore var layer *Layer - for _, store := range append([]ROLayerStore{lstore}, lstores...) { + for _, store := range layerStores { if layer, err = store.Get(layerID); err == nil { layerStore = store break @@ -1859,21 +1919,16 @@ func (s *store) ImageSize(id string) (int64, error) { } func (s *store) ContainerSize(id string) (int64, error) { - lstore, err := s.LayerStore() - if err != nil { - return -1, err - } - lstores, err := s.ROLayerStores() + layerStores, err := s.allLayerStores() if err != nil { return -1, err } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { + for _, s := range layerStores { store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { + if err := store.startReading(); err != nil { return -1, err } + defer store.stopReading() } // Get the location of the container directory and container run directory. @@ -1887,188 +1942,132 @@ func (s *store) ContainerSize(id string) (int64, error) { return -1, err } - rcstore, err := s.ContainerStore() - if err != nil { - return -1, err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return -1, err - } + var res int64 = -1 + err = s.writeToContainerStore(func() error { // Yes, s.containerStore.BigDataSize requires a write lock. + // Read the container record. + container, err := s.containerStore.Get(id) + if err != nil { + return err + } - // Read the container record. - container, err := rcstore.Get(id) - if err != nil { - return -1, err - } + // Read the container's layer's size. + var layer *Layer + var size int64 + for _, store := range layerStores { + if layer, err = store.Get(container.LayerID); err == nil { + size, err = store.DiffSize("", layer.ID) + if err != nil { + return fmt.Errorf("determining size of layer with ID %q: %w", layer.ID, err) + } + break + } + } + if layer == nil { + return fmt.Errorf("locating layer with ID %q: %w", container.LayerID, ErrLayerUnknown) + } - // Read the container's layer's size. - var layer *Layer - var size int64 - for _, store := range append([]ROLayerStore{lstore}, lstores...) { - if layer, err = store.Get(container.LayerID); err == nil { - size, err = store.DiffSize("", layer.ID) + // Count big data items. + names, err := s.containerStore.BigDataNames(id) + if err != nil { + return fmt.Errorf("reading list of big data items for container %q: %w", container.ID, err) + } + for _, name := range names { + n, err := s.containerStore.BigDataSize(id, name) if err != nil { - return -1, fmt.Errorf("determining size of layer with ID %q: %w", layer.ID, err) + return fmt.Errorf("reading size of big data item %q for container %q: %w", name, id, err) } - break + size += n } - } - if layer == nil { - return -1, fmt.Errorf("locating layer with ID %q: %w", container.LayerID, ErrLayerUnknown) - } - // Count big data items. - names, err := rcstore.BigDataNames(id) - if err != nil { - return -1, fmt.Errorf("reading list of big data items for container %q: %w", container.ID, err) - } - for _, name := range names { - n, err := rcstore.BigDataSize(id, name) + // Count the size of our container directory and container run directory. + n, err := directory.Size(cdir) if err != nil { - return -1, fmt.Errorf("reading size of big data item %q for container %q: %w", name, id, err) + return err + } + size += n + n, err = directory.Size(rdir) + if err != nil { + return err } size += n - } - - // Count the size of our container directory and container run directory. - n, err := directory.Size(cdir) - if err != nil { - return -1, err - } - size += n - n, err = directory.Size(rdir) - if err != nil { - return -1, err - } - size += n - return size, nil + res = size + return nil + }) + return res, err } func (s *store) ListContainerBigData(id string) ([]string, error) { - rcstore, err := s.ContainerStore() - if err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } + defer s.containerStore.stopReading() - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return nil, err - } - - return rcstore.BigDataNames(id) + return s.containerStore.BigDataNames(id) } func (s *store) ContainerBigDataSize(id, key string) (int64, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return -1, err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return -1, err - } - return rcstore.BigDataSize(id, key) + var res int64 = -1 + err := s.writeToContainerStore(func() error { // Yes, BigDataSize requires a write lock. + var err error + res, err = s.containerStore.BigDataSize(id, key) + return err + }) + return res, err } func (s *store) ContainerBigDataDigest(id, key string) (digest.Digest, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return "", err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return "", err - } - return rcstore.BigDataDigest(id, key) + var res digest.Digest + err := s.writeToContainerStore(func() error { // Yes, BigDataDigest requires a write lock. + var err error + res, err = s.containerStore.BigDataDigest(id, key) + return err + }) + return res, err } func (s *store) ContainerBigData(id, key string) ([]byte, error) { - rcstore, err := s.ContainerStore() - if err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return nil, err - } - return rcstore.BigData(id, key) + defer s.containerStore.stopReading() + return s.containerStore.BigData(id, key) } func (s *store) SetContainerBigData(id, key string, data []byte) error { - rcstore, err := s.ContainerStore() - if err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } - return rcstore.SetBigData(id, key, data) + return s.writeToContainerStore(func() error { + return s.containerStore.SetBigData(id, key, data) + }) } func (s *store) Exists(id string) bool { - lstore, err := s.LayerStore() - if err != nil { - return false - } - lstores, err := s.ROLayerStores() - if err != nil { - return false - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return false - } + var res = false + + if done, _ := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if store.Exists(id) { - return true + res = true + return true, nil } + return false, nil + }); done { + return res } - istore, err := s.ImageStore() - if err != nil { - return false - } - istores, err := s.ROImageStores() - if err != nil { - return false - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return false - } + if done, _ := s.readAllImageStores(func(store roImageStore) (bool, error) { if store.Exists(id) { - return true + res = true + return true, nil } + return false, nil + }); done { + return res } - rcstore, err := s.ContainerStore() - if err != nil { - return false - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return false } - if rcstore.Exists(id) { - return true - } - - return false + defer s.containerStore.stopReading() + return s.containerStore.Exists(id) } func dedupeNames(names []string) []string { @@ -2099,206 +2098,117 @@ func (s *store) RemoveNames(id string, names []string) error { func (s *store) updateNames(id string, names []string, op updateNameOperation) error { deduped := dedupeNames(names) - rlstore, err := s.LayerStore() - if err != nil { - return err - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - if rlstore.Exists(id) { - switch op { - case setNames: - return rlstore.SetNames(id, deduped) - case removeNames: - return rlstore.RemoveNames(id, deduped) - case addNames: - return rlstore.AddNames(id, deduped) - default: - return errInvalidUpdateNameOperation + layerFound := false + if err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if !rlstore.Exists(id) { + return nil } - } - - ristore, err := s.ImageStore() - if err != nil { + layerFound = true + return rlstore.updateNames(id, deduped, op) + }); err != nil || layerFound { return err } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { + + if err := s.imageStore.startWriting(); err != nil { return err } - if ristore.Exists(id) { - switch op { - case setNames: - return ristore.SetNames(id, deduped) - case removeNames: - return ristore.RemoveNames(id, deduped) - case addNames: - return ristore.AddNames(id, deduped) - default: - return errInvalidUpdateNameOperation - } + defer s.imageStore.stopWriting() + if s.imageStore.Exists(id) { + return s.imageStore.updateNames(id, deduped, op) } // Check is id refers to a RO Store - ristores, err := s.ROImageStores() - if err != nil { - return err - } - for _, s := range ristores { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { + for _, is := range s.roImageStores { + store := is + if err := store.startReading(); err != nil { return err } + defer store.stopReading() if i, err := store.Get(id); err == nil { if len(deduped) > 1 { // Do not want to create image name in R/W storage deduped = deduped[1:] } - _, err := ristore.Create(id, deduped, i.TopLayer, i.Metadata, i.Created, i.Digest) - if err == nil { - return ristore.Save() - } + _, err := s.imageStore.Create(id, deduped, i.TopLayer, i.Metadata, i.Created, i.Digest) return err } } - rcstore, err := s.ContainerStore() - if err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } - if rcstore.Exists(id) { - switch op { - case setNames: - return rcstore.SetNames(id, deduped) - case removeNames: - return rcstore.RemoveNames(id, deduped) - case addNames: - return rcstore.AddNames(id, deduped) - default: - return errInvalidUpdateNameOperation + containerFound := false + if err := s.writeToContainerStore(func() error { + if !s.containerStore.Exists(id) { + return nil } + containerFound = true + return s.containerStore.updateNames(id, deduped, op) + }); err != nil || containerFound { + return err } + return ErrLayerUnknown } func (s *store) Names(id string) ([]string, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res []string + + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if l, err := store.Get(id); l != nil && err == nil { - return l.Names, nil + res = l.Names + return true, nil } + return false, nil + }); done { + return res, err } - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { if i, err := store.Get(id); i != nil && err == nil { - return i.Names, nil + res = i.Names + return true, nil } + return false, nil + }); done { + return res, err } - rcstore, err := s.ContainerStore() - if err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return nil, err - } - if c, err := rcstore.Get(id); c != nil && err == nil { + defer s.containerStore.stopReading() + if c, err := s.containerStore.Get(id); c != nil && err == nil { return c.Names, nil } return nil, ErrLayerUnknown } func (s *store) Lookup(name string) (string, error) { - lstore, err := s.LayerStore() - if err != nil { - return "", err - } - lstores, err := s.ROLayerStores() - if err != nil { - return "", err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return "", err - } + var res string + + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if l, err := store.Get(name); l != nil && err == nil { - return l.ID, nil + res = l.ID + return true, nil } + return false, nil + }); done { + return res, err } - istore, err := s.ImageStore() - if err != nil { - return "", err - } - istores, err := s.ROImageStores() - if err != nil { - return "", err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return "", err - } + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { if i, err := store.Get(name); i != nil && err == nil { - return i.ID, nil + res = i.ID + return true, nil } + return false, nil + }); done { + return res, err } - cstore, err := s.ContainerStore() - if err != nil { + if err := s.containerStore.startReading(); err != nil { return "", err } - cstore.RLock() - defer cstore.Unlock() - if err := cstore.ReloadIfChanged(); err != nil { - return "", err - } - if c, err := cstore.Get(name); c != nil && err == nil { + defer s.containerStore.stopReading() + if c, err := s.containerStore.Get(name); c != nil && err == nil { return c.ID, nil } @@ -2306,417 +2216,280 @@ func (s *store) Lookup(name string) (string, error) { } func (s *store) DeleteLayer(id string) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - ristore, err := s.ImageStore() - if err != nil { - return err - } - rcstore, err := s.ContainerStore() - if err != nil { - return err - } - - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } - - if rlstore.Exists(id) { - if l, err := rlstore.Get(id); err != nil { - id = l.ID - } - layers, err := rlstore.Layers() - if err != nil { - return err - } - for _, layer := range layers { - if layer.Parent == id { - return fmt.Errorf("used by layer %v: %w", layer.ID, ErrLayerHasChildren) + return s.writeToAllStores(func(rlstore rwLayerStore) error { + if rlstore.Exists(id) { + if l, err := rlstore.Get(id); err != nil { + id = l.ID } - } - images, err := ristore.Images() - if err != nil { - return err - } - - for _, image := range images { - if image.TopLayer == id { - return fmt.Errorf("layer %v used by image %v: %w", id, image.ID, ErrLayerUsedByImage) + layers, err := rlstore.Layers() + if err != nil { + return err + } + for _, layer := range layers { + if layer.Parent == id { + return fmt.Errorf("used by layer %v: %w", layer.ID, ErrLayerHasChildren) + } + } + images, err := s.imageStore.Images() + if err != nil { + return err } - if stringutils.InSlice(image.MappedTopLayers, id) { - // No write access to the image store, fail before the layer is deleted - if _, ok := ristore.(*imageStore); !ok { + + for _, image := range images { + if image.TopLayer == id { return fmt.Errorf("layer %v used by image %v: %w", id, image.ID, ErrLayerUsedByImage) } } - } - containers, err := rcstore.Containers() - if err != nil { - return err - } - for _, container := range containers { - if container.LayerID == id { - return fmt.Errorf("layer %v used by container %v: %w", id, container.ID, ErrLayerUsedByContainer) + containers, err := s.containerStore.Containers() + if err != nil { + return err + } + for _, container := range containers { + if container.LayerID == id { + return fmt.Errorf("layer %v used by container %v: %w", id, container.ID, ErrLayerUsedByContainer) + } + } + if err := rlstore.Delete(id); err != nil { + return fmt.Errorf("delete layer %v: %w", id, err) } - } - if err := rlstore.Delete(id); err != nil { - return fmt.Errorf("delete layer %v: %w", id, err) - } - // The check here is used to avoid iterating the images if we don't need to. - // There is already a check above for the imageStore to be writeable when the layer is part of MappedTopLayers. - if istore, ok := ristore.(*imageStore); ok { for _, image := range images { if stringutils.InSlice(image.MappedTopLayers, id) { - if err = istore.removeMappedTopLayer(image.ID, id); err != nil { + if err = s.imageStore.removeMappedTopLayer(image.ID, id); err != nil { return fmt.Errorf("remove mapped top layer %v from image %v: %w", id, image.ID, err) } } } + return nil } - return nil - } - return ErrNotALayer + return ErrNotALayer + }) } func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) { - rlstore, err := s.LayerStore() - if err != nil { - return nil, err - } - ristore, err := s.ImageStore() - if err != nil { - return nil, err - } - rcstore, err := s.ContainerStore() - if err != nil { - return nil, err - } - - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return nil, err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return nil, err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return nil, err - } layersToRemove := []string{} - if ristore.Exists(id) { - image, err := ristore.Get(id) - if err != nil { - return nil, err - } - id = image.ID - containers, err := rcstore.Containers() - if err != nil { - return nil, err - } - aContainerByImage := make(map[string]string) - for _, container := range containers { - aContainerByImage[container.ImageID] = container.ID - } - if container, ok := aContainerByImage[id]; ok { - return nil, fmt.Errorf("image used by %v: %w", container, ErrImageUsedByContainer) - } - images, err := ristore.Images() - if err != nil { - return nil, err - } - layers, err := rlstore.Layers() - if err != nil { - return nil, err - } - childrenByParent := make(map[string][]string) - for _, layer := range layers { - childrenByParent[layer.Parent] = append(childrenByParent[layer.Parent], layer.ID) - } - otherImagesTopLayers := make(map[string]struct{}) - for _, img := range images { - if img.ID != id { - otherImagesTopLayers[img.TopLayer] = struct{}{} - for _, layerID := range img.MappedTopLayers { - otherImagesTopLayers[layerID] = struct{}{} - } + if err := s.writeToAllStores(func(rlstore rwLayerStore) error { + if s.imageStore.Exists(id) { + image, err := s.imageStore.Get(id) + if err != nil { + return err } - } - if commit { - if err = ristore.Delete(id); err != nil { - return nil, err + id = image.ID + containers, err := s.containerStore.Containers() + if err != nil { + return err } - } - layer := image.TopLayer - layersToRemoveMap := make(map[string]struct{}) - layersToRemove = append(layersToRemove, image.MappedTopLayers...) - for _, mappedTopLayer := range image.MappedTopLayers { - layersToRemoveMap[mappedTopLayer] = struct{}{} - } - for layer != "" { - if rcstore.Exists(layer) { - break + aContainerByImage := make(map[string]string) + for _, container := range containers { + aContainerByImage[container.ImageID] = container.ID } - if _, used := otherImagesTopLayers[layer]; used { - break + if container, ok := aContainerByImage[id]; ok { + return fmt.Errorf("image used by %v: %w", container, ErrImageUsedByContainer) + } + images, err := s.imageStore.Images() + if err != nil { + return err + } + layers, err := rlstore.Layers() + if err != nil { + return err + } + childrenByParent := make(map[string][]string) + for _, layer := range layers { + childrenByParent[layer.Parent] = append(childrenByParent[layer.Parent], layer.ID) + } + otherImagesTopLayers := make(map[string]struct{}) + for _, img := range images { + if img.ID != id { + otherImagesTopLayers[img.TopLayer] = struct{}{} + for _, layerID := range img.MappedTopLayers { + otherImagesTopLayers[layerID] = struct{}{} + } + } + } + if commit { + if err = s.imageStore.Delete(id); err != nil { + return err + } } - parent := "" - if l, err := rlstore.Get(layer); err == nil { - parent = l.Parent + layer := image.TopLayer + layersToRemoveMap := make(map[string]struct{}) + layersToRemove = append(layersToRemove, image.MappedTopLayers...) + for _, mappedTopLayer := range image.MappedTopLayers { + layersToRemoveMap[mappedTopLayer] = struct{}{} } - hasChildrenNotBeingRemoved := func() bool { - layersToCheck := []string{layer} - if layer == image.TopLayer { - layersToCheck = append(layersToCheck, image.MappedTopLayers...) + for layer != "" { + if s.containerStore.Exists(layer) { + break + } + if _, used := otherImagesTopLayers[layer]; used { + break + } + parent := "" + if l, err := rlstore.Get(layer); err == nil { + parent = l.Parent } - for _, layer := range layersToCheck { - if childList := childrenByParent[layer]; len(childList) > 0 { - for _, child := range childList { - if _, childIsSlatedForRemoval := layersToRemoveMap[child]; childIsSlatedForRemoval { - continue + hasChildrenNotBeingRemoved := func() bool { + layersToCheck := []string{layer} + if layer == image.TopLayer { + layersToCheck = append(layersToCheck, image.MappedTopLayers...) + } + for _, layer := range layersToCheck { + if childList := childrenByParent[layer]; len(childList) > 0 { + for _, child := range childList { + if _, childIsSlatedForRemoval := layersToRemoveMap[child]; childIsSlatedForRemoval { + continue + } + return true } - return true } } + return false } - return false - } - if hasChildrenNotBeingRemoved() { - break + if hasChildrenNotBeingRemoved() { + break + } + layersToRemove = append(layersToRemove, layer) + layersToRemoveMap[layer] = struct{}{} + layer = parent } - layersToRemove = append(layersToRemove, layer) - layersToRemoveMap[layer] = struct{}{} - layer = parent + } else { + return ErrNotAnImage } - } else { - return nil, ErrNotAnImage - } - if commit { - for _, layer := range layersToRemove { - if err = rlstore.Delete(layer); err != nil { - return nil, err + if commit { + for _, layer := range layersToRemove { + if err = rlstore.Delete(layer); err != nil { + return err + } } } + return nil + }); err != nil { + return nil, err } return layersToRemove, nil } func (s *store) DeleteContainer(id string) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - ristore, err := s.ImageStore() - if err != nil { - return err - } - rcstore, err := s.ContainerStore() - if err != nil { - return err - } + return s.writeToAllStores(func(rlstore rwLayerStore) error { + if !s.containerStore.Exists(id) { + return ErrNotAContainer + } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } + container, err := s.containerStore.Get(id) + if err != nil { + return ErrNotAContainer + } - if rcstore.Exists(id) { - if container, err := rcstore.Get(id); err == nil { - errChan := make(chan error) - var wg sync.WaitGroup + errChan := make(chan error) + var wg sync.WaitGroup - if rlstore.Exists(container.LayerID) { - wg.Add(1) - go func() { - errChan <- rlstore.Delete(container.LayerID) - wg.Done() - }() - } + if rlstore.Exists(container.LayerID) { wg.Add(1) go func() { - errChan <- rcstore.Delete(id) + errChan <- rlstore.Delete(container.LayerID) wg.Done() }() + } + wg.Add(1) + go func() { + errChan <- s.containerStore.Delete(id) + wg.Done() + }() + + middleDir := s.graphDriverName + "-containers" + gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID) + wg.Add(1) + go func() { + defer wg.Done() + // attempt a simple rm -rf first + err := os.RemoveAll(gcpath) + if err == nil { + errChan <- nil + return + } + // and if it fails get to the more complicated cleanup + errChan <- system.EnsureRemoveAll(gcpath) + }() + + rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID) + wg.Add(1) + go func() { + defer wg.Done() + // attempt a simple rm -rf first + err := os.RemoveAll(rcpath) + if err == nil { + errChan <- nil + return + } + // and if it fails get to the more complicated cleanup + errChan <- system.EnsureRemoveAll(rcpath) + }() - middleDir := s.graphDriverName + "-containers" - gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID) - wg.Add(1) - go func() { - defer wg.Done() - // attempt a simple rm -rf first - err := os.RemoveAll(gcpath) - if err == nil { - errChan <- nil - return - } - // and if it fails get to the more complicated cleanup - errChan <- system.EnsureRemoveAll(gcpath) - }() - - rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID) - wg.Add(1) - go func() { - defer wg.Done() - // attempt a simple rm -rf first - err := os.RemoveAll(rcpath) - if err == nil { - errChan <- nil - return - } - // and if it fails get to the more complicated cleanup - errChan <- system.EnsureRemoveAll(rcpath) - }() - - go func() { - wg.Wait() - close(errChan) - }() + go func() { + wg.Wait() + close(errChan) + }() - var errors []error - for err := range errChan { - if err != nil { - errors = append(errors, err) - } + var errors []error + for err := range errChan { + if err != nil { + errors = append(errors, err) } - return multierror.Append(nil, errors...).ErrorOrNil() } - } - return ErrNotAContainer + return multierror.Append(nil, errors...).ErrorOrNil() + }) } func (s *store) Delete(id string) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - ristore, err := s.ImageStore() - if err != nil { - return err - } - rcstore, err := s.ContainerStore() - if err != nil { - return err - } - - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } - - if rcstore.Exists(id) { - if container, err := rcstore.Get(id); err == nil { - if rlstore.Exists(container.LayerID) { - if err = rlstore.Delete(container.LayerID); err != nil { - return err - } - if err = rcstore.Delete(id); err != nil { - return err - } - middleDir := s.graphDriverName + "-containers" - gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID, "userdata") - if err = os.RemoveAll(gcpath); err != nil { - return err - } - rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID, "userdata") - if err = os.RemoveAll(rcpath); err != nil { - return err + return s.writeToAllStores(func(rlstore rwLayerStore) error { + if s.containerStore.Exists(id) { + if container, err := s.containerStore.Get(id); err == nil { + if rlstore.Exists(container.LayerID) { + if err = rlstore.Delete(container.LayerID); err != nil { + return err + } + if err = s.containerStore.Delete(id); err != nil { + return err + } + middleDir := s.graphDriverName + "-containers" + gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID, "userdata") + if err = os.RemoveAll(gcpath); err != nil { + return err + } + rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID, "userdata") + if err = os.RemoveAll(rcpath); err != nil { + return err + } + return nil } - return nil + return ErrNotALayer } - return ErrNotALayer } - } - if ristore.Exists(id) { - return ristore.Delete(id) - } - if rlstore.Exists(id) { - return rlstore.Delete(id) - } - return ErrLayerUnknown -} - -func (s *store) Wipe() error { - rcstore, err := s.ContainerStore() - if err != nil { - return err - } - ristore, err := s.ImageStore() - if err != nil { - return err - } - rlstore, err := s.LayerStore() - if err != nil { - return err - } - - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return err - } - ristore.Lock() - defer ristore.Unlock() - if err := ristore.ReloadIfChanged(); err != nil { - return err - } - rcstore.Lock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { - return err - } + if s.imageStore.Exists(id) { + return s.imageStore.Delete(id) + } + if rlstore.Exists(id) { + return rlstore.Delete(id) + } + return ErrLayerUnknown + }) +} - if err = rcstore.Wipe(); err != nil { - return err - } - if err = ristore.Wipe(); err != nil { - return err - } - return rlstore.Wipe() +func (s *store) Wipe() error { + return s.writeToAllStores(func(rlstore rwLayerStore) error { + if err := s.containerStore.Wipe(); err != nil { + return err + } + if err := s.imageStore.Wipe(); err != nil { + return err + } + return rlstore.Wipe() + }) } func (s *store) Status() ([][2]string, error) { - rlstore, err := s.LayerStore() + rlstore, err := s.getLayerStore() if err != nil { return nil, err } @@ -2728,37 +2501,24 @@ func (s *store) Version() ([][2]string, error) { } func (s *store) mount(id string, options drivers.MountOpts) (string, error) { - rlstore, err := s.LayerStore() - if err != nil { - return "", err - } - - s.graphLock.Lock() - defer s.graphLock.Unlock() - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + // We need to make sure the home mount is present when the Mount is done, which happens by possibly reinitializing the graph driver + // in startUsingGraphDriver(). + if err := s.startUsingGraphDriver(); err != nil { return "", err } + defer s.stopUsingGraphDriver() - modified, err := s.graphLock.Modified() + rlstore, err := s.getLayerStoreLocked() if err != nil { return "", err } - - /* We need to make sure the home mount is present when the Mount is done. */ - if modified { - s.graphDriver = nil - s.layerStore = nil - s.graphDriver, err = s.getGraphDriver() - if err != nil { - return "", err - } - s.lastLoaded = time.Now() + if err := rlstore.startWriting(); err != nil { + return "", err } + defer rlstore.stopWriting() if options.UidMaps != nil || options.GidMaps != nil { - options.DisableShifting = !s.canUseShifting(options.UidMaps, options.GidMaps) + options.DisableShifting = !canUseShifting(rlstore, options.UidMaps, options.GidMaps) } if rlstore.Exists(id) { @@ -2811,15 +2571,14 @@ func (s *store) Mounted(id string) (int, error) { if layerID, err := s.ContainerLayerID(id); err == nil { id = layerID } - rlstore, err := s.LayerStore() + rlstore, err := s.getLayerStore() if err != nil { return 0, err } - rlstore.RLock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + if err := rlstore.startReading(); err != nil { return 0, err } + defer rlstore.stopReading() return rlstore.Mounted(id) } @@ -2836,103 +2595,66 @@ func (s *store) Unmount(id string, force bool) (bool, error) { if layerID, err := s.ContainerLayerID(id); err == nil { id = layerID } - rlstore, err := s.LayerStore() - if err != nil { - return false, err - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return false, err - } - if rlstore.Exists(id) { - return rlstore.Unmount(id, force) - } - return false, ErrLayerUnknown + var res bool + err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if rlstore.Exists(id) { + var err error + res, err = rlstore.unmount(id, force, false) + return err + } + return ErrLayerUnknown + }) + return res, err } func (s *store) Changes(from, to string) ([]archive.Change, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res []archive.Change + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if store.Exists(to) { - return store.Changes(from, to) + var err error + res, err = store.Changes(from, to) + return true, err } + return false, nil + }); done { + return res, err } return nil, ErrLayerUnknown } func (s *store) DiffSize(from, to string) (int64, error) { - lstore, err := s.LayerStore() - if err != nil { - return -1, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return -1, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return -1, err - } + var res int64 = -1 + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if store.Exists(to) { - return store.DiffSize(from, to) + var err error + res, err = store.DiffSize(from, to) + return true, err } + return false, nil + }); done { + return res, err } return -1, ErrLayerUnknown } func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - // NaiveDiff could cause mounts to happen without a lock, so be safe // and treat the .Diff operation as a Mount. - s.graphLock.Lock() - defer s.graphLock.Unlock() - - modified, err := s.graphLock.Modified() - if err != nil { + // We need to make sure the home mount is present when the Mount is done, which happens by possibly reinitializing the graph driver + // in startUsingGraphDriver(). + if err := s.startUsingGraphDriver(); err != nil { return nil, err } + defer s.stopUsingGraphDriver() - // We need to make sure the home mount is present when the Mount is done. - if modified { - s.graphDriver = nil - s.layerStore = nil - s.graphDriver, err = s.getGraphDriver() - if err != nil { - return nil, err - } - s.lastLoaded = time.Now() + layerStores, err := s.allLayerStoresLocked() + if err != nil { + return nil, err } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { + for _, s := range layerStores { store := s - store.RLock() - if err := store.ReloadIfChanged(); err != nil { - store.Unlock() + if err := store.startReading(); err != nil { return nil, err } if store.Exists(to) { @@ -2940,130 +2662,87 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro if rc != nil && err == nil { wrapped := ioutils.NewReadCloserWrapper(rc, func() error { err := rc.Close() - store.Unlock() + store.stopReading() return err }) return wrapped, nil } - store.Unlock() + store.stopReading() return rc, err } - store.Unlock() + store.stopReading() } return nil, ErrLayerUnknown } func (s *store) ApplyDiffFromStagingDirectory(to, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - rlstore.Lock() - defer rlstore.Unlock() - if modified, err := rlstore.Modified(); modified || err != nil { - if err = rlstore.Load(); err != nil { - return err + return s.writeToLayerStore(func(rlstore rwLayerStore) error { + if !rlstore.Exists(to) { + return ErrLayerUnknown } - } - if !rlstore.Exists(to) { - return ErrLayerUnknown - } - return rlstore.ApplyDiffFromStagingDirectory(to, stagingDirectory, diffOutput, options) + return rlstore.ApplyDiffFromStagingDirectory(to, stagingDirectory, diffOutput, options) + }) } func (s *store) CleanupStagingDirectory(stagingDirectory string) error { - rlstore, err := s.LayerStore() - if err != nil { - return err - } - rlstore.Lock() - defer rlstore.Unlock() - if modified, err := rlstore.Modified(); modified || err != nil { - if err = rlstore.Load(); err != nil { - return err - } - } - return rlstore.CleanupStagingDirectory(stagingDirectory) + return s.writeToLayerStore(func(rlstore rwLayerStore) error { + return rlstore.CleanupStagingDirectory(stagingDirectory) + }) } func (s *store) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error) { - rlstore, err := s.LayerStore() - if err != nil { - return nil, err - } - rlstore.Lock() - defer rlstore.Unlock() - if modified, err := rlstore.Modified(); modified || err != nil { - if err = rlstore.Load(); err != nil { - return nil, err + var res *drivers.DriverWithDifferOutput + err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if to != "" && !rlstore.Exists(to) { + return ErrLayerUnknown } - } - if to != "" && !rlstore.Exists(to) { - return nil, ErrLayerUnknown - } - return rlstore.ApplyDiffWithDiffer(to, options, differ) + var err error + res, err = rlstore.ApplyDiffWithDiffer(to, options, differ) + return err + }) + return res, err } func (s *store) DifferTarget(id string) (string, error) { - rlstore, err := s.LayerStore() - if err != nil { - return "", err - } - rlstore.Lock() - defer rlstore.Unlock() - if modified, err := rlstore.Modified(); modified || err != nil { - if err = rlstore.Load(); err != nil { - return "", err + var res string + err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if rlstore.Exists(id) { + var err error + res, err = rlstore.DifferTarget(id) + return err } - } - if rlstore.Exists(id) { - return rlstore.DifferTarget(id) - } - return "", ErrLayerUnknown + return ErrLayerUnknown + }) + return res, err } func (s *store) ApplyDiff(to string, diff io.Reader) (int64, error) { - rlstore, err := s.LayerStore() - if err != nil { - return -1, err - } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return -1, err - } - if rlstore.Exists(to) { - return rlstore.ApplyDiff(to, diff) - } - return -1, ErrLayerUnknown + var res int64 = -1 + err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if rlstore.Exists(to) { + var err error + res, err = rlstore.ApplyDiff(to, diff) + return err + } + return ErrLayerUnknown + }) + return res, err } -func (s *store) layersByMappedDigest(m func(ROLayerStore, digest.Digest) ([]Layer, error), d digest.Digest) ([]Layer, error) { +func (s *store) layersByMappedDigest(m func(roLayerStore, digest.Digest) ([]Layer, error), d digest.Digest) ([]Layer, error) { var layers []Layer - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + if _, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { storeLayers, err := m(store, d) if err != nil { if !errors.Is(err, ErrLayerUnknown) { - return nil, err + return true, err } - continue + return false, nil } layers = append(layers, storeLayers...) + return false, nil + }); err != nil { + return nil, err } if len(layers) == 0 { return nil, ErrLayerUnknown @@ -3075,49 +2754,40 @@ func (s *store) LayersByCompressedDigest(d digest.Digest) ([]Layer, error) { if err := d.Validate(); err != nil { return nil, fmt.Errorf("looking for compressed layers matching digest %q: %w", d, err) } - return s.layersByMappedDigest(func(r ROLayerStore, d digest.Digest) ([]Layer, error) { return r.LayersByCompressedDigest(d) }, d) + return s.layersByMappedDigest(func(r roLayerStore, d digest.Digest) ([]Layer, error) { return r.LayersByCompressedDigest(d) }, d) } func (s *store) LayersByUncompressedDigest(d digest.Digest) ([]Layer, error) { if err := d.Validate(); err != nil { return nil, fmt.Errorf("looking for layers matching digest %q: %w", d, err) } - return s.layersByMappedDigest(func(r ROLayerStore, d digest.Digest) ([]Layer, error) { return r.LayersByUncompressedDigest(d) }, d) + return s.layersByMappedDigest(func(r roLayerStore, d digest.Digest) ([]Layer, error) { return r.LayersByUncompressedDigest(d) }, d) } func (s *store) LayerSize(id string) (int64, error) { - lstore, err := s.LayerStore() - if err != nil { - return -1, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return -1, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return -1, err - } + var res int64 = -1 + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { if store.Exists(id) { - return store.Size(id) + var err error + res, err = store.Size(id) + return true, err } + return false, nil + }); done { + return res, err } return -1, ErrLayerUnknown } func (s *store) LayerParentOwners(id string) ([]int, []int, error) { - rlstore, err := s.LayerStore() + rlstore, err := s.getLayerStore() if err != nil { return nil, nil, err } - rlstore.RLock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + if err := rlstore.startReading(); err != nil { return nil, nil, err } + defer rlstore.stopReading() if rlstore.Exists(id) { return rlstore.ParentOwners(id) } @@ -3125,25 +2795,19 @@ func (s *store) LayerParentOwners(id string) ([]int, []int, error) { } func (s *store) ContainerParentOwners(id string) ([]int, []int, error) { - rlstore, err := s.LayerStore() + rlstore, err := s.getLayerStore() if err != nil { return nil, nil, err } - rcstore, err := s.ContainerStore() - if err != nil { - return nil, nil, err - } - rlstore.RLock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + if err := rlstore.startReading(); err != nil { return nil, nil, err } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + defer rlstore.stopReading() + if err := s.containerStore.startReading(); err != nil { return nil, nil, err } - container, err := rcstore.Get(id) + defer s.containerStore.stopReading() + container, err := s.containerStore.Get(id) if err != nil { return nil, nil, err } @@ -3154,114 +2818,74 @@ func (s *store) ContainerParentOwners(id string) ([]int, []int, error) { } func (s *store) Layers() ([]Layer, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - - layers, err := func() ([]Layer, error) { - lstore.Lock() - defer lstore.Unlock() - if err := lstore.Load(); err != nil { - return nil, err - } - return lstore.Layers() - }() - if err != nil { - return nil, err - } - - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - - for _, s := range lstores { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var layers []Layer + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { storeLayers, err := store.Layers() if err != nil { - return nil, err + return true, err } layers = append(layers, storeLayers...) + return false, nil + }); done { + return nil, err } return layers, nil } func (s *store) Images() ([]Image, error) { var images []Image - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { storeImages, err := store.Images() if err != nil { - return nil, err + return true, err } images = append(images, storeImages...) + return false, nil + }); err != nil { + return nil, err } return images, nil } func (s *store) Containers() ([]Container, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return nil, err - } - - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } + defer s.containerStore.stopReading() - return rcstore.Containers() + return s.containerStore.Containers() } func (s *store) Layer(id string) (*Layer, error) { - lstore, err := s.LayerStore() - if err != nil { - return nil, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROLayerStore{lstore}, lstores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res *Layer + if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { layer, err := store.Get(id) if err == nil { - return layer, nil + res = layer + return true, nil } + return false, nil + }); done { + return res, err } return nil, ErrLayerUnknown } func (s *store) LookupAdditionalLayer(d digest.Digest, imageref string) (AdditionalLayer, error) { - adriver, ok := s.graphDriver.(drivers.AdditionalLayerStoreDriver) - if !ok { - return nil, ErrLayerUnknown + var adriver drivers.AdditionalLayerStoreDriver + if err := func() error { // A scope for defer + if err := s.startUsingGraphDriver(); err != nil { + return err + } + defer s.stopUsingGraphDriver() + a, ok := s.graphDriver.(drivers.AdditionalLayerStoreDriver) + if !ok { + return ErrLayerUnknown + } + adriver = a + return nil + }(); err != nil { + return nil, err } al, err := adriver.LookupAdditionalLayer(d, imageref) @@ -3298,29 +2922,23 @@ func (al *additionalLayer) CompressedSize() int64 { } func (al *additionalLayer) PutAs(id, parent string, names []string) (*Layer, error) { - rlstore, err := al.s.LayerStore() + rlstore, rlstores, err := al.s.bothLayerStoreKinds() if err != nil { return nil, err } - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { - return nil, err - } - rlstores, err := al.s.ROLayerStores() - if err != nil { + if err := rlstore.startWriting(); err != nil { return nil, err } + defer rlstore.stopWriting() var parentLayer *Layer if parent != "" { - for _, lstore := range append([]ROLayerStore{rlstore}, rlstores...) { + for _, lstore := range append([]roLayerStore{rlstore}, rlstores...) { if lstore != rlstore { - lstore.RLock() - defer lstore.Unlock() - if err := lstore.ReloadIfChanged(); err != nil { + if err := lstore.startReading(); err != nil { return nil, err } + defer lstore.stopReading() } parentLayer, err = lstore.Get(parent) if err == nil { @@ -3340,117 +2958,75 @@ func (al *additionalLayer) Release() { } func (s *store) Image(id string) (*Image, error) { - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + var res *Image + if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { image, err := store.Get(id) if err == nil { - return image, nil + res = image + return true, nil } + return false, nil + }); done { + return res, err } return nil, fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown) } func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { - images := []*Image{} layer, err := s.Layer(id) if err != nil { return nil, err } - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, s := range append([]ROImageStore{istore}, istores...) { - store := s - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + images := []*Image{} + if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { imageList, err := store.Images() if err != nil { - return nil, err + return true, err } for _, image := range imageList { + image := image if image.TopLayer == layer.ID || stringutils.InSlice(image.MappedTopLayers, layer.ID) { images = append(images, &image) } } + return false, nil + }); err != nil { + return nil, err } return images, nil } func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) { images := []*Image{} - - istore, err := s.ImageStore() - if err != nil { - return nil, err - } - - istores, err := s.ROImageStores() - if err != nil { - return nil, err - } - for _, store := range append([]ROImageStore{istore}, istores...) { - store.RLock() - defer store.Unlock() - if err := store.ReloadIfChanged(); err != nil { - return nil, err - } + if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { imageList, err := store.ByDigest(d) if err != nil && !errors.Is(err, ErrImageUnknown) { - return nil, err + return true, err } images = append(images, imageList...) + return false, nil + }); err != nil { + return nil, err } return images, nil } func (s *store) Container(id string) (*Container, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return nil, err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } + defer s.containerStore.stopReading() - return rcstore.Get(id) + return s.containerStore.Get(id) } func (s *store) ContainerLayerID(id string) (string, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return "", err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return "", err } - container, err := rcstore.Get(id) + defer s.containerStore.stopReading() + container, err := s.containerStore.Get(id) if err != nil { return "", err } @@ -3462,16 +3038,11 @@ func (s *store) ContainerByLayer(id string) (*Container, error) { if err != nil { return nil, err } - rcstore, err := s.ContainerStore() - if err != nil { - return nil, err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return nil, err } - containerList, err := rcstore.Containers() + defer s.containerStore.stopReading() + containerList, err := s.containerStore.Containers() if err != nil { return nil, err } @@ -3485,17 +3056,12 @@ func (s *store) ContainerByLayer(id string) (*Container, error) { } func (s *store) ContainerDirectory(id string) (string, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return "", err - } - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return "", err } + defer s.containerStore.stopReading() - id, err = rcstore.Lookup(id) + id, err := s.containerStore.Lookup(id) if err != nil { return "", err } @@ -3509,18 +3075,12 @@ func (s *store) ContainerDirectory(id string) (string, error) { } func (s *store) ContainerRunDirectory(id string) (string, error) { - rcstore, err := s.ContainerStore() - if err != nil { - return "", err - } - - rcstore.RLock() - defer rcstore.Unlock() - if err := rcstore.ReloadIfChanged(); err != nil { + if err := s.containerStore.startReading(); err != nil { return "", err } + defer s.containerStore.stopReading() - id, err = rcstore.Lookup(id) + id, err := s.containerStore.Lookup(id) if err != nil { return "", err } @@ -3575,21 +3135,20 @@ func (s *store) FromContainerRunDirectory(id, file string) ([]byte, error) { func (s *store) Shutdown(force bool) ([]string, error) { mounted := []string{} - modified := false - rlstore, err := s.LayerStore() - if err != nil { + if err := s.startUsingGraphDriver(); err != nil { return mounted, err } + defer s.stopUsingGraphDriver() - s.graphLock.Lock() - defer s.graphLock.Unlock() - - rlstore.Lock() - defer rlstore.Unlock() - if err := rlstore.ReloadIfChanged(); err != nil { + rlstore, err := s.getLayerStoreLocked() + if err != nil { + return mounted, err + } + if err := rlstore.startWriting(); err != nil { return nil, err } + defer rlstore.stopWriting() layers, err := rlstore.Layers() if err != nil { @@ -3601,15 +3160,17 @@ func (s *store) Shutdown(force bool) ([]string, error) { } mounted = append(mounted, layer.ID) if force { - for layer.MountCount > 0 { - _, err2 := rlstore.Unmount(layer.ID, force) + for { + _, err2 := rlstore.unmount(layer.ID, force, true) + if err2 == ErrLayerNotMounted { + break + } if err2 != nil { if err == nil { err = err2 } break } - modified = true } } } @@ -3618,11 +3179,16 @@ func (s *store) Shutdown(force bool) ([]string, error) { } if err == nil { err = s.graphDriver.Cleanup() - s.graphLock.Touch() - modified = true - } - if modified { - rlstore.Touch() + // We don’t retain the lastWrite value, and treat this update as if someone else did the .Cleanup(), + // so that we reload after a .Shutdown() the same way other processes would. + // Shutdown() is basically an error path, so reliability is more important than performance. + if _, err2 := s.graphLock.RecordWrite(); err2 != nil { + if err == nil { + err = err2 + } else { + err = fmt.Errorf("(graphLock.RecordWrite failed: %v) %w", err2, err) + } + } } return mounted, err } @@ -3710,9 +3276,10 @@ const AutoUserNsMaxSize = 65536 // creating a user namespace. const RootAutoUserNsUser = "containers" -// SetDefaultConfigFilePath sets the default configuration to the specified path +// SetDefaultConfigFilePath sets the default configuration to the specified path, and loads the file. +// Deprecated: Use types.SetDefaultConfigFilePath, which can return an error. func SetDefaultConfigFilePath(path string) { - types.SetDefaultConfigFilePath(path) + _ = types.SetDefaultConfigFilePath(path) } // DefaultConfigFile returns the path to the storage config file used @@ -3722,8 +3289,9 @@ func DefaultConfigFile(rootless bool) (string, error) { // ReloadConfigurationFile parses the specified configuration file and overrides // the configuration in storeOptions. +// Deprecated: Use types.ReloadConfigurationFile, which can return an error. func ReloadConfigurationFile(configFile string, storeOptions *types.StoreOptions) { - types.ReloadConfigurationFile(configFile, storeOptions) + _ = types.ReloadConfigurationFile(configFile, storeOptions) } // GetDefaultMountOptions returns the default mountoptions defined in container/storage @@ -3765,3 +3333,20 @@ func (s *store) Free() { } } } + +// Tries to clean up old unreferenced container leftovers. returns the first error +// but continues as far as it can +func (s *store) GarbageCollect() error { + firstErr := s.writeToContainerStore(func() error { + return s.containerStore.GarbageCollect() + }) + + moreErr := s.writeToLayerStore(func(rlstore rwLayerStore) error { + return rlstore.GarbageCollect() + }) + if firstErr == nil { + firstErr = moreErr + } + + return firstErr +} diff --git a/vendor/github.com/containers/storage/types/errors.go b/vendor/github.com/containers/storage/types/errors.go index ad12ffdb..dc6ee3e0 100644 --- a/vendor/github.com/containers/storage/types/errors.go +++ b/vendor/github.com/containers/storage/types/errors.go @@ -57,4 +57,6 @@ var ( ErrNotSupported = errors.New("not supported") // ErrInvalidMappings is returned when the specified mappings are invalid. ErrInvalidMappings = errors.New("invalid mappings specified") + // ErrNoAvailableIDs is returned when there are not enough unused IDS within the user namespace. + ErrNoAvailableIDs = errors.New("not enough unused IDs in user namespace") ) diff --git a/vendor/github.com/containers/storage/types/options.go b/vendor/github.com/containers/storage/types/options.go index 4c873b45..01f4e5a7 100644 --- a/vendor/github.com/containers/storage/types/options.go +++ b/vendor/github.com/containers/storage/types/options.go @@ -19,9 +19,11 @@ import ( type TomlConfig struct { Storage struct { Driver string `toml:"driver,omitempty"` + DriverPriority []string `toml:"driver_priority,omitempty"` RunRoot string `toml:"runroot,omitempty"` GraphRoot string `toml:"graphroot,omitempty"` RootlessStoragePath string `toml:"rootless_storage_path,omitempty"` + TransientStore bool `toml:"transient_store,omitempty"` Options cfg.OptionsConfig `toml:"options,omitempty"` } `toml:"storage"` } @@ -35,6 +37,14 @@ const ( var ( defaultStoreOptionsOnce sync.Once loadDefaultStoreOptionsErr error + once sync.Once + storeOptions StoreOptions + storeError error + defaultConfigFileSet bool + // defaultConfigFile path to the system wide storage.conf file + defaultConfigFile = SystemConfigFile + // DefaultStoreOptions is a reasonable default set of options. + defaultStoreOptions StoreOptions ) func loadDefaultStoreOptions() { @@ -167,8 +177,8 @@ func defaultStoreOptionsIsolated(rootless bool, rootlessUID int, storageConf str return storageOpts, nil } -// DefaultStoreOptions returns the default storage ops for containers -func DefaultStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) { +// loadStoreOptions returns the default storage ops for containers +func loadStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) { storageConf, err := DefaultConfigFile(rootless && rootlessUID != 0) if err != nil { return defaultStoreOptions, err @@ -176,6 +186,21 @@ func DefaultStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) { return defaultStoreOptionsIsolated(rootless, rootlessUID, storageConf) } +// UpdateOptions should be called iff container engine recieved a SIGHUP, +// otherwise use DefaultStoreOptions +func UpdateStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) { + storeOptions, storeError = loadStoreOptions(rootless, rootlessUID) + return storeOptions, storeError +} + +// DefaultStoreOptions returns the default storage ops for containers +func DefaultStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) { + once.Do(func() { + storeOptions, storeError = loadStoreOptions(rootless, rootlessUID) + }) + return storeOptions, storeError +} + // StoreOptions is used for passing initialization options to GetStore(), for // initializing a Store object and the underlying storage that it controls. type StoreOptions struct { @@ -189,10 +214,16 @@ type StoreOptions struct { // RootlessStoragePath is the storage path for rootless users // default $HOME/.local/share/containers/storage RootlessStoragePath string `toml:"rootless_storage_path"` - // GraphDriverName is the underlying storage driver that we'll be - // using. It only needs to be specified the first time a Store is - // initialized for a given RunRoot and GraphRoot. + // If the driver is not specified, the best suited driver will be picked + // either from GraphDriverPriority, if specified, or from the platform + // dependent priority list (in that order). GraphDriverName string `json:"driver,omitempty"` + // GraphDriverPriority is a list of storage drivers that will be tried + // to initialize the Store for a given RunRoot and GraphRoot unless a + // GraphDriverName is set. + // This list can be used to define a custom order in which the drivers + // will be tried. + GraphDriverPriority []string `json:"driver-priority,omitempty"` // GraphDriverOptions are driver-specific options. GraphDriverOptions []string `json:"driver-options,omitempty"` // UIDMap and GIDMap are used for setting up a container's root filesystem @@ -211,6 +242,8 @@ type StoreOptions struct { PullOptions map[string]string `toml:"pull_options"` // DisableVolatile doesn't allow volatile mounts when it is set. DisableVolatile bool `json:"disable-volatile,omitempty"` + // If transient, don't persist containers over boot (stores db in runroot) + TransientStore bool `json:"transient_store,omitempty"` } // isRootlessDriver returns true if the given storage driver is valid for containers running as non root @@ -267,7 +300,11 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti } } if opts.GraphDriverName == "" { - opts.GraphDriverName = "vfs" + if len(systemOpts.GraphDriverPriority) == 0 { + opts.GraphDriverName = "vfs" + } else { + opts.GraphDriverPriority = systemOpts.GraphDriverPriority + } } if os.Getenv("STORAGE_OPTS") != "" { @@ -336,7 +373,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro } } else { 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 } } @@ -354,8 +391,9 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver") storeOptions.GraphDriverName = overlayDriver } - if storeOptions.GraphDriverName == "" { - logrus.Errorf("The storage 'driver' option must be set in %s to guarantee proper operation", configFile) + storeOptions.GraphDriverPriority = config.Storage.DriverPriority + if storeOptions.GraphDriverName == "" && len(storeOptions.GraphDriverPriority) == 0 { + logrus.Warnf("The storage 'driver' option should be set in %s. A driver was picked automatically.", configFile) } if config.Storage.RunRoot != "" { storeOptions.RunRoot = config.Storage.RunRoot @@ -399,7 +437,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup != "" { mappings, err := idtools.NewIDMappings(config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup) 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 } storeOptions.UIDMap = mappings.UIDs() @@ -429,6 +467,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro } storeOptions.DisableVolatile = config.Storage.Options.DisableVolatile + storeOptions.TransientStore = config.Storage.TransientStore storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, cfg.GetGraphDriverOptions(storeOptions.GraphDriverName, config.Storage.Options)...) diff --git a/vendor/github.com/containers/storage/types/options_darwin.go b/vendor/github.com/containers/storage/types/options_darwin.go index d5ad50bc..eed1a3d9 100644 --- a/vendor/github.com/containers/storage/types/options_darwin.go +++ b/vendor/github.com/containers/storage/types/options_darwin.go @@ -5,13 +5,9 @@ const ( // for rootless path is constructed via getRootlessStorageOpts defaultRunRoot string = "/run/containers/storage" defaultGraphRoot string = "/var/lib/containers/storage" + SystemConfigFile = "/usr/share/containers/storage.conf" ) -// defaultConfigFile path to the system wide storage.conf file var ( - defaultConfigFile = "/usr/share/containers/storage.conf" defaultOverrideConfigFile = "/etc/containers/storage.conf" - defaultConfigFileSet = false - // DefaultStoreOptions is a reasonable default set of options. - defaultStoreOptions StoreOptions ) diff --git a/vendor/github.com/containers/storage/types/options_freebsd.go b/vendor/github.com/containers/storage/types/options_freebsd.go index d5976b6d..afb7ec6b 100644 --- a/vendor/github.com/containers/storage/types/options_freebsd.go +++ b/vendor/github.com/containers/storage/types/options_freebsd.go @@ -5,13 +5,10 @@ const ( // for rootless path is constructed via getRootlessStorageOpts defaultRunRoot string = "/var/run/containers/storage" defaultGraphRoot string = "/var/db/containers/storage" + SystemConfigFile = "/usr/local/share/containers/storage.conf" ) // defaultConfigFile path to the system wide storage.conf file var ( - defaultConfigFile = "/usr/local/share/containers/storage.conf" defaultOverrideConfigFile = "/usr/local/etc/containers/storage.conf" - defaultConfigFileSet = false - // DefaultStoreOptions is a reasonable default set of options. - defaultStoreOptions StoreOptions ) diff --git a/vendor/github.com/containers/storage/types/options_linux.go b/vendor/github.com/containers/storage/types/options_linux.go index d5ad50bc..d44aaf76 100644 --- a/vendor/github.com/containers/storage/types/options_linux.go +++ b/vendor/github.com/containers/storage/types/options_linux.go @@ -5,13 +5,10 @@ const ( // for rootless path is constructed via getRootlessStorageOpts defaultRunRoot string = "/run/containers/storage" defaultGraphRoot string = "/var/lib/containers/storage" + SystemConfigFile = "/usr/share/containers/storage.conf" ) // defaultConfigFile path to the system wide storage.conf file var ( - defaultConfigFile = "/usr/share/containers/storage.conf" defaultOverrideConfigFile = "/etc/containers/storage.conf" - defaultConfigFileSet = false - // DefaultStoreOptions is a reasonable default set of options. - defaultStoreOptions StoreOptions ) diff --git a/vendor/github.com/containers/storage/types/options_windows.go b/vendor/github.com/containers/storage/types/options_windows.go index d5ad50bc..d44aaf76 100644 --- a/vendor/github.com/containers/storage/types/options_windows.go +++ b/vendor/github.com/containers/storage/types/options_windows.go @@ -5,13 +5,10 @@ const ( // for rootless path is constructed via getRootlessStorageOpts defaultRunRoot string = "/run/containers/storage" defaultGraphRoot string = "/var/lib/containers/storage" + SystemConfigFile = "/usr/share/containers/storage.conf" ) // defaultConfigFile path to the system wide storage.conf file var ( - defaultConfigFile = "/usr/share/containers/storage.conf" defaultOverrideConfigFile = "/etc/containers/storage.conf" - defaultConfigFileSet = false - // DefaultStoreOptions is a reasonable default set of options. - defaultStoreOptions StoreOptions ) diff --git a/vendor/github.com/containers/storage/types/utils.go b/vendor/github.com/containers/storage/types/utils.go index c54de763..72c38f86 100644 --- a/vendor/github.com/containers/storage/types/utils.go +++ b/vendor/github.com/containers/storage/types/utils.go @@ -173,6 +173,9 @@ func DefaultConfigFile(rootless bool) (string, error) { return path, nil } if !rootless { + if _, err := os.Stat(defaultOverrideConfigFile); err == nil { + return defaultOverrideConfigFile, nil + } return defaultConfigFile, nil } @@ -193,7 +196,7 @@ func reloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio fi, err := os.Stat(configFile) if err != nil { 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 } diff --git a/vendor/github.com/containers/storage/userns.go b/vendor/github.com/containers/storage/userns.go index 6e200ec1..9c27b5d3 100644 --- a/vendor/github.com/containers/storage/userns.go +++ b/vendor/github.com/containers/storage/userns.go @@ -78,6 +78,10 @@ func (s *store) getAvailableIDs() (*idSet, *idSet, error) { return u, g, nil } +// nobodyUser returns the UID and GID of the "nobody" user. Hardcode its value +// for simplicity. +const nobodyUser = 65534 + // parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and // /etc/group files. func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { @@ -98,10 +102,10 @@ func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { if u.Name == "nobody" { continue } - if u.Uid > size { + if u.Uid > size && u.Uid != nobodyUser { size = u.Uid } - if u.Gid > size { + if u.Gid > size && u.Gid != nobodyUser { size = u.Gid } } @@ -113,7 +117,7 @@ func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { if g.Name == "nobody" { continue } - if g.Gid > size { + if g.Gid > size && g.Gid != nobodyUser { size = g.Gid } } @@ -123,16 +127,9 @@ func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { } // getMaxSizeFromImage returns the maximum ID used by the specified image. -// The layer stores must be already locked. -func (s *store) getMaxSizeFromImage(image *Image, passwdFile, groupFile string) (uint32, error) { - lstore, err := s.LayerStore() - if err != nil { - return 0, err - } - lstores, err := s.ROLayerStores() - if err != nil { - return 0, err - } +// On entry, rlstore must be locked for writing, and lstores must be locked for reading. +func (s *store) getMaxSizeFromImage(image *Image, rlstore rwLayerStore, lstores []roLayerStore, passwdFile, groupFile string) (_ uint32, retErr error) { + layerStores := append([]roLayerStore{rlstore}, lstores...) size := uint32(0) @@ -140,7 +137,7 @@ func (s *store) getMaxSizeFromImage(image *Image, passwdFile, groupFile string) layerName := image.TopLayer outer: for { - for _, ls := range append([]ROLayerStore{lstore}, lstores...) { + for _, ls := range layerStores { layer, err := ls.Get(layerName) if err != nil { continue @@ -167,11 +164,6 @@ outer: return 0, fmt.Errorf("cannot find layer %q", layerName) } - rlstore, err := s.LayerStore() - if err != nil { - return 0, err - } - layerOptions := &LayerOptions{ IDMappingOptions: types.IDMappingOptions{ HostUIDMapping: true, @@ -187,7 +179,15 @@ outer: if err != nil { 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{ MountLabel: "", @@ -200,7 +200,15 @@ outer: if err != nil { return 0, err } - defer rlstore.Unmount(clayer.ID, true) + defer func() { + if _, err2 := rlstore.unmount(clayer.ID, true, false); 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) if userFilesSize > size { @@ -211,7 +219,8 @@ outer: } // getAutoUserNS creates an automatic user namespace -func (s *store) getAutoUserNS(options *types.AutoUserNsOptions, image *Image) ([]idtools.IDMap, []idtools.IDMap, error) { +// If image != nil, On entry, rlstore must be locked for writing, and lstores must be locked for reading. +func (s *store) getAutoUserNS(options *types.AutoUserNsOptions, image *Image, rlstore rwLayerStore, lstores []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) { requestedSize := uint32(0) initialSize := uint32(1) if options.Size > 0 { @@ -250,7 +259,7 @@ func (s *store) getAutoUserNS(options *types.AutoUserNsOptions, image *Image) ([ size = s.autoNsMinSize } if image != nil { - sizeFromImage, err := s.getMaxSizeFromImage(image, options.PasswdFile, options.GroupFile) + sizeFromImage, err := s.getMaxSizeFromImage(image, rlstore, lstores, options.PasswdFile, options.GroupFile) if err != nil { return nil, nil, err } @@ -259,7 +268,7 @@ func (s *store) getAutoUserNS(options *types.AutoUserNsOptions, image *Image) ([ } } if s.autoNsMaxSize > 0 && size > s.autoNsMaxSize { - return nil, nil, fmt.Errorf("the container needs a user namespace with size %q that is bigger than the maximum value allowed with userns=auto %q", size, s.autoNsMaxSize) + return nil, nil, fmt.Errorf("the container needs a user namespace with size %v that is bigger than the maximum value allowed with userns=auto %v", size, s.autoNsMaxSize) } } diff --git a/vendor/github.com/containers/storage/utils.go b/vendor/github.com/containers/storage/utils.go index ae9600e6..7f9e92b9 100644 --- a/vendor/github.com/containers/storage/utils.go +++ b/vendor/github.com/containers/storage/utils.go @@ -2,6 +2,7 @@ package storage import ( "fmt" + "unicode" "github.com/containers/storage/types" ) @@ -72,3 +73,15 @@ func applyNameOperation(oldNames []string, opParameters []string, op updateNameO } return dedupeNames(result), nil } + +func nameLooksLikeID(name string) bool { + if len(name) != 64 { + return false + } + for _, c := range name { + if !unicode.Is(unicode.ASCII_Hex_Digit, c) { + return false + } + } + return true +} diff --git a/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen_example.go b/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen_example.go new file mode 100644 index 00000000..2065c5ef --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen_example.go @@ -0,0 +1,57 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//go:build linux +// +build linux + +package dlopen + +// #include +// #include +// +// int +// my_strlen(void *f, const char *s) +// { +// size_t (*strlen)(const char *); +// +// strlen = (size_t (*)(const char *))f; +// return strlen(s); +// } +import "C" + +import ( + "fmt" + "unsafe" +) + +func strlen(libs []string, s string) (int, error) { + h, err := GetHandle(libs) + if err != nil { + return -1, fmt.Errorf(`couldn't get a handle to the library: %v`, err) + } + defer h.Close() + + f := "strlen" + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + + strlen, err := h.GetSymbolPointer(f) + if err != nil { + return -1, fmt.Errorf(`couldn't get symbol %q: %v`, f, err) + } + + len := C.my_strlen(strlen, cs) + + return int(len), nil +} diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go index 439ad287..c5b23a81 100644 --- a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go @@ -69,6 +69,58 @@ func Enabled() bool { return true } +// StderrIsJournalStream returns whether the process stderr is connected +// to the Journal's stream transport. +// +// This can be used for automatic protocol upgrading described in [Journal Native Protocol]. +// +// Returns true if JOURNAL_STREAM environment variable is present, +// and stderr's device and inode numbers match it. +// +// Error is returned if unexpected error occurs: e.g. if JOURNAL_STREAM environment variable +// is present, but malformed, fstat syscall fails, etc. +// +// [Journal Native Protocol]: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/#automatic-protocol-upgrading +func StderrIsJournalStream() (bool, error) { + return fdIsJournalStream(syscall.Stderr) +} + +// StdoutIsJournalStream returns whether the process stdout is connected +// to the Journal's stream transport. +// +// Returns true if JOURNAL_STREAM environment variable is present, +// and stdout's device and inode numbers match it. +// +// Error is returned if unexpected error occurs: e.g. if JOURNAL_STREAM environment variable +// is present, but malformed, fstat syscall fails, etc. +// +// Most users should probably use [StderrIsJournalStream]. +func StdoutIsJournalStream() (bool, error) { + return fdIsJournalStream(syscall.Stdout) +} + +func fdIsJournalStream(fd int) (bool, error) { + journalStream := os.Getenv("JOURNAL_STREAM") + if journalStream == "" { + return false, nil + } + + var expectedStat syscall.Stat_t + _, err := fmt.Sscanf(journalStream, "%d:%d", &expectedStat.Dev, &expectedStat.Ino) + if err != nil { + return false, fmt.Errorf("failed to parse JOURNAL_STREAM=%q: %v", journalStream, err) + } + + var stat syscall.Stat_t + err = syscall.Fstat(fd, &stat) + if err != nil { + return false, err + } + + match := stat.Dev == expectedStat.Dev && stat.Ino == expectedStat.Ino + return match, nil +} + // Send a message to the local systemd journal. vars is a map of journald // fields to values. Fields must be composed of uppercase letters, numbers, // and underscores, but must not start with an underscore. Within these diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go index 677aca68..322e41e7 100644 --- a/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go @@ -33,3 +33,11 @@ func Enabled() bool { func Send(message string, priority Priority, vars map[string]string) error { return errors.New("could not initialize socket to journald") } + +func StderrIsJournalStream() (bool, error) { + return false, nil +} + +func StdoutIsJournalStream() (bool, error) { + return false, nil +} diff --git a/vendor/github.com/cyberphone/json-canonicalization/LICENSE b/vendor/github.com/cyberphone/json-canonicalization/LICENSE new file mode 100644 index 00000000..59121159 --- /dev/null +++ b/vendor/github.com/cyberphone/json-canonicalization/LICENSE @@ -0,0 +1,13 @@ + Copyright 2018 Anders Rundgren + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/es6numfmt.go b/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/es6numfmt.go new file mode 100644 index 00000000..92574a3f --- /dev/null +++ b/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/es6numfmt.go @@ -0,0 +1,71 @@ +// +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This package converts numbers in IEEE-754 double precision into the +// format specified for JSON in EcmaScript Version 6 and forward. +// The core application for this is canonicalization: +// https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 + +package jsoncanonicalizer + +import ( + "errors" + "math" + "strconv" + "strings" +) + +const invalidPattern uint64 = 0x7ff0000000000000 + +func NumberToJSON(ieeeF64 float64) (res string, err error) { + ieeeU64 := math.Float64bits(ieeeF64) + + // Special case: NaN and Infinity are invalid in JSON + if (ieeeU64 & invalidPattern) == invalidPattern { + return "null", errors.New("Invalid JSON number: " + strconv.FormatUint(ieeeU64, 16)) + } + + // Special case: eliminate "-0" as mandated by the ES6-JSON/JCS specifications + if ieeeF64 == 0 { // Right, this line takes both -0 and 0 + return "0", nil + } + + // Deal with the sign separately + var sign string = "" + if ieeeF64 < 0 { + ieeeF64 =-ieeeF64 + sign = "-" + } + + // ES6 has a unique "g" format + var format byte = 'e' + if ieeeF64 < 1e+21 && ieeeF64 >= 1e-6 { + format = 'f' + } + + // The following should do the trick: + es6Formatted := strconv.FormatFloat(ieeeF64, format, -1, 64) + + // Minor cleanup + exponent := strings.IndexByte(es6Formatted, 'e') + if exponent > 0 { + // Go outputs "1e+09" which must be rewritten as "1e+9" + if es6Formatted[exponent + 2] == '0' { + es6Formatted = es6Formatted[:exponent + 2] + es6Formatted[exponent + 3:] + } + } + return sign + es6Formatted, nil +} diff --git a/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/jsoncanonicalizer.go b/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/jsoncanonicalizer.go new file mode 100644 index 00000000..661f4105 --- /dev/null +++ b/vendor/github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer/jsoncanonicalizer.go @@ -0,0 +1,378 @@ +// +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This package transforms JSON data in UTF-8 according to: +// https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 + +package jsoncanonicalizer + +import ( + "errors" + "container/list" + "fmt" + "strconv" + "strings" + "unicode/utf16" +) + +type nameValueType struct { + name string + sortKey []uint16 + value string +} + +// JSON standard escapes (modulo \u) +var asciiEscapes = []byte{'\\', '"', 'b', 'f', 'n', 'r', 't'} +var binaryEscapes = []byte{'\\', '"', '\b', '\f', '\n', '\r', '\t'} + +// JSON literals +var literals = []string{"true", "false", "null"} + +func Transform(jsonData []byte) (result []byte, e error) { + + // JSON data MUST be UTF-8 encoded + var jsonDataLength int = len(jsonData) + + // Current pointer in jsonData + var index int = 0 + + // "Forward" declarations are needed for closures referring each other + var parseElement func() string + var parseSimpleType func() string + var parseQuotedString func() string + var parseObject func() string + var parseArray func() string + + var globalError error = nil + + checkError := func(e error) { + // We only honor the first reported error + if globalError == nil { + globalError = e + } + } + + setError := func(msg string) { + checkError(errors.New(msg)) + } + + isWhiteSpace := func(c byte) bool { + return c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09 + } + + nextChar := func() byte { + if index < jsonDataLength { + c := jsonData[index] + if c > 0x7f { + setError("Unexpected non-ASCII character") + } + index++ + return c + } + setError("Unexpected EOF reached") + return '"' + } + + scan := func() byte { + for { + c := nextChar() + if isWhiteSpace(c) { + continue; + } + return c + } + } + + scanFor := func(expected byte) { + c := scan() + if c != expected { + setError("Expected '" + string(expected) + "' but got '" + string(c) + "'") + } + } + + getUEscape := func() rune { + start := index + nextChar() + nextChar() + nextChar() + nextChar() + if globalError != nil { + return 0 + } + u16, err := strconv.ParseUint(string(jsonData[start:index]), 16, 64) + checkError(err) + return rune(u16) + } + + testNextNonWhiteSpaceChar := func() byte { + save := index + c := scan() + index = save + return c + } + + decorateString := func(rawUTF8 string) string { + var quotedString strings.Builder + quotedString.WriteByte('"') + CoreLoop: + for _, c := range []byte(rawUTF8) { + // Is this within the JSON standard escapes? + for i, esc := range binaryEscapes { + if esc == c { + quotedString.WriteByte('\\') + quotedString.WriteByte(asciiEscapes[i]) + continue CoreLoop + } + } + if c < 0x20 { + // Other ASCII control characters must be escaped with \uhhhh + quotedString.WriteString(fmt.Sprintf("\\u%04x", c)) + } else { + quotedString.WriteByte(c) + } + } + quotedString.WriteByte('"') + return quotedString.String() + } + + parseQuotedString = func() string { + var rawString strings.Builder + CoreLoop: + for globalError == nil { + var c byte + if index < jsonDataLength { + c = jsonData[index] + index++ + } else { + nextChar() + break + } + if (c == '"') { + break; + } + if c < ' ' { + setError("Unterminated string literal") + } else if c == '\\' { + // Escape sequence + c = nextChar() + if c == 'u' { + // The \u escape + firstUTF16 := getUEscape() + if utf16.IsSurrogate(firstUTF16) { + // If the first UTF-16 code unit has a certain value there must be + // another succeeding UTF-16 code unit as well + if nextChar() != '\\' || nextChar() != 'u' { + setError("Missing surrogate") + } else { + // Output the UTF-32 code point as UTF-8 + rawString.WriteRune(utf16.DecodeRune(firstUTF16, getUEscape())) + } + } else { + // Single UTF-16 code identical to UTF-32. Output as UTF-8 + rawString.WriteRune(firstUTF16) + } + } else if c == '/' { + // Benign but useless escape + rawString.WriteByte('/') + } else { + // The JSON standard escapes + for i, esc := range asciiEscapes { + if esc == c { + rawString.WriteByte(binaryEscapes[i]) + continue CoreLoop + } + } + setError("Unexpected escape: \\" + string(c)) + } + } else { + // Just an ordinary ASCII character alternatively a UTF-8 byte + // outside of ASCII. + // Note that properly formatted UTF-8 never clashes with ASCII + // making byte per byte search for ASCII break characters work + // as expected. + rawString.WriteByte(c) + } + } + return rawString.String() + } + + parseSimpleType = func() string { + var token strings.Builder + index-- + for globalError == nil { + c := testNextNonWhiteSpaceChar() + if c == ',' || c == ']' || c == '}' { + break; + } + c = nextChar() + if isWhiteSpace(c) { + break + } + token.WriteByte(c) + } + if token.Len() == 0 { + setError("Missing argument") + } + value := token.String() + // Is it a JSON literal? + for _, literal := range literals { + if literal == value { + return literal + } + } + // Apparently not so we assume that it is a I-JSON number + ieeeF64, err := strconv.ParseFloat(value, 64) + checkError(err) + value, err = NumberToJSON(ieeeF64) + checkError(err) + return value + } + + parseElement = func() string { + switch scan() { + case '{': + return parseObject() + case '"': + return decorateString(parseQuotedString()) + case '[': + return parseArray() + default: + return parseSimpleType() + } + } + + parseArray = func() string { + var arrayData strings.Builder + arrayData.WriteByte('[') + var next bool = false + for globalError == nil && testNextNonWhiteSpaceChar() != ']' { + if next { + scanFor(',') + arrayData.WriteByte(',') + } else { + next = true + } + arrayData.WriteString(parseElement()) + } + scan() + arrayData.WriteByte(']') + return arrayData.String() + } + + lexicographicallyPrecedes := func(sortKey []uint16, e *list.Element) bool { + // Find the minimum length of the sortKeys + oldSortKey := e.Value.(nameValueType).sortKey + minLength := len(oldSortKey) + if minLength > len(sortKey) { + minLength = len(sortKey) + } + for q := 0; q < minLength; q++ { + diff := int(sortKey[q]) - int(oldSortKey[q]) + if diff < 0 { + // Smaller => Precedes + return true + } else if diff > 0 { + // Bigger => No match + return false + } + // Still equal => Continue + } + // The sortKeys compared equal up to minLength + if len(sortKey) < len(oldSortKey) { + // Shorter => Precedes + return true + } + if len(sortKey) == len(oldSortKey) { + setError("Duplicate key: " + e.Value.(nameValueType).name) + } + // Longer => No match + return false + } + + parseObject = func() string { + nameValueList := list.New() + var next bool = false + CoreLoop: + for globalError == nil && testNextNonWhiteSpaceChar() != '}' { + if next { + scanFor(',') + } + next = true + scanFor('"') + rawUTF8 := parseQuotedString() + if globalError != nil { + break; + } + // Sort keys on UTF-16 code units + // Since UTF-8 doesn't have endianess this is just a value transformation + // In the Go case the transformation is UTF-8 => UTF-32 => UTF-16 + sortKey := utf16.Encode([]rune(rawUTF8)) + scanFor(':') + nameValue := nameValueType{rawUTF8, sortKey, parseElement()} + for e := nameValueList.Front(); e != nil; e = e.Next() { + // Check if the key is smaller than a previous key + if lexicographicallyPrecedes(sortKey, e) { + // Precedes => Insert before and exit sorting + nameValueList.InsertBefore(nameValue, e) + continue CoreLoop + } + // Continue searching for a possibly succeeding sortKey + // (which is straightforward since the list is ordered) + } + // The sortKey is either the first or is succeeding all previous sortKeys + nameValueList.PushBack(nameValue) + } + // Scan away '}' + scan() + // Now everything is sorted so we can properly serialize the object + var objectData strings.Builder + objectData.WriteByte('{') + next = false + for e := nameValueList.Front(); e != nil; e = e.Next() { + if next { + objectData.WriteByte(',') + } + next = true + nameValue := e.Value.(nameValueType) + objectData.WriteString(decorateString(nameValue.name)) + objectData.WriteByte(':') + objectData.WriteString(nameValue.value) + } + objectData.WriteByte('}') + return objectData.String() + } + + ///////////////////////////////////////////////// + // This is where Transform actually begins... // + ///////////////////////////////////////////////// + var transformed string + + if testNextNonWhiteSpaceChar() == '[' { + scan() + transformed = parseArray() + } else { + scanFor('{') + transformed = parseObject() + } + for index < jsonDataLength { + if !isWhiteSpace(jsonData[index]) { + setError("Improperly terminated JSON object") + break; + } + index++ + } + return []byte(transformed), globalError +} \ No newline at end of file diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml index 0756ff1b..c24f57bc 100644 --- a/vendor/github.com/docker/docker/api/swagger.yaml +++ b/vendor/github.com/docker/docker/api/swagger.yaml @@ -24,7 +24,7 @@ info: title: "Docker Engine API" version: "1.41" x-logo: - url: "https://docs.docker.com/images/logo-docker-main.png" + url: "https://docs.docker.com/assets/images/logo-docker-main.png" description: | The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker diff --git a/vendor/github.com/docker/docker/api/types/filters/parse.go b/vendor/github.com/docker/docker/api/types/filters/parse.go index 4bc91cff..63db4c61 100644 --- a/vendor/github.com/docker/docker/api/types/filters/parse.go +++ b/vendor/github.com/docker/docker/api/types/filters/parse.go @@ -48,7 +48,7 @@ func (args Args) Keys() []string { // MarshalJSON returns a JSON byte representation of the Args func (args Args) MarshalJSON() ([]byte, error) { if len(args.fields) == 0 { - return []byte{}, nil + return []byte("{}"), nil } return json.Marshal(args.fields) } @@ -106,9 +106,6 @@ func FromJSON(p string) (Args, error) { // UnmarshalJSON populates the Args from JSON encode bytes func (args Args) UnmarshalJSON(raw []byte) error { - if len(raw) == 0 { - return nil - } return json.Unmarshal(raw, &args.fields) } diff --git a/vendor/github.com/fsouza/go-dockerclient/auth.go b/vendor/github.com/fsouza/go-dockerclient/auth.go index d867c473..37c1cbe2 100644 --- a/vendor/github.com/fsouza/go-dockerclient/auth.go +++ b/vendor/github.com/fsouza/go-dockerclient/auth.go @@ -6,6 +6,7 @@ package docker import ( "bytes" + "context" "encoding/base64" "encoding/json" "errors" @@ -262,11 +263,21 @@ type AuthStatus struct { // // See https://goo.gl/6nsZkH for more details. func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) { + return c.AuthCheckWithContext(conf, context.TODO()) +} + +// AuthCheckWithContext validates the given credentials. It returns nil if successful. The context object +// can be used to cancel the request. +// +// For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty. +// +// See https://goo.gl/6nsZkH for more details. +func (c *Client) AuthCheckWithContext(conf *AuthConfiguration, ctx context.Context) (AuthStatus, error) { var authStatus AuthStatus if conf == nil { return authStatus, errors.New("conf is nil") } - resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf}) + resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf, context: ctx}) if err != nil { return authStatus, err } diff --git a/vendor/github.com/fsouza/go-dockerclient/client.go b/vendor/github.com/fsouza/go-dockerclient/client.go index 1bbf611a..2d8d019c 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client.go +++ b/vendor/github.com/fsouza/go-dockerclient/client.go @@ -416,7 +416,7 @@ func (c *Client) getServerAPIVersionString() (version string, err error) { if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("received unexpected status %d while trying to retrieve the server version", resp.StatusCode) } - var versionResponse map[string]interface{} + var versionResponse map[string]any if err := json.NewDecoder(resp.Body).Decode(&versionResponse); err != nil { return "", err } @@ -427,7 +427,7 @@ func (c *Client) getServerAPIVersionString() (version string, err error) { } type doOptions struct { - data interface{} + data any forceJSON bool headers map[string]string context context.Context @@ -485,7 +485,7 @@ func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, e return nil, chooseError(ctx, err) } - if resp.StatusCode < 200 || resp.StatusCode >= 400 { + if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { return nil, newError(resp) } return resp, nil @@ -710,7 +710,7 @@ type hijackOptions struct { in io.Reader stdout io.Writer stderr io.Writer - data interface{} + data any } // CloseWaiter is an interface with methods for closing the underlying resource @@ -873,7 +873,7 @@ func (c *Client) getURL(path string) string { return fmt.Sprintf("%s%s", urlStr, path) } -func (c *Client) getPath(basepath string, opts interface{}) (string, error) { +func (c *Client) getPath(basepath string, opts any) (string, error) { queryStr, requiredAPIVersion := queryStringVersion(opts) return c.pathVersionCheck(basepath, queryStr, requiredAPIVersion) } @@ -912,7 +912,7 @@ func (c *Client) getFakeNativeURL(path string) string { return fmt.Sprintf("%s%s", urlStr, path) } -func queryStringVersion(opts interface{}) (string, APIVersion) { +func queryStringVersion(opts any) (string, APIVersion) { if opts == nil { return "", nil } @@ -951,7 +951,7 @@ func queryStringVersion(opts interface{}) (string, APIVersion) { return items.Encode(), apiVersion } -func queryString(opts interface{}) string { +func queryString(opts any) string { s, _ := queryStringVersion(opts) return s } diff --git a/vendor/github.com/fsouza/go-dockerclient/client_unix.go b/vendor/github.com/fsouza/go-dockerclient/client_unix.go index e9f13f39..ee4dba2a 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client_unix.go +++ b/vendor/github.com/fsouza/go-dockerclient/client_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package docker diff --git a/vendor/github.com/fsouza/go-dockerclient/env.go b/vendor/github.com/fsouza/go-dockerclient/env.go index 0f2e72f1..67bb99f5 100644 --- a/vendor/github.com/fsouza/go-dockerclient/env.go +++ b/vendor/github.com/fsouza/go-dockerclient/env.go @@ -79,7 +79,7 @@ func (env *Env) SetInt64(key string, value int64) { // GetJSON unmarshals the value of the provided key in the provided iface. // // iface is a value that can be provided to the json.Unmarshal function. -func (env *Env) GetJSON(key string, iface interface{}) error { +func (env *Env) GetJSON(key string, iface any) error { sval := env.Get(key) if sval == "" { return nil @@ -89,7 +89,7 @@ func (env *Env) GetJSON(key string, iface interface{}) error { // SetJSON marshals the given value to JSON format and stores it using the // provided key. -func (env *Env) SetJSON(key string, value interface{}) error { +func (env *Env) SetJSON(key string, value any) error { sval, err := json.Marshal(value) if err != nil { return err @@ -131,7 +131,7 @@ func (env *Env) Set(key, value string) { // // If `src` cannot be decoded as a json dictionary, an error is returned. func (env *Env) Decode(src io.Reader) error { - m := make(map[string]interface{}) + m := make(map[string]any) if err := json.NewDecoder(src).Decode(&m); err != nil { return err } @@ -142,7 +142,7 @@ func (env *Env) Decode(src io.Reader) error { } // SetAuto will try to define the Set* method to call based on the given value. -func (env *Env) SetAuto(key string, value interface{}) { +func (env *Env) SetAuto(key string, value any) { if fval, ok := value.(float64); ok { env.SetInt64(key, int64(fval)) } else if sval, ok := value.(string); ok { diff --git a/vendor/github.com/fsouza/go-dockerclient/image.go b/vendor/github.com/fsouza/go-dockerclient/image.go index 85d7d6a7..3576d8e6 100644 --- a/vendor/github.com/fsouza/go-dockerclient/image.go +++ b/vendor/github.com/fsouza/go-dockerclient/image.go @@ -328,7 +328,7 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error return c.createImage(&opts, headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } -func (c *Client) createImage(opts interface{}, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error { +func (c *Client) createImage(opts any, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error { url, err := c.getPath("/images/create", opts) if err != nil { return err diff --git a/vendor/github.com/fsouza/go-dockerclient/network.go b/vendor/github.com/fsouza/go-dockerclient/network.go index f3ce4ce9..b41c964b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/network.go +++ b/vendor/github.com/fsouza/go-dockerclient/network.go @@ -113,20 +113,20 @@ func (c *Client) NetworkInfo(id string) (*Network, error) { // // See https://goo.gl/6GugX3 for more details. type CreateNetworkOptions struct { - Name string `json:"Name" yaml:"Name" toml:"Name"` - Driver string `json:"Driver" yaml:"Driver" toml:"Driver"` - Scope string `json:"Scope" yaml:"Scope" toml:"Scope"` - IPAM *IPAMOptions `json:"IPAM,omitempty" yaml:"IPAM" toml:"IPAM"` - ConfigFrom *NetworkConfigFrom `json:"ConfigFrom,omitempty" yaml:"ConfigFrom" toml:"ConfigFrom"` - Options map[string]interface{} `json:"Options" yaml:"Options" toml:"Options"` - Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"` - CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"` - Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"` - EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"` - Attachable bool `json:"Attachable" yaml:"Attachable" toml:"Attachable"` - ConfigOnly bool `json:"ConfigOnly" yaml:"ConfigOnly" toml:"ConfigOnly"` - Ingress bool `json:"Ingress" yaml:"Ingress" toml:"Ingress"` - Context context.Context `json:"-"` + Name string `json:"Name" yaml:"Name" toml:"Name"` + Driver string `json:"Driver" yaml:"Driver" toml:"Driver"` + Scope string `json:"Scope" yaml:"Scope" toml:"Scope"` + IPAM *IPAMOptions `json:"IPAM,omitempty" yaml:"IPAM" toml:"IPAM"` + ConfigFrom *NetworkConfigFrom `json:"ConfigFrom,omitempty" yaml:"ConfigFrom" toml:"ConfigFrom"` + Options map[string]any `json:"Options" yaml:"Options" toml:"Options"` + Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"` + CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"` + Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"` + EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"` + Attachable bool `json:"Attachable" yaml:"Attachable" toml:"Attachable"` + ConfigOnly bool `json:"ConfigOnly" yaml:"ConfigOnly" toml:"ConfigOnly"` + Ingress bool `json:"Ingress" yaml:"Ingress" toml:"Ingress"` + Context context.Context `json:"-"` } // NetworkConfigFrom is used in network creation for specifying the source of a diff --git a/vendor/github.com/fsouza/go-dockerclient/volume.go b/vendor/github.com/fsouza/go-dockerclient/volume.go index 9f8a435c..92dcb71b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/volume.go +++ b/vendor/github.com/fsouza/go-dockerclient/volume.go @@ -51,7 +51,7 @@ func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) { return nil, err } defer resp.Body.Close() - m := make(map[string]interface{}) + m := make(map[string]any) if err = json.NewDecoder(resp.Body).Decode(&m); err != nil { return nil, err } diff --git a/vendor/github.com/go-openapi/analysis/.codecov.yml b/vendor/github.com/go-openapi/analysis/.codecov.yml new file mode 100644 index 00000000..841c4281 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.codecov.yml @@ -0,0 +1,5 @@ +coverage: + status: + patch: + default: + target: 80% diff --git a/vendor/github.com/go-openapi/analysis/.gitattributes b/vendor/github.com/go-openapi/analysis/.gitattributes new file mode 100644 index 00000000..d020be8e --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.gitattributes @@ -0,0 +1,2 @@ +*.go text eol=lf + diff --git a/vendor/github.com/go-openapi/analysis/.gitignore b/vendor/github.com/go-openapi/analysis/.gitignore new file mode 100644 index 00000000..87c3bd3e --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +coverage.txt +*.cov +.idea diff --git a/vendor/github.com/go-openapi/analysis/.golangci.yml b/vendor/github.com/go-openapi/analysis/.golangci.yml new file mode 100644 index 00000000..e24a6c14 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.golangci.yml @@ -0,0 +1,56 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 40 + gocognit: + min-complexity: 40 + maligned: + suggest-new: true + dupl: + threshold: 150 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoglobals + - gochecknoinits + # scopelint is useful, but also reports false positives + # that unfortunately can't be disabled. So we disable the + # linter rather than changing code that works. + # see: https://github.com/kyoh86/scopelint/issues/4 + - scopelint + - godox + - gocognit + #- whitespace + - wsl + - funlen + - testpackage + - wrapcheck + #- nlreturn + - gomnd + - goerr113 + - exhaustivestruct + #- errorlint + #- nestif + - gofumpt + - godot + - gci + - dogsled + - paralleltest + - tparallel + - thelper + - ifshort + - forbidigo + - cyclop + - varnamelen + - exhaustruct + - nonamedreturns + - nosnakecase diff --git a/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/analysis/LICENSE b/vendor/github.com/go-openapi/analysis/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/analysis/README.md b/vendor/github.com/go-openapi/analysis/README.md new file mode 100644 index 00000000..aad6da10 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/README.md @@ -0,0 +1,31 @@ +# OpenAPI initiative analysis + +[![Build Status](https://travis-ci.org/go-openapi/analysis.svg?branch=master)](https://travis-ci.org/go-openapi/analysis) +[![Build status](https://ci.appveyor.com/api/projects/status/x377t5o9ennm847o/branch/master?svg=true)](https://ci.appveyor.com/project/casualjim/go-openapi/analysis/branch/master) +[![codecov](https://codecov.io/gh/go-openapi/analysis/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/analysis) +[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/analysis/master/LICENSE) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/analysis.svg)](https://pkg.go.dev/github.com/go-openapi/analysis) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/analysis)](https://goreportcard.com/report/github.com/go-openapi/analysis) + + +A foundational library to analyze an OAI specification document for easier reasoning about the content. + +## What's inside? + +* A analyzer providing methods to walk the functional content of a specification +* A spec flattener producing a self-contained document bundle, while preserving `$ref`s +* A spec merger ("mixin") to merge several spec documents into a primary spec +* A spec "fixer" ensuring that response descriptions are non empty + +[Documentation](https://godoc.org/github.com/go-openapi/analysis) + +## FAQ + +* Does this library support OpenAPI 3? + +> No. +> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0). +> There is no plan to make it evolve toward supporting OpenAPI 3.x. +> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story. +> diff --git a/vendor/github.com/go-openapi/analysis/analyzer.go b/vendor/github.com/go-openapi/analysis/analyzer.go new file mode 100644 index 00000000..c17aee1b --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/analyzer.go @@ -0,0 +1,1064 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analysis + +import ( + "fmt" + slashpath "path" + "strconv" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +type referenceAnalysis struct { + schemas map[string]spec.Ref + responses map[string]spec.Ref + parameters map[string]spec.Ref + items map[string]spec.Ref + headerItems map[string]spec.Ref + parameterItems map[string]spec.Ref + allRefs map[string]spec.Ref + pathItems map[string]spec.Ref +} + +func (r *referenceAnalysis) addRef(key string, ref spec.Ref) { + r.allRefs["#"+key] = ref +} + +func (r *referenceAnalysis) addItemsRef(key string, items *spec.Items, location string) { + r.items["#"+key] = items.Ref + r.addRef(key, items.Ref) + if location == "header" { + // NOTE: in swagger 2.0, headers and parameters (but not body param schemas) are simple schemas + // and $ref are not supported here. However it is possible to analyze this. + r.headerItems["#"+key] = items.Ref + } else { + r.parameterItems["#"+key] = items.Ref + } +} + +func (r *referenceAnalysis) addSchemaRef(key string, ref SchemaRef) { + r.schemas["#"+key] = ref.Schema.Ref + r.addRef(key, ref.Schema.Ref) +} + +func (r *referenceAnalysis) addResponseRef(key string, resp *spec.Response) { + r.responses["#"+key] = resp.Ref + r.addRef(key, resp.Ref) +} + +func (r *referenceAnalysis) addParamRef(key string, param *spec.Parameter) { + r.parameters["#"+key] = param.Ref + r.addRef(key, param.Ref) +} + +func (r *referenceAnalysis) addPathItemRef(key string, pathItem *spec.PathItem) { + r.pathItems["#"+key] = pathItem.Ref + r.addRef(key, pathItem.Ref) +} + +type patternAnalysis struct { + parameters map[string]string + headers map[string]string + items map[string]string + schemas map[string]string + allPatterns map[string]string +} + +func (p *patternAnalysis) addPattern(key, pattern string) { + p.allPatterns["#"+key] = pattern +} + +func (p *patternAnalysis) addParameterPattern(key, pattern string) { + p.parameters["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addHeaderPattern(key, pattern string) { + p.headers["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addItemsPattern(key, pattern string) { + p.items["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addSchemaPattern(key, pattern string) { + p.schemas["#"+key] = pattern + p.addPattern(key, pattern) +} + +type enumAnalysis struct { + parameters map[string][]interface{} + headers map[string][]interface{} + items map[string][]interface{} + schemas map[string][]interface{} + allEnums map[string][]interface{} +} + +func (p *enumAnalysis) addEnum(key string, enum []interface{}) { + p.allEnums["#"+key] = enum +} + +func (p *enumAnalysis) addParameterEnum(key string, enum []interface{}) { + p.parameters["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addHeaderEnum(key string, enum []interface{}) { + p.headers["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addItemsEnum(key string, enum []interface{}) { + p.items["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addSchemaEnum(key string, enum []interface{}) { + p.schemas["#"+key] = enum + p.addEnum(key, enum) +} + +// New takes a swagger spec object and returns an analyzed spec document. +// The analyzed document contains a number of indices that make it easier to +// reason about semantics of a swagger specification for use in code generation +// or validation etc. +func New(doc *spec.Swagger) *Spec { + a := &Spec{ + spec: doc, + references: referenceAnalysis{}, + patterns: patternAnalysis{}, + enums: enumAnalysis{}, + } + a.reset() + a.initialize() + + return a +} + +// Spec is an analyzed specification object. It takes a swagger spec object and turns it into a registry +// with a bunch of utility methods to act on the information in the spec. +type Spec struct { + spec *spec.Swagger + consumes map[string]struct{} + produces map[string]struct{} + authSchemes map[string]struct{} + operations map[string]map[string]*spec.Operation + references referenceAnalysis + patterns patternAnalysis + enums enumAnalysis + allSchemas map[string]SchemaRef + allOfs map[string]SchemaRef +} + +func (s *Spec) reset() { + s.consumes = make(map[string]struct{}, 150) + s.produces = make(map[string]struct{}, 150) + s.authSchemes = make(map[string]struct{}, 150) + s.operations = make(map[string]map[string]*spec.Operation, 150) + s.allSchemas = make(map[string]SchemaRef, 150) + s.allOfs = make(map[string]SchemaRef, 150) + s.references.schemas = make(map[string]spec.Ref, 150) + s.references.pathItems = make(map[string]spec.Ref, 150) + s.references.responses = make(map[string]spec.Ref, 150) + s.references.parameters = make(map[string]spec.Ref, 150) + s.references.items = make(map[string]spec.Ref, 150) + s.references.headerItems = make(map[string]spec.Ref, 150) + s.references.parameterItems = make(map[string]spec.Ref, 150) + s.references.allRefs = make(map[string]spec.Ref, 150) + s.patterns.parameters = make(map[string]string, 150) + s.patterns.headers = make(map[string]string, 150) + s.patterns.items = make(map[string]string, 150) + s.patterns.schemas = make(map[string]string, 150) + s.patterns.allPatterns = make(map[string]string, 150) + s.enums.parameters = make(map[string][]interface{}, 150) + s.enums.headers = make(map[string][]interface{}, 150) + s.enums.items = make(map[string][]interface{}, 150) + s.enums.schemas = make(map[string][]interface{}, 150) + s.enums.allEnums = make(map[string][]interface{}, 150) +} + +func (s *Spec) reload() { + s.reset() + s.initialize() +} + +func (s *Spec) initialize() { + for _, c := range s.spec.Consumes { + s.consumes[c] = struct{}{} + } + for _, c := range s.spec.Produces { + s.produces[c] = struct{}{} + } + for _, ss := range s.spec.Security { + for k := range ss { + s.authSchemes[k] = struct{}{} + } + } + for path, pathItem := range s.AllPaths() { + s.analyzeOperations(path, &pathItem) //#nosec + } + + for name, parameter := range s.spec.Parameters { + refPref := slashpath.Join("/parameters", jsonpointer.Escape(name)) + if parameter.Items != nil { + s.analyzeItems("items", parameter.Items, refPref, "parameter") + } + if parameter.In == "body" && parameter.Schema != nil { + s.analyzeSchema("schema", parameter.Schema, refPref) + } + if parameter.Pattern != "" { + s.patterns.addParameterPattern(refPref, parameter.Pattern) + } + if len(parameter.Enum) > 0 { + s.enums.addParameterEnum(refPref, parameter.Enum) + } + } + + for name, response := range s.spec.Responses { + refPref := slashpath.Join("/responses", jsonpointer.Escape(name)) + for k, v := range response.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + if v.Items != nil { + s.analyzeItems("items", v.Items, hRefPref, "header") + } + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + if len(v.Enum) > 0 { + s.enums.addHeaderEnum(hRefPref, v.Enum) + } + } + if response.Schema != nil { + s.analyzeSchema("schema", response.Schema, refPref) + } + } + + for name := range s.spec.Definitions { + schema := s.spec.Definitions[name] + s.analyzeSchema(name, &schema, "/definitions") + } + // TODO: after analyzing all things and flattening schemas etc + // resolve all the collected references to their final representations + // best put in a separate method because this could get expensive +} + +func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) { + // TODO: resolve refs here? + // Currently, operations declared via pathItem $ref are known only after expansion + op := pi + if pi.Ref.String() != "" { + key := slashpath.Join("/paths", jsonpointer.Escape(path)) + s.references.addPathItemRef(key, pi) + } + s.analyzeOperation("GET", path, op.Get) + s.analyzeOperation("PUT", path, op.Put) + s.analyzeOperation("POST", path, op.Post) + s.analyzeOperation("PATCH", path, op.Patch) + s.analyzeOperation("DELETE", path, op.Delete) + s.analyzeOperation("HEAD", path, op.Head) + s.analyzeOperation("OPTIONS", path, op.Options) + for i, param := range op.Parameters { + refPref := slashpath.Join("/paths", jsonpointer.Escape(path), "parameters", strconv.Itoa(i)) + if param.Ref.String() != "" { + s.references.addParamRef(refPref, ¶m) //#nosec + } + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } + if len(param.Enum) > 0 { + s.enums.addParameterEnum(refPref, param.Enum) + } + if param.Items != nil { + s.analyzeItems("items", param.Items, refPref, "parameter") + } + if param.Schema != nil { + s.analyzeSchema("schema", param.Schema, refPref) + } + } +} + +func (s *Spec) analyzeItems(name string, items *spec.Items, prefix, location string) { + if items == nil { + return + } + refPref := slashpath.Join(prefix, name) + s.analyzeItems(name, items.Items, refPref, location) + if items.Ref.String() != "" { + s.references.addItemsRef(refPref, items, location) + } + if items.Pattern != "" { + s.patterns.addItemsPattern(refPref, items.Pattern) + } + if len(items.Enum) > 0 { + s.enums.addItemsEnum(refPref, items.Enum) + } +} + +func (s *Spec) analyzeParameter(prefix string, i int, param spec.Parameter) { + refPref := slashpath.Join(prefix, "parameters", strconv.Itoa(i)) + if param.Ref.String() != "" { + s.references.addParamRef(refPref, ¶m) //#nosec + } + + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } + + if len(param.Enum) > 0 { + s.enums.addParameterEnum(refPref, param.Enum) + } + + s.analyzeItems("items", param.Items, refPref, "parameter") + if param.In == "body" && param.Schema != nil { + s.analyzeSchema("schema", param.Schema, refPref) + } +} + +func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { + if op == nil { + return + } + + for _, c := range op.Consumes { + s.consumes[c] = struct{}{} + } + + for _, c := range op.Produces { + s.produces[c] = struct{}{} + } + + for _, ss := range op.Security { + for k := range ss { + s.authSchemes[k] = struct{}{} + } + } + + if _, ok := s.operations[method]; !ok { + s.operations[method] = make(map[string]*spec.Operation) + } + + s.operations[method][path] = op + prefix := slashpath.Join("/paths", jsonpointer.Escape(path), strings.ToLower(method)) + for i, param := range op.Parameters { + s.analyzeParameter(prefix, i, param) + } + + if op.Responses == nil { + return + } + + if op.Responses.Default != nil { + s.analyzeDefaultResponse(prefix, op.Responses.Default) + } + + for k, res := range op.Responses.StatusCodeResponses { + s.analyzeResponse(prefix, k, res) + } +} + +func (s *Spec) analyzeDefaultResponse(prefix string, res *spec.Response) { + refPref := slashpath.Join(prefix, "responses", "default") + if res.Ref.String() != "" { + s.references.addResponseRef(refPref, res) + } + + for k, v := range res.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + } + + if res.Schema != nil { + s.analyzeSchema("schema", res.Schema, refPref) + } +} + +func (s *Spec) analyzeResponse(prefix string, k int, res spec.Response) { + refPref := slashpath.Join(prefix, "responses", strconv.Itoa(k)) + if res.Ref.String() != "" { + s.references.addResponseRef(refPref, &res) //#nosec + } + + for k, v := range res.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + + if len(v.Enum) > 0 { + s.enums.addHeaderEnum(hRefPref, v.Enum) + } + } + + if res.Schema != nil { + s.analyzeSchema("schema", res.Schema, refPref) + } +} + +func (s *Spec) analyzeSchema(name string, schema *spec.Schema, prefix string) { + refURI := slashpath.Join(prefix, jsonpointer.Escape(name)) + schRef := SchemaRef{ + Name: name, + Schema: schema, + Ref: spec.MustCreateRef("#" + refURI), + TopLevel: prefix == "/definitions", + } + + s.allSchemas["#"+refURI] = schRef + + if schema.Ref.String() != "" { + s.references.addSchemaRef(refURI, schRef) + } + + if schema.Pattern != "" { + s.patterns.addSchemaPattern(refURI, schema.Pattern) + } + + if len(schema.Enum) > 0 { + s.enums.addSchemaEnum(refURI, schema.Enum) + } + + for k, v := range schema.Definitions { + v := v + s.analyzeSchema(k, &v, slashpath.Join(refURI, "definitions")) + } + + for k, v := range schema.Properties { + v := v + s.analyzeSchema(k, &v, slashpath.Join(refURI, "properties")) + } + + for k, v := range schema.PatternProperties { + v := v + // NOTE: swagger 2.0 does not support PatternProperties. + // However it is possible to analyze this in a schema + s.analyzeSchema(k, &v, slashpath.Join(refURI, "patternProperties")) + } + + for i := range schema.AllOf { + v := &schema.AllOf[i] + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf")) + } + + if len(schema.AllOf) > 0 { + s.allOfs["#"+refURI] = schRef + } + + for i := range schema.AnyOf { + v := &schema.AnyOf[i] + // NOTE: swagger 2.0 does not support anyOf constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf")) + } + + for i := range schema.OneOf { + v := &schema.OneOf[i] + // NOTE: swagger 2.0 does not support oneOf constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "oneOf")) + } + + if schema.Not != nil { + // NOTE: swagger 2.0 does not support "not" constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema("not", schema.Not, refURI) + } + + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + s.analyzeSchema("additionalProperties", schema.AdditionalProperties.Schema, refURI) + } + + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + // NOTE: swagger 2.0 does not support AdditionalItems. + // However it is possible to analyze this in a schema + s.analyzeSchema("additionalItems", schema.AdditionalItems.Schema, refURI) + } + + if schema.Items != nil { + if schema.Items.Schema != nil { + s.analyzeSchema("items", schema.Items.Schema, refURI) + } + + for i := range schema.Items.Schemas { + sch := &schema.Items.Schemas[i] + s.analyzeSchema(strconv.Itoa(i), sch, slashpath.Join(refURI, "items")) + } + } +} + +// SecurityRequirement is a representation of a security requirement for an operation +type SecurityRequirement struct { + Name string + Scopes []string +} + +// SecurityRequirementsFor gets the security requirements for the operation +func (s *Spec) SecurityRequirementsFor(operation *spec.Operation) [][]SecurityRequirement { + if s.spec.Security == nil && operation.Security == nil { + return nil + } + + schemes := s.spec.Security + if operation.Security != nil { + schemes = operation.Security + } + + result := [][]SecurityRequirement{} + for _, scheme := range schemes { + if len(scheme) == 0 { + // append a zero object for anonymous + result = append(result, []SecurityRequirement{{}}) + + continue + } + + var reqs []SecurityRequirement + for k, v := range scheme { + if v == nil { + v = []string{} + } + reqs = append(reqs, SecurityRequirement{Name: k, Scopes: v}) + } + + result = append(result, reqs) + } + + return result +} + +// SecurityDefinitionsForRequirements gets the matching security definitions for a set of requirements +func (s *Spec) SecurityDefinitionsForRequirements(requirements []SecurityRequirement) map[string]spec.SecurityScheme { + result := make(map[string]spec.SecurityScheme) + + for _, v := range requirements { + if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok { + if definition != nil { + result[v.Name] = *definition + } + } + } + + return result +} + +// SecurityDefinitionsFor gets the matching security definitions for a set of requirements +func (s *Spec) SecurityDefinitionsFor(operation *spec.Operation) map[string]spec.SecurityScheme { + requirements := s.SecurityRequirementsFor(operation) + if len(requirements) == 0 { + return nil + } + + result := make(map[string]spec.SecurityScheme) + for _, reqs := range requirements { + for _, v := range reqs { + if v.Name == "" { + // optional requirement + continue + } + + if _, ok := result[v.Name]; ok { + // duplicate requirement + continue + } + + if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok { + if definition != nil { + result[v.Name] = *definition + } + } + } + } + + return result +} + +// ConsumesFor gets the mediatypes for the operation +func (s *Spec) ConsumesFor(operation *spec.Operation) []string { + if len(operation.Consumes) == 0 { + cons := make(map[string]struct{}, len(s.spec.Consumes)) + for _, k := range s.spec.Consumes { + cons[k] = struct{}{} + } + + return s.structMapKeys(cons) + } + + cons := make(map[string]struct{}, len(operation.Consumes)) + for _, c := range operation.Consumes { + cons[c] = struct{}{} + } + + return s.structMapKeys(cons) +} + +// ProducesFor gets the mediatypes for the operation +func (s *Spec) ProducesFor(operation *spec.Operation) []string { + if len(operation.Produces) == 0 { + prod := make(map[string]struct{}, len(s.spec.Produces)) + for _, k := range s.spec.Produces { + prod[k] = struct{}{} + } + + return s.structMapKeys(prod) + } + + prod := make(map[string]struct{}, len(operation.Produces)) + for _, c := range operation.Produces { + prod[c] = struct{}{} + } + + return s.structMapKeys(prod) +} + +func mapKeyFromParam(param *spec.Parameter) string { + return fmt.Sprintf("%s#%s", param.In, fieldNameFromParam(param)) +} + +func fieldNameFromParam(param *spec.Parameter) string { + // TODO: this should be x-go-name + if nm, ok := param.Extensions.GetString("go-name"); ok { + return nm + } + + return swag.ToGoName(param.Name) +} + +// ErrorOnParamFunc is a callback function to be invoked +// whenever an error is encountered while resolving references +// on parameters. +// +// This function takes as input the spec.Parameter which triggered the +// error and the error itself. +// +// If the callback function returns false, the calling function should bail. +// +// If it returns true, the calling function should continue evaluating parameters. +// A nil ErrorOnParamFunc must be evaluated as equivalent to panic(). +type ErrorOnParamFunc func(spec.Parameter, error) bool + +func (s *Spec) paramsAsMap(parameters []spec.Parameter, res map[string]spec.Parameter, callmeOnError ErrorOnParamFunc) { + for _, param := range parameters { + pr := param + if pr.Ref.String() == "" { + res[mapKeyFromParam(&pr)] = pr + + continue + } + + // resolve $ref + if callmeOnError == nil { + callmeOnError = func(_ spec.Parameter, err error) bool { + panic(err) + } + } + + obj, _, err := pr.Ref.GetPointer().Get(s.spec) + if err != nil { + if callmeOnError(param, fmt.Errorf("invalid reference: %q", pr.Ref.String())) { + continue + } + + break + } + + objAsParam, ok := obj.(spec.Parameter) + if !ok { + if callmeOnError(param, fmt.Errorf("resolved reference is not a parameter: %q", pr.Ref.String())) { + continue + } + + break + } + + pr = objAsParam + res[mapKeyFromParam(&pr)] = pr + } +} + +// ParametersFor the specified operation id. +// +// Assumes parameters properly resolve references if any and that +// such references actually resolve to a parameter object. +// Otherwise, panics. +func (s *Spec) ParametersFor(operationID string) []spec.Parameter { + return s.SafeParametersFor(operationID, nil) +} + +// SafeParametersFor the specified operation id. +// +// Does not assume parameters properly resolve references or that +// such references actually resolve to a parameter object. +// +// Upon error, invoke a ErrorOnParamFunc callback with the erroneous +// parameters. If the callback is set to nil, panics upon errors. +func (s *Spec) SafeParametersFor(operationID string, callmeOnError ErrorOnParamFunc) []spec.Parameter { + gatherParams := func(pi *spec.PathItem, op *spec.Operation) []spec.Parameter { + bag := make(map[string]spec.Parameter) + s.paramsAsMap(pi.Parameters, bag, callmeOnError) + s.paramsAsMap(op.Parameters, bag, callmeOnError) + + var res []spec.Parameter + for _, v := range bag { + res = append(res, v) + } + + return res + } + + for _, pi := range s.spec.Paths.Paths { + if pi.Get != nil && pi.Get.ID == operationID { + return gatherParams(&pi, pi.Get) //#nosec + } + if pi.Head != nil && pi.Head.ID == operationID { + return gatherParams(&pi, pi.Head) //#nosec + } + if pi.Options != nil && pi.Options.ID == operationID { + return gatherParams(&pi, pi.Options) //#nosec + } + if pi.Post != nil && pi.Post.ID == operationID { + return gatherParams(&pi, pi.Post) //#nosec + } + if pi.Patch != nil && pi.Patch.ID == operationID { + return gatherParams(&pi, pi.Patch) //#nosec + } + if pi.Put != nil && pi.Put.ID == operationID { + return gatherParams(&pi, pi.Put) //#nosec + } + if pi.Delete != nil && pi.Delete.ID == operationID { + return gatherParams(&pi, pi.Delete) //#nosec + } + } + + return nil +} + +// ParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that +// apply for the method and path. +// +// Assumes parameters properly resolve references if any and that +// such references actually resolve to a parameter object. +// Otherwise, panics. +func (s *Spec) ParamsFor(method, path string) map[string]spec.Parameter { + return s.SafeParamsFor(method, path, nil) +} + +// SafeParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that +// apply for the method and path. +// +// Does not assume parameters properly resolve references or that +// such references actually resolve to a parameter object. +// +// Upon error, invoke a ErrorOnParamFunc callback with the erroneous +// parameters. If the callback is set to nil, panics upon errors. +func (s *Spec) SafeParamsFor(method, path string, callmeOnError ErrorOnParamFunc) map[string]spec.Parameter { + res := make(map[string]spec.Parameter) + if pi, ok := s.spec.Paths.Paths[path]; ok { + s.paramsAsMap(pi.Parameters, res, callmeOnError) + s.paramsAsMap(s.operations[strings.ToUpper(method)][path].Parameters, res, callmeOnError) + } + + return res +} + +// OperationForName gets the operation for the given id +func (s *Spec) OperationForName(operationID string) (string, string, *spec.Operation, bool) { + for method, pathItem := range s.operations { + for path, op := range pathItem { + if operationID == op.ID { + return method, path, op, true + } + } + } + + return "", "", nil, false +} + +// OperationFor the given method and path +func (s *Spec) OperationFor(method, path string) (*spec.Operation, bool) { + if mp, ok := s.operations[strings.ToUpper(method)]; ok { + op, fn := mp[path] + + return op, fn + } + + return nil, false +} + +// Operations gathers all the operations specified in the spec document +func (s *Spec) Operations() map[string]map[string]*spec.Operation { + return s.operations +} + +func (s *Spec) structMapKeys(mp map[string]struct{}) []string { + if len(mp) == 0 { + return nil + } + + result := make([]string, 0, len(mp)) + for k := range mp { + result = append(result, k) + } + + return result +} + +// AllPaths returns all the paths in the swagger spec +func (s *Spec) AllPaths() map[string]spec.PathItem { + if s.spec == nil || s.spec.Paths == nil { + return nil + } + + return s.spec.Paths.Paths +} + +// OperationIDs gets all the operation ids based on method an dpath +func (s *Spec) OperationIDs() []string { + if len(s.operations) == 0 { + return nil + } + + result := make([]string, 0, len(s.operations)) + for method, v := range s.operations { + for p, o := range v { + if o.ID != "" { + result = append(result, o.ID) + } else { + result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p)) + } + } + } + + return result +} + +// OperationMethodPaths gets all the operation ids based on method an dpath +func (s *Spec) OperationMethodPaths() []string { + if len(s.operations) == 0 { + return nil + } + + result := make([]string, 0, len(s.operations)) + for method, v := range s.operations { + for p := range v { + result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p)) + } + } + + return result +} + +// RequiredConsumes gets all the distinct consumes that are specified in the specification document +func (s *Spec) RequiredConsumes() []string { + return s.structMapKeys(s.consumes) +} + +// RequiredProduces gets all the distinct produces that are specified in the specification document +func (s *Spec) RequiredProduces() []string { + return s.structMapKeys(s.produces) +} + +// RequiredSecuritySchemes gets all the distinct security schemes that are specified in the swagger spec +func (s *Spec) RequiredSecuritySchemes() []string { + return s.structMapKeys(s.authSchemes) +} + +// SchemaRef is a reference to a schema +type SchemaRef struct { + Name string + Ref spec.Ref + Schema *spec.Schema + TopLevel bool +} + +// SchemasWithAllOf returns schema references to all schemas that are defined +// with an allOf key +func (s *Spec) SchemasWithAllOf() (result []SchemaRef) { + for _, v := range s.allOfs { + result = append(result, v) + } + + return +} + +// AllDefinitions returns schema references for all the definitions that were discovered +func (s *Spec) AllDefinitions() (result []SchemaRef) { + for _, v := range s.allSchemas { + result = append(result, v) + } + + return +} + +// AllDefinitionReferences returns json refs for all the discovered schemas +func (s *Spec) AllDefinitionReferences() (result []string) { + for _, v := range s.references.schemas { + result = append(result, v.String()) + } + + return +} + +// AllParameterReferences returns json refs for all the discovered parameters +func (s *Spec) AllParameterReferences() (result []string) { + for _, v := range s.references.parameters { + result = append(result, v.String()) + } + + return +} + +// AllResponseReferences returns json refs for all the discovered responses +func (s *Spec) AllResponseReferences() (result []string) { + for _, v := range s.references.responses { + result = append(result, v.String()) + } + + return +} + +// AllPathItemReferences returns the references for all the items +func (s *Spec) AllPathItemReferences() (result []string) { + for _, v := range s.references.pathItems { + result = append(result, v.String()) + } + + return +} + +// AllItemsReferences returns the references for all the items in simple schemas (parameters or headers). +// +// NOTE: since Swagger 2.0 forbids $ref in simple params, this should always yield an empty slice for a valid +// Swagger 2.0 spec. +func (s *Spec) AllItemsReferences() (result []string) { + for _, v := range s.references.items { + result = append(result, v.String()) + } + + return +} + +// AllReferences returns all the references found in the document, with possible duplicates +func (s *Spec) AllReferences() (result []string) { + for _, v := range s.references.allRefs { + result = append(result, v.String()) + } + + return +} + +// AllRefs returns all the unique references found in the document +func (s *Spec) AllRefs() (result []spec.Ref) { + set := make(map[string]struct{}) + for _, v := range s.references.allRefs { + a := v.String() + if a == "" { + continue + } + + if _, ok := set[a]; !ok { + set[a] = struct{}{} + result = append(result, v) + } + } + + return +} + +func cloneStringMap(source map[string]string) map[string]string { + res := make(map[string]string, len(source)) + for k, v := range source { + res[k] = v + } + + return res +} + +func cloneEnumMap(source map[string][]interface{}) map[string][]interface{} { + res := make(map[string][]interface{}, len(source)) + for k, v := range source { + res[k] = v + } + + return res +} + +// ParameterPatterns returns all the patterns found in parameters +// the map is cloned to avoid accidental changes +func (s *Spec) ParameterPatterns() map[string]string { + return cloneStringMap(s.patterns.parameters) +} + +// HeaderPatterns returns all the patterns found in response headers +// the map is cloned to avoid accidental changes +func (s *Spec) HeaderPatterns() map[string]string { + return cloneStringMap(s.patterns.headers) +} + +// ItemsPatterns returns all the patterns found in simple array items +// the map is cloned to avoid accidental changes +func (s *Spec) ItemsPatterns() map[string]string { + return cloneStringMap(s.patterns.items) +} + +// SchemaPatterns returns all the patterns found in schemas +// the map is cloned to avoid accidental changes +func (s *Spec) SchemaPatterns() map[string]string { + return cloneStringMap(s.patterns.schemas) +} + +// AllPatterns returns all the patterns found in the spec +// the map is cloned to avoid accidental changes +func (s *Spec) AllPatterns() map[string]string { + return cloneStringMap(s.patterns.allPatterns) +} + +// ParameterEnums returns all the enums found in parameters +// the map is cloned to avoid accidental changes +func (s *Spec) ParameterEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.parameters) +} + +// HeaderEnums returns all the enums found in response headers +// the map is cloned to avoid accidental changes +func (s *Spec) HeaderEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.headers) +} + +// ItemsEnums returns all the enums found in simple array items +// the map is cloned to avoid accidental changes +func (s *Spec) ItemsEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.items) +} + +// SchemaEnums returns all the enums found in schemas +// the map is cloned to avoid accidental changes +func (s *Spec) SchemaEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.schemas) +} + +// AllEnums returns all the enums found in the spec +// the map is cloned to avoid accidental changes +func (s *Spec) AllEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.allEnums) +} diff --git a/vendor/github.com/go-openapi/analysis/appveyor.yml b/vendor/github.com/go-openapi/analysis/appveyor.yml new file mode 100644 index 00000000..c2f6fd73 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/appveyor.yml @@ -0,0 +1,32 @@ +version: "0.1.{build}" + +clone_folder: C:\go-openapi\analysis +shallow_clone: true # for startup speed +pull_requests: + do_not_increment_build_number: true + +#skip_tags: true +#skip_branch_with_pr: true + +# appveyor.yml +build: off + +environment: + GOPATH: c:\gopath + +stack: go 1.16 + +test_script: + - go test -v -timeout 20m ./... + +deploy: off + +notifications: + - provider: Slack + incoming_webhook: https://hooks.slack.com/services/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 + auth_token: + secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4= + channel: bots + on_build_success: false + on_build_failure: true + on_build_status_changed: true diff --git a/vendor/github.com/go-openapi/analysis/debug.go b/vendor/github.com/go-openapi/analysis/debug.go new file mode 100644 index 00000000..33c15704 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/debug.go @@ -0,0 +1,23 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analysis + +import ( + "os" + + "github.com/go-openapi/analysis/internal/debug" +) + +var debugLog = debug.GetLogger("analysis", os.Getenv("SWAGGER_DEBUG") != "") diff --git a/vendor/github.com/go-openapi/analysis/doc.go b/vendor/github.com/go-openapi/analysis/doc.go new file mode 100644 index 00000000..d5294c09 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/doc.go @@ -0,0 +1,43 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +Package analysis provides methods to work with a Swagger specification document from +package go-openapi/spec. + +Analyzing a specification + +An analysed specification object (type Spec) provides methods to work with swagger definition. + +Flattening or expanding a specification + +Flattening a specification bundles all remote $ref in the main spec document. +Depending on flattening options, additional preprocessing may take place: + - full flattening: replacing all inline complex constructs by a named entry in #/definitions + - expand: replace all $ref's in the document by their expanded content + +Merging several specifications + +Mixin several specifications merges all Swagger constructs, and warns about found conflicts. + +Fixing a specification + +Unmarshalling a specification with golang json unmarshalling may lead to +some unwanted result on present but empty fields. + +Analyzing a Swagger schema + +Swagger schemas are analyzed to determine their complexity and qualify their content. +*/ +package analysis diff --git a/vendor/github.com/go-openapi/analysis/fixer.go b/vendor/github.com/go-openapi/analysis/fixer.go new file mode 100644 index 00000000..7c2ca084 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixer.go @@ -0,0 +1,79 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analysis + +import "github.com/go-openapi/spec" + +// FixEmptyResponseDescriptions replaces empty ("") response +// descriptions in the input with "(empty)" to ensure that the +// resulting Swagger is stays valid. The problem appears to arise +// from reading in valid specs that have a explicit response +// description of "" (valid, response.description is required), but +// due to zero values being omitted upon re-serializing (omitempty) we +// lose them unless we stick some chars in there. +func FixEmptyResponseDescriptions(s *spec.Swagger) { + for k, v := range s.Responses { + FixEmptyDesc(&v) //#nosec + s.Responses[k] = v + } + + if s.Paths == nil { + return + } + + for _, v := range s.Paths.Paths { + if v.Get != nil { + FixEmptyDescs(v.Get.Responses) + } + if v.Put != nil { + FixEmptyDescs(v.Put.Responses) + } + if v.Post != nil { + FixEmptyDescs(v.Post.Responses) + } + if v.Delete != nil { + FixEmptyDescs(v.Delete.Responses) + } + if v.Options != nil { + FixEmptyDescs(v.Options.Responses) + } + if v.Head != nil { + FixEmptyDescs(v.Head.Responses) + } + if v.Patch != nil { + FixEmptyDescs(v.Patch.Responses) + } + } +} + +// FixEmptyDescs adds "(empty)" as the description for any Response in +// the given Responses object that doesn't already have one. +func FixEmptyDescs(rs *spec.Responses) { + FixEmptyDesc(rs.Default) + for k, v := range rs.StatusCodeResponses { + FixEmptyDesc(&v) //#nosec + rs.StatusCodeResponses[k] = v + } +} + +// FixEmptyDesc adds "(empty)" as the description to the given +// Response object if it doesn't already have one and isn't a +// ref. No-op on nil input. +func FixEmptyDesc(rs *spec.Response) { + if rs == nil || rs.Description != "" || rs.Ref.Ref.GetURL() != nil { + return + } + rs.Description = "(empty)" +} diff --git a/vendor/github.com/go-openapi/analysis/flatten.go b/vendor/github.com/go-openapi/analysis/flatten.go new file mode 100644 index 00000000..0576220f --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten.go @@ -0,0 +1,802 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analysis + +import ( + "fmt" + "log" + "path" + "sort" + "strings" + + "github.com/go-openapi/analysis/internal/flatten/normalize" + "github.com/go-openapi/analysis/internal/flatten/operations" + "github.com/go-openapi/analysis/internal/flatten/replace" + "github.com/go-openapi/analysis/internal/flatten/schutils" + "github.com/go-openapi/analysis/internal/flatten/sortref" + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" +) + +const definitionsPath = "#/definitions" + +// newRef stores information about refs created during the flattening process +type newRef struct { + key string + newName string + path string + isOAIGen bool + resolved bool + schema *spec.Schema + parents []string +} + +// context stores intermediary results from flatten +type context struct { + newRefs map[string]*newRef + warnings []string + resolved map[string]string +} + +func newContext() *context { + return &context{ + newRefs: make(map[string]*newRef, 150), + warnings: make([]string, 0), + resolved: make(map[string]string, 50), + } +} + +// Flatten an analyzed spec and produce a self-contained spec bundle. +// +// There is a minimal and a full flattening mode. +// +// +// Minimally flattening a spec means: +// - Expanding parameters, responses, path items, parameter items and header items (references to schemas are left +// unscathed) +// - Importing external (http, file) references so they become internal to the document +// - Moving every JSON pointer to a $ref to a named definition (i.e. the reworked spec does not contain pointers +// like "$ref": "#/definitions/myObject/allOfs/1") +// +// A minimally flattened spec thus guarantees the following properties: +// - all $refs point to a local definition (i.e. '#/definitions/...') +// - definitions are unique +// +// NOTE: arbitrary JSON pointers (other than $refs to top level definitions) are rewritten as definitions if they +// represent a complex schema or express commonality in the spec. +// Otherwise, they are simply expanded. +// Self-referencing JSON pointers cannot resolve to a type and trigger an error. +// +// +// Minimal flattening is necessary and sufficient for codegen rendering using go-swagger. +// +// Fully flattening a spec means: +// - Moving every complex inline schema to be a definition with an auto-generated name in a depth-first fashion. +// +// By complex, we mean every JSON object with some properties. +// Arrays, when they do not define a tuple, +// or empty objects with or without additionalProperties, are not considered complex and remain inline. +// +// NOTE: rewritten schemas get a vendor extension x-go-gen-location so we know from which part of the spec definitions +// have been created. +// +// Available flattening options: +// - Minimal: stops flattening after minimal $ref processing, leaving schema constructs untouched +// - Expand: expand all $ref's in the document (inoperant if Minimal set to true) +// - Verbose: croaks about name conflicts detected +// - RemoveUnused: removes unused parameters, responses and definitions after expansion/flattening +// +// NOTE: expansion removes all $ref save circular $ref, which remain in place +// +// TODO: additional options +// - ProgagateNameExtensions: ensure that created entries properly follow naming rules when their parent have set a +// x-go-name extension +// - LiftAllOfs: +// - limit the flattening of allOf members when simple objects +// - merge allOf with validation only +// - merge allOf with extensions only +// - ... +// +func Flatten(opts FlattenOpts) error { + debugLog("FlattenOpts: %#v", opts) + + opts.flattenContext = newContext() + + // 1. Recursively expand responses, parameters, path items and items in simple schemas. + // + // This simplifies the spec and leaves only the $ref's in schema objects. + if err := expand(&opts); err != nil { + return err + } + + // 2. Strip the current document from absolute $ref's that actually a in the root, + // so we can recognize them as proper definitions + // + // In particular, this works around issue go-openapi/spec#76: leading absolute file in $ref is stripped + if err := normalizeRef(&opts); err != nil { + return err + } + + // 3. Optionally remove shared parameters and responses already expanded (now unused). + // + // Operation parameters (i.e. under paths) remain. + if opts.RemoveUnused { + removeUnusedShared(&opts) + } + + // 4. Import all remote references. + if err := importReferences(&opts); err != nil { + return err + } + + // 5. full flattening: rewrite inline schemas (schemas that aren't simple types or arrays or maps) + if !opts.Minimal && !opts.Expand { + if err := nameInlinedSchemas(&opts); err != nil { + return err + } + } + + // 6. Rewrite JSON pointers other than $ref to named definitions + // and attempt to resolve conflicting names whenever possible. + if err := stripPointersAndOAIGen(&opts); err != nil { + return err + } + + // 7. Strip the spec from unused definitions + if opts.RemoveUnused { + removeUnused(&opts) + } + + // 8. Issue warning notifications, if any + opts.croak() + + // TODO: simplify known schema patterns to flat objects with properties + // examples: + // - lift simple allOf object, + // - empty allOf with validation only or extensions only + // - rework allOf arrays + // - rework allOf additionalProperties + + return nil +} + +func expand(opts *FlattenOpts) error { + if err := spec.ExpandSpec(opts.Swagger(), opts.ExpandOpts(!opts.Expand)); err != nil { + return err + } + + opts.Spec.reload() // re-analyze + + return nil +} + +// normalizeRef strips the current file from any absolute file $ref. This works around issue go-openapi/spec#76: +// leading absolute file in $ref is stripped +func normalizeRef(opts *FlattenOpts) error { + debugLog("normalizeRef") + + altered := false + for k, w := range opts.Spec.references.allRefs { + if !strings.HasPrefix(w.String(), opts.BasePath+definitionsPath) { // may be a mix of / and \, depending on OS + continue + } + + altered = true + debugLog("stripping absolute path for: %s", w.String()) + + // strip the base path from definition + if err := replace.UpdateRef(opts.Swagger(), k, + spec.MustCreateRef(path.Join(definitionsPath, path.Base(w.String())))); err != nil { + return err + } + } + + if altered { + opts.Spec.reload() // re-analyze + } + + return nil +} + +func removeUnusedShared(opts *FlattenOpts) { + opts.Swagger().Parameters = nil + opts.Swagger().Responses = nil + + opts.Spec.reload() // re-analyze +} + +func importReferences(opts *FlattenOpts) error { + var ( + imported bool + err error + ) + + for !imported && err == nil { + // iteratively import remote references until none left. + // This inlining deals with name conflicts by introducing auto-generated names ("OAIGen") + imported, err = importExternalReferences(opts) + + opts.Spec.reload() // re-analyze + } + + return err +} + +// nameInlinedSchemas replaces every complex inline construct by a named definition. +func nameInlinedSchemas(opts *FlattenOpts) error { + debugLog("nameInlinedSchemas") + + namer := &InlineSchemaNamer{ + Spec: opts.Swagger(), + Operations: operations.AllOpRefsByRef(opts.Spec, nil), + flattenContext: opts.flattenContext, + opts: opts, + } + + depthFirst := sortref.DepthFirst(opts.Spec.allSchemas) + for _, key := range depthFirst { + sch := opts.Spec.allSchemas[key] + if sch.Schema == nil || sch.Schema.Ref.String() != "" || sch.TopLevel { + continue + } + + asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if err != nil { + return fmt.Errorf("schema analysis [%s]: %w", key, err) + } + + if asch.isAnalyzedAsComplex() { // move complex schemas to definitions + if err := namer.Name(key, sch.Schema, asch); err != nil { + return err + } + } + } + + opts.Spec.reload() // re-analyze + + return nil +} + +func removeUnused(opts *FlattenOpts) { + expected := make(map[string]struct{}) + for k := range opts.Swagger().Definitions { + expected[path.Join(definitionsPath, jsonpointer.Escape(k))] = struct{}{} + } + + for _, k := range opts.Spec.AllDefinitionReferences() { + delete(expected, k) + } + + for k := range expected { + debugLog("removing unused definition %s", path.Base(k)) + if opts.Verbose { + log.Printf("info: removing unused definition: %s", path.Base(k)) + } + delete(opts.Swagger().Definitions, path.Base(k)) + } + + opts.Spec.reload() // re-analyze +} + +func importKnownRef(entry sortref.RefRevIdx, refStr, newName string, opts *FlattenOpts) error { + // rewrite ref with already resolved external ref (useful for cyclical refs): + // rewrite external refs to local ones + debugLog("resolving known ref [%s] to %s", refStr, newName) + + for _, key := range entry.Keys { + if err := replace.UpdateRef(opts.Swagger(), key, spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil { + return err + } + } + + return nil +} + +func importNewRef(entry sortref.RefRevIdx, refStr string, opts *FlattenOpts) error { + var ( + isOAIGen bool + newName string + ) + + debugLog("resolving schema from remote $ref [%s]", refStr) + + sch, err := spec.ResolveRefWithBase(opts.Swagger(), &entry.Ref, opts.ExpandOpts(false)) + if err != nil { + return fmt.Errorf("could not resolve schema: %w", err) + } + + // at this stage only $ref analysis matters + partialAnalyzer := &Spec{ + references: referenceAnalysis{}, + patterns: patternAnalysis{}, + enums: enumAnalysis{}, + } + partialAnalyzer.reset() + partialAnalyzer.analyzeSchema("", sch, "/") + + // now rewrite those refs with rebase + for key, ref := range partialAnalyzer.references.allRefs { + if err := replace.UpdateRef(sch, key, spec.MustCreateRef(normalize.RebaseRef(entry.Ref.String(), ref.String()))); err != nil { + return fmt.Errorf("failed to rewrite ref for key %q at %s: %w", key, entry.Ref.String(), err) + } + } + + // generate a unique name - isOAIGen means that a naming conflict was resolved by changing the name + newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref)) + debugLog("new name for [%s]: %s - with name conflict:%t", strings.Join(entry.Keys, ", "), newName, isOAIGen) + + opts.flattenContext.resolved[refStr] = newName + + // rewrite the external refs to local ones + for _, key := range entry.Keys { + if err := replace.UpdateRef(opts.Swagger(), key, + spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil { + return err + } + + // keep track of created refs + resolved := false + if _, ok := opts.flattenContext.newRefs[key]; ok { + resolved = opts.flattenContext.newRefs[key].resolved + } + + debugLog("keeping track of ref: %s (%s), resolved: %t", key, newName, resolved) + opts.flattenContext.newRefs[key] = &newRef{ + key: key, + newName: newName, + path: path.Join(definitionsPath, newName), + isOAIGen: isOAIGen, + resolved: resolved, + schema: sch, + } + } + + // add the resolved schema to the definitions + schutils.Save(opts.Swagger(), newName, sch) + + return nil +} + +// importExternalReferences iteratively digs remote references and imports them into the main schema. +// +// At every iteration, new remotes may be found when digging deeper: they are rebased to the current schema before being imported. +// +// This returns true when no more remote references can be found. +func importExternalReferences(opts *FlattenOpts) (bool, error) { + debugLog("importExternalReferences") + + groupedRefs := sortref.ReverseIndex(opts.Spec.references.schemas, opts.BasePath) + sortedRefStr := make([]string, 0, len(groupedRefs)) + if opts.flattenContext == nil { + opts.flattenContext = newContext() + } + + // sort $ref resolution to ensure deterministic name conflict resolution + for refStr := range groupedRefs { + sortedRefStr = append(sortedRefStr, refStr) + } + sort.Strings(sortedRefStr) + + complete := true + + for _, refStr := range sortedRefStr { + entry := groupedRefs[refStr] + if entry.Ref.HasFragmentOnly { + continue + } + + complete = false + + newName := opts.flattenContext.resolved[refStr] + if newName != "" { + if err := importKnownRef(entry, refStr, newName, opts); err != nil { + return false, err + } + + continue + } + + // resolve schemas + if err := importNewRef(entry, refStr, opts); err != nil { + return false, err + } + } + + // maintains ref index entries + for k := range opts.flattenContext.newRefs { + r := opts.flattenContext.newRefs[k] + + // update tracking with resolved schemas + if r.schema.Ref.String() != "" { + ref := spec.MustCreateRef(r.path) + sch, err := spec.ResolveRefWithBase(opts.Swagger(), &ref, opts.ExpandOpts(false)) + if err != nil { + return false, fmt.Errorf("could not resolve schema: %w", err) + } + + r.schema = sch + } + + if r.path == k { + continue + } + + // update tracking with renamed keys: got a cascade of refs + renamed := *r + renamed.key = r.path + opts.flattenContext.newRefs[renamed.path] = &renamed + + // indirect ref + r.newName = path.Base(k) + r.schema = spec.RefSchema(r.path) + r.path = k + r.isOAIGen = strings.Contains(k, "OAIGen") + } + + return complete, nil +} + +// stripPointersAndOAIGen removes anonymous JSON pointers from spec and chain with name conflicts handler. +// This loops until the spec has no such pointer and all name conflicts have been reduced as much as possible. +func stripPointersAndOAIGen(opts *FlattenOpts) error { + // name all JSON pointers to anonymous documents + if err := namePointers(opts); err != nil { + return err + } + + // remove unnecessary OAIGen ref (created when flattening external refs creates name conflicts) + hasIntroducedPointerOrInline, ers := stripOAIGen(opts) + if ers != nil { + return ers + } + + // iterate as pointer or OAIGen resolution may introduce inline schemas or pointers + for hasIntroducedPointerOrInline { + if !opts.Minimal { + opts.Spec.reload() // re-analyze + if err := nameInlinedSchemas(opts); err != nil { + return err + } + } + + if err := namePointers(opts); err != nil { + return err + } + + // restrip and re-analyze + var err error + if hasIntroducedPointerOrInline, err = stripOAIGen(opts); err != nil { + return err + } + } + + return nil +} + +// stripOAIGen strips the spec from unnecessary OAIGen constructs, initially created to dedupe flattened definitions. +// +// A dedupe is deemed unnecessary whenever: +// - the only conflict is with its (single) parent: OAIGen is merged into its parent (reinlining) +// - there is a conflict with multiple parents: merge OAIGen in first parent, the rewrite other parents to point to +// the first parent. +// +// This function returns true whenever it re-inlined a complex schema, so the caller may chose to iterate +// pointer and name resolution again. +func stripOAIGen(opts *FlattenOpts) (bool, error) { + debugLog("stripOAIGen") + replacedWithComplex := false + + // figure out referers of OAIGen definitions (doing it before the ref start mutating) + for _, r := range opts.flattenContext.newRefs { + updateRefParents(opts.Spec.references.allRefs, r) + } + + for k := range opts.flattenContext.newRefs { + r := opts.flattenContext.newRefs[k] + debugLog("newRefs[%s]: isOAIGen: %t, resolved: %t, name: %s, path:%s, #parents: %d, parents: %v, ref: %s", + k, r.isOAIGen, r.resolved, r.newName, r.path, len(r.parents), r.parents, r.schema.Ref.String()) + + if !r.isOAIGen || len(r.parents) == 0 { + continue + } + + hasReplacedWithComplex, err := stripOAIGenForRef(opts, k, r) + if err != nil { + return replacedWithComplex, err + } + + replacedWithComplex = replacedWithComplex || hasReplacedWithComplex + } + + debugLog("replacedWithComplex: %t", replacedWithComplex) + opts.Spec.reload() // re-analyze + + return replacedWithComplex, nil +} + +// updateRefParents updates all parents of an updated $ref +func updateRefParents(allRefs map[string]spec.Ref, r *newRef) { + if !r.isOAIGen || r.resolved { // bail on already resolved entries (avoid looping) + return + } + for k, v := range allRefs { + if r.path != v.String() { + continue + } + + found := false + for _, p := range r.parents { + if p == k { + found = true + + break + } + } + if !found { + r.parents = append(r.parents, k) + } + } +} + +func stripOAIGenForRef(opts *FlattenOpts, k string, r *newRef) (bool, error) { + replacedWithComplex := false + + pr := sortref.TopmostFirst(r.parents) + + // rewrite first parent schema in hierarchical then lexicographical order + debugLog("rewrite first parent %s with schema", pr[0]) + if err := replace.UpdateRefWithSchema(opts.Swagger(), pr[0], r.schema); err != nil { + return false, err + } + + if pa, ok := opts.flattenContext.newRefs[pr[0]]; ok && pa.isOAIGen { + // update parent in ref index entry + debugLog("update parent entry: %s", pr[0]) + pa.schema = r.schema + pa.resolved = false + replacedWithComplex = true + } + + // rewrite other parents to point to first parent + if len(pr) > 1 { + for _, p := range pr[1:] { + replacingRef := spec.MustCreateRef(pr[0]) + + // set complex when replacing ref is an anonymous jsonpointer: further processing may be required + replacedWithComplex = replacedWithComplex || path.Dir(replacingRef.String()) != definitionsPath + debugLog("rewrite parent with ref: %s", replacingRef.String()) + + // NOTE: it is possible at this stage to introduce json pointers (to non-definitions places). + // Those are stripped later on. + if err := replace.UpdateRef(opts.Swagger(), p, replacingRef); err != nil { + return false, err + } + + if pa, ok := opts.flattenContext.newRefs[p]; ok && pa.isOAIGen { + // update parent in ref index + debugLog("update parent entry: %s", p) + pa.schema = r.schema + pa.resolved = false + replacedWithComplex = true + } + } + } + + // remove OAIGen definition + debugLog("removing definition %s", path.Base(r.path)) + delete(opts.Swagger().Definitions, path.Base(r.path)) + + // propagate changes in ref index for keys which have this one as a parent + for kk, value := range opts.flattenContext.newRefs { + if kk == k || !value.isOAIGen || value.resolved { + continue + } + + found := false + newParents := make([]string, 0, len(value.parents)) + for _, parent := range value.parents { + switch { + case parent == r.path: + found = true + parent = pr[0] + case strings.HasPrefix(parent, r.path+"/"): + found = true + parent = path.Join(pr[0], strings.TrimPrefix(parent, r.path)) + } + + newParents = append(newParents, parent) + } + + if found { + value.parents = newParents + } + } + + // mark naming conflict as resolved + debugLog("marking naming conflict resolved for key: %s", r.key) + opts.flattenContext.newRefs[r.key].isOAIGen = false + opts.flattenContext.newRefs[r.key].resolved = true + + // determine if the previous substitution did inline a complex schema + if r.schema != nil && r.schema.Ref.String() == "" { // inline schema + asch, err := Schema(SchemaOpts{Schema: r.schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if err != nil { + return false, err + } + + debugLog("re-inlined schema: parent: %s, %t", pr[0], asch.isAnalyzedAsComplex()) + replacedWithComplex = replacedWithComplex || !(path.Dir(pr[0]) == definitionsPath) && asch.isAnalyzedAsComplex() + } + + return replacedWithComplex, nil +} + +// namePointers replaces all JSON pointers to anonymous documents by a $ref to a new named definitions. +// +// This is carried on depth-first. Pointers to $refs which are top level definitions are replaced by the $ref itself. +// Pointers to simple types are expanded, unless they express commonality (i.e. several such $ref are used). +func namePointers(opts *FlattenOpts) error { + debugLog("name pointers") + + refsToReplace := make(map[string]SchemaRef, len(opts.Spec.references.schemas)) + for k, ref := range opts.Spec.references.allRefs { + if path.Dir(ref.String()) == definitionsPath { + // this a ref to a top-level definition: ok + continue + } + + result, err := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), ref) + if err != nil { + return fmt.Errorf("at %s, %w", k, err) + } + + replacingRef := result.Ref + sch := result.Schema + if opts.flattenContext != nil { + opts.flattenContext.warnings = append(opts.flattenContext.warnings, result.Warnings...) + } + + debugLog("planning pointer to replace at %s: %s, resolved to: %s", k, ref.String(), replacingRef.String()) + refsToReplace[k] = SchemaRef{ + Name: k, // caller + Ref: replacingRef, // called + Schema: sch, + TopLevel: path.Dir(replacingRef.String()) == definitionsPath, + } + } + + depthFirst := sortref.DepthFirst(refsToReplace) + namer := &InlineSchemaNamer{ + Spec: opts.Swagger(), + Operations: operations.AllOpRefsByRef(opts.Spec, nil), + flattenContext: opts.flattenContext, + opts: opts, + } + + for _, key := range depthFirst { + v := refsToReplace[key] + // update current replacement, which may have been updated by previous changes of deeper elements + result, erd := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), v.Ref) + if erd != nil { + return fmt.Errorf("at %s, %w", key, erd) + } + + if opts.flattenContext != nil { + opts.flattenContext.warnings = append(opts.flattenContext.warnings, result.Warnings...) + } + + v.Ref = result.Ref + v.Schema = result.Schema + v.TopLevel = path.Dir(result.Ref.String()) == definitionsPath + debugLog("replacing pointer at %s: resolved to: %s", key, v.Ref.String()) + + if v.TopLevel { + debugLog("replace pointer %s by canonical definition: %s", key, v.Ref.String()) + + // if the schema is a $ref to a top level definition, just rewrite the pointer to this $ref + if err := replace.UpdateRef(opts.Swagger(), key, v.Ref); err != nil { + return err + } + + continue + } + + if err := flattenAnonPointer(key, v, refsToReplace, namer, opts); err != nil { + return err + } + } + + opts.Spec.reload() // re-analyze + + return nil +} + +func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]SchemaRef, namer *InlineSchemaNamer, opts *FlattenOpts) error { + // this is a JSON pointer to an anonymous document (internal or external): + // create a definition for this schema when: + // - it is a complex schema + // - or it is pointed by more than one $ref (i.e. expresses commonality) + // otherwise, expand the pointer (single reference to a simple type) + // + // The named definition for this follows the target's key, not the caller's + debugLog("namePointers at %s for %s", key, v.Ref.String()) + + // qualify the expanded schema + asch, ers := Schema(SchemaOpts{Schema: v.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if ers != nil { + return fmt.Errorf("schema analysis [%s]: %w", key, ers) + } + callers := make([]string, 0, 64) + + debugLog("looking for callers") + + an := New(opts.Swagger()) + for k, w := range an.references.allRefs { + r, err := replace.DeepestRef(opts.Swagger(), opts.ExpandOpts(false), w) + if err != nil { + return fmt.Errorf("at %s, %w", key, err) + } + + if opts.flattenContext != nil { + opts.flattenContext.warnings = append(opts.flattenContext.warnings, r.Warnings...) + } + + if r.Ref.String() == v.Ref.String() { + callers = append(callers, k) + } + } + + debugLog("callers for %s: %d", v.Ref.String(), len(callers)) + if len(callers) == 0 { + // has already been updated and resolved + return nil + } + + parts := sortref.KeyParts(v.Ref.String()) + debugLog("number of callers for %s: %d", v.Ref.String(), len(callers)) + + // identifying edge case when the namer did nothing because we point to a non-schema object + // no definition is created and we expand the $ref for all callers + if (!asch.IsSimpleSchema || len(callers) > 1) && !parts.IsSharedParam() && !parts.IsSharedResponse() { + debugLog("replace JSON pointer at [%s] by definition: %s", key, v.Ref.String()) + if err := namer.Name(v.Ref.String(), v.Schema, asch); err != nil { + return err + } + + // regular case: we named the $ref as a definition, and we move all callers to this new $ref + for _, caller := range callers { + if caller == key { + continue + } + + // move $ref for next to resolve + debugLog("identified caller of %s at [%s]", v.Ref.String(), caller) + c := refsToReplace[caller] + c.Ref = v.Ref + refsToReplace[caller] = c + } + + return nil + } + + debugLog("expand JSON pointer for key=%s", key) + + if err := replace.UpdateRefWithSchema(opts.Swagger(), key, v.Schema); err != nil { + return err + } + // NOTE: there is no other caller to update + + return nil +} diff --git a/vendor/github.com/go-openapi/analysis/flatten_name.go b/vendor/github.com/go-openapi/analysis/flatten_name.go new file mode 100644 index 00000000..3ad2ccfb --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten_name.go @@ -0,0 +1,293 @@ +package analysis + +import ( + "fmt" + "path" + "sort" + "strings" + + "github.com/go-openapi/analysis/internal/flatten/operations" + "github.com/go-openapi/analysis/internal/flatten/replace" + "github.com/go-openapi/analysis/internal/flatten/schutils" + "github.com/go-openapi/analysis/internal/flatten/sortref" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// InlineSchemaNamer finds a new name for an inlined type +type InlineSchemaNamer struct { + Spec *spec.Swagger + Operations map[string]operations.OpRef + flattenContext *context + opts *FlattenOpts +} + +// Name yields a new name for the inline schema +func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *AnalyzedSchema) error { + debugLog("naming inlined schema at %s", key) + + parts := sortref.KeyParts(key) + for _, name := range namesFromKey(parts, aschema, isn.Operations) { + if name == "" { + continue + } + + // create unique name + newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name)) + + // clone schema + sch := schutils.Clone(schema) + + // replace values on schema + if err := replace.RewriteSchemaToRef(isn.Spec, key, + spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil { + return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err) + } + + // rewrite any dependent $ref pointing to this place, + // when not already pointing to a top-level definition. + // + // NOTE: this is important if such referers use arbitrary JSON pointers. + an := New(isn.Spec) + for k, v := range an.references.allRefs { + r, erd := replace.DeepestRef(isn.opts.Swagger(), isn.opts.ExpandOpts(false), v) + if erd != nil { + return fmt.Errorf("at %s, %w", k, erd) + } + + if isn.opts.flattenContext != nil { + isn.opts.flattenContext.warnings = append(isn.opts.flattenContext.warnings, r.Warnings...) + } + + if r.Ref.String() != key && (r.Ref.String() != path.Join(definitionsPath, newName) || path.Dir(v.String()) == definitionsPath) { + continue + } + + debugLog("found a $ref to a rewritten schema: %s points to %s", k, v.String()) + + // rewrite $ref to the new target + if err := replace.UpdateRef(isn.Spec, k, + spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil { + return err + } + } + + // NOTE: this extension is currently not used by go-swagger (provided for information only) + sch.AddExtension("x-go-gen-location", GenLocation(parts)) + + // save cloned schema to definitions + schutils.Save(isn.Spec, newName, sch) + + // keep track of created refs + if isn.flattenContext == nil { + continue + } + + debugLog("track created ref: key=%s, newName=%s, isOAIGen=%t", key, newName, isOAIGen) + resolved := false + + if _, ok := isn.flattenContext.newRefs[key]; ok { + resolved = isn.flattenContext.newRefs[key].resolved + } + + isn.flattenContext.newRefs[key] = &newRef{ + key: key, + newName: newName, + path: path.Join(definitionsPath, newName), + isOAIGen: isOAIGen, + resolved: resolved, + schema: sch, + } + } + + return nil +} + +// uniqifyName yields a unique name for a definition +func uniqifyName(definitions spec.Definitions, name string) (string, bool) { + isOAIGen := false + if name == "" { + name = "oaiGen" + isOAIGen = true + } + + if len(definitions) == 0 { + return name, isOAIGen + } + + unq := true + for k := range definitions { + if strings.EqualFold(k, name) { + unq = false + + break + } + } + + if unq { + return name, isOAIGen + } + + name += "OAIGen" + isOAIGen = true + var idx int + unique := name + _, known := definitions[unique] + + for known { + idx++ + unique = fmt.Sprintf("%s%d", name, idx) + _, known = definitions[unique] + } + + return unique, isOAIGen +} + +func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations map[string]operations.OpRef) []string { + var ( + baseNames [][]string + startIndex int + ) + + if parts.IsOperation() { + baseNames, startIndex = namesForOperation(parts, operations) + } + + // definitions + if parts.IsDefinition() { + baseNames, startIndex = namesForDefinition(parts) + } + + result := make([]string, 0, len(baseNames)) + for _, segments := range baseNames { + nm := parts.BuildName(segments, startIndex, partAdder(aschema)) + if nm == "" { + continue + } + + result = append(result, nm) + } + sort.Strings(result) + + return result +} + +func namesForParam(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) { + var ( + baseNames [][]string + startIndex int + ) + + piref := parts.PathItemRef() + if piref.String() != "" && parts.IsOperationParam() { + if op, ok := operations[piref.String()]; ok { + startIndex = 5 + baseNames = append(baseNames, []string{op.ID, "params", "body"}) + } + } else if parts.IsSharedOperationParam() { + pref := parts.PathRef() + for k, v := range operations { + if strings.HasPrefix(k, pref.String()) { + startIndex = 4 + baseNames = append(baseNames, []string{v.ID, "params", "body"}) + } + } + } + + return baseNames, startIndex +} + +func namesForOperation(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) { + var ( + baseNames [][]string + startIndex int + ) + + // params + if parts.IsOperationParam() || parts.IsSharedOperationParam() { + baseNames, startIndex = namesForParam(parts, operations) + } + + // responses + if parts.IsOperationResponse() { + piref := parts.PathItemRef() + if piref.String() != "" { + if op, ok := operations[piref.String()]; ok { + startIndex = 6 + baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"}) + } + } + } + + return baseNames, startIndex +} + +func namesForDefinition(parts sortref.SplitKey) ([][]string, int) { + nm := parts.DefinitionName() + if nm != "" { + return [][]string{{parts.DefinitionName()}}, 2 + } + + return [][]string{}, 0 +} + +// partAdder knows how to interpret a schema when it comes to build a name from parts +func partAdder(aschema *AnalyzedSchema) sortref.PartAdder { + return func(part string) []string { + segments := make([]string, 0, 2) + + if part == "items" || part == "additionalItems" { + if aschema.IsTuple || aschema.IsTupleWithExtra { + segments = append(segments, "tuple") + } else { + segments = append(segments, "items") + } + + if part == "additionalItems" { + segments = append(segments, part) + } + + return segments + } + + segments = append(segments, part) + + return segments + } +} + +func nameFromRef(ref spec.Ref) string { + u := ref.GetURL() + if u.Fragment != "" { + return swag.ToJSONName(path.Base(u.Fragment)) + } + + if u.Path != "" { + bn := path.Base(u.Path) + if bn != "" && bn != "/" { + ext := path.Ext(bn) + if ext != "" { + return swag.ToJSONName(bn[:len(bn)-len(ext)]) + } + + return swag.ToJSONName(bn) + } + } + + return swag.ToJSONName(strings.ReplaceAll(u.Host, ".", " ")) +} + +// GenLocation indicates from which section of the specification (models or operations) a definition has been created. +// +// This is reflected in the output spec with a "x-go-gen-location" extension. At the moment, this is is provided +// for information only. +func GenLocation(parts sortref.SplitKey) string { + switch { + case parts.IsOperation(): + return "operations" + case parts.IsDefinition(): + return "models" + default: + return "" + } +} diff --git a/vendor/github.com/go-openapi/analysis/flatten_options.go b/vendor/github.com/go-openapi/analysis/flatten_options.go new file mode 100644 index 00000000..c5bb97b0 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten_options.go @@ -0,0 +1,78 @@ +package analysis + +import ( + "log" + + "github.com/go-openapi/spec" +) + +// FlattenOpts configuration for flattening a swagger specification. +// +// The BasePath parameter is used to locate remote relative $ref found in the specification. +// This path is a file: it points to the location of the root document and may be either a local +// file path or a URL. +// +// If none specified, relative references (e.g. "$ref": "folder/schema.yaml#/definitions/...") +// found in the spec are searched from the current working directory. +type FlattenOpts struct { + Spec *Spec // The analyzed spec to work with + flattenContext *context // Internal context to track flattening activity + + BasePath string // The location of the root document for this spec to resolve relative $ref + + // Flattening options + Expand bool // When true, skip flattening the spec and expand it instead (if Minimal is false) + Minimal bool // When true, do not decompose complex structures such as allOf + Verbose bool // enable some reporting on possible name conflicts detected + RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening + ContinueOnError bool // Continue when spec expansion issues are found + + /* Extra keys */ + _ struct{} // require keys +} + +// ExpandOpts creates a spec.ExpandOptions to configure expanding a specification document. +func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *spec.ExpandOptions { + return &spec.ExpandOptions{ + RelativeBase: f.BasePath, + SkipSchemas: skipSchemas, + ContinueOnError: f.ContinueOnError, + } +} + +// Swagger gets the swagger specification for this flatten operation +func (f *FlattenOpts) Swagger() *spec.Swagger { + return f.Spec.spec +} + +// croak logs notifications and warnings about valid, but possibly unwanted constructs resulting +// from flattening a spec +func (f *FlattenOpts) croak() { + if !f.Verbose { + return + } + + reported := make(map[string]bool, len(f.flattenContext.newRefs)) + for _, v := range f.Spec.references.allRefs { + // warns about duplicate handling + for _, r := range f.flattenContext.newRefs { + if r.isOAIGen && r.path == v.String() { + reported[r.newName] = true + } + } + } + + for k := range reported { + log.Printf("warning: duplicate flattened definition name resolved as %s", k) + } + + // warns about possible type mismatches + uniqueMsg := make(map[string]bool) + for _, msg := range f.flattenContext.warnings { + if _, ok := uniqueMsg[msg]; ok { + continue + } + log.Printf("warning: %s", msg) + uniqueMsg[msg] = true + } +} diff --git a/vendor/github.com/go-openapi/analysis/internal/debug/debug.go b/vendor/github.com/go-openapi/analysis/internal/debug/debug.go new file mode 100644 index 00000000..ec0fec02 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/debug/debug.go @@ -0,0 +1,41 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package debug + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + output = os.Stdout +) + +// GetLogger provides a prefix debug logger +func GetLogger(prefix string, debug bool) func(string, ...interface{}) { + if debug { + logger := log.New(output, fmt.Sprintf("%s:", prefix), log.LstdFlags) + + return func(msg string, args ...interface{}) { + _, file1, pos1, _ := runtime.Caller(1) + logger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } + } + + return func(msg string, args ...interface{}) {} +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go b/vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go new file mode 100644 index 00000000..8c9df058 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go @@ -0,0 +1,87 @@ +package normalize + +import ( + "net/url" + "path" + "path/filepath" + "strings" + + "github.com/go-openapi/spec" +) + +// RebaseRef rebases a remote ref relative to a base ref. +// +// NOTE: does not support JSONschema ID for $ref (we assume we are working with swagger specs here). +// +// NOTE(windows): +// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec) +// * "/ in paths may appear as escape sequences +func RebaseRef(baseRef string, ref string) string { + baseRef, _ = url.PathUnescape(baseRef) + ref, _ = url.PathUnescape(ref) + + if baseRef == "" || baseRef == "." || strings.HasPrefix(baseRef, "#") { + return ref + } + + parts := strings.Split(ref, "#") + + baseParts := strings.Split(baseRef, "#") + baseURL, _ := url.Parse(baseParts[0]) + if strings.HasPrefix(ref, "#") { + if baseURL.Host == "" { + return strings.Join([]string{baseParts[0], parts[1]}, "#") + } + + return strings.Join([]string{baseParts[0], parts[1]}, "#") + } + + refURL, _ := url.Parse(parts[0]) + if refURL.Host != "" || filepath.IsAbs(parts[0]) { + // not rebasing an absolute path + return ref + } + + // there is a relative path + var basePath string + if baseURL.Host != "" { + // when there is a host, standard URI rules apply (with "/") + baseURL.Path = path.Dir(baseURL.Path) + baseURL.Path = path.Join(baseURL.Path, "/"+parts[0]) + + return baseURL.String() + } + + // this is a local relative path + // basePart[0] and parts[0] are local filesystem directories/files + basePath = filepath.Dir(baseParts[0]) + relPath := filepath.Join(basePath, string(filepath.Separator)+parts[0]) + if len(parts) > 1 { + return strings.Join([]string{relPath, parts[1]}, "#") + } + + return relPath +} + +// Path renders absolute path on remote file refs +// +// NOTE(windows): +// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec) +// * "/ in paths may appear as escape sequences +func Path(ref spec.Ref, basePath string) string { + uri, _ := url.PathUnescape(ref.String()) + if ref.HasFragmentOnly || filepath.IsAbs(uri) { + return uri + } + + refURL, _ := url.Parse(uri) + if refURL.Host != "" { + return uri + } + + parts := strings.Split(uri, "#") + // BasePath, parts[0] are local filesystem directories, guaranteed to be absolute at this stage + parts[0] = filepath.Join(filepath.Dir(basePath), parts[0]) + + return strings.Join(parts, "#") +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go b/vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go new file mode 100644 index 00000000..7f3a2b87 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go @@ -0,0 +1,90 @@ +package operations + +import ( + "path" + "sort" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// AllOpRefsByRef returns an index of sortable operations +func AllOpRefsByRef(specDoc Provider, operationIDs []string) map[string]OpRef { + return OpRefsByRef(GatherOperations(specDoc, operationIDs)) +} + +// OpRefsByRef indexes a map of sortable operations +func OpRefsByRef(oprefs map[string]OpRef) map[string]OpRef { + result := make(map[string]OpRef, len(oprefs)) + for _, v := range oprefs { + result[v.Ref.String()] = v + } + + return result +} + +// OpRef is an indexable, sortable operation +type OpRef struct { + Method string + Path string + Key string + ID string + Op *spec.Operation + Ref spec.Ref +} + +// OpRefs is a sortable collection of operations +type OpRefs []OpRef + +func (o OpRefs) Len() int { return len(o) } +func (o OpRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o OpRefs) Less(i, j int) bool { return o[i].Key < o[j].Key } + +// Provider knows how to collect operations from a spec +type Provider interface { + Operations() map[string]map[string]*spec.Operation +} + +// GatherOperations builds a map of sorted operations from a spec +func GatherOperations(specDoc Provider, operationIDs []string) map[string]OpRef { + var oprefs OpRefs + + for method, pathItem := range specDoc.Operations() { + for pth, operation := range pathItem { + vv := *operation + oprefs = append(oprefs, OpRef{ + Key: swag.ToGoName(strings.ToLower(method) + " " + pth), + Method: method, + Path: pth, + ID: vv.ID, + Op: &vv, + Ref: spec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)), + }) + } + } + + sort.Sort(oprefs) + + operations := make(map[string]OpRef) + for _, opr := range oprefs { + nm := opr.ID + if nm == "" { + nm = opr.Key + } + + oo, found := operations[nm] + if found && oo.Method != opr.Method && oo.Path != opr.Path { + nm = opr.Key + } + + if len(operationIDs) == 0 || swag.ContainsStrings(operationIDs, opr.ID) || swag.ContainsStrings(operationIDs, nm) { + opr.ID = nm + opr.Op.ID = nm + operations[nm] = opr + } + } + + return operations +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go b/vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go new file mode 100644 index 00000000..26c2a05a --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go @@ -0,0 +1,434 @@ +package replace + +import ( + "fmt" + "net/url" + "os" + "path" + "strconv" + + "github.com/go-openapi/analysis/internal/debug" + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" +) + +const definitionsPath = "#/definitions" + +var debugLog = debug.GetLogger("analysis/flatten/replace", os.Getenv("SWAGGER_DEBUG") != "") + +// RewriteSchemaToRef replaces a schema with a Ref +func RewriteSchemaToRef(sp *spec.Swagger, key string, ref spec.Ref) error { + debugLog("rewriting schema to ref for %s with %s", key, ref.String()) + _, value, err := getPointerFromKey(sp, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *spec.Schema: + return rewriteParentRef(sp, key, ref) + + case spec.Schema: + return rewriteParentRef(sp, key, ref) + + case *spec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + } + + case *spec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + } + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +func rewriteParentRef(sp *spec.Swagger, key string, ref spec.Ref) error { + parent, entry, pvalue, err := getParentFromKey(sp, key) + if err != nil { + return err + } + + debugLog("rewriting holder for %T", pvalue) + switch container := pvalue.(type) { + case spec.Response: + if err := rewriteParentRef(sp, "#"+parent, ref); err != nil { + return err + } + + case *spec.Response: + container.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case *spec.Responses: + statusCode, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", key[1:], err) + } + resp := container.StatusCodeResponses[statusCode] + resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + container.StatusCodeResponses[statusCode] = resp + + case map[string]spec.Response: + resp := container[entry] + resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + container[entry] = resp + + case spec.Parameter: + if err := rewriteParentRef(sp, "#"+parent, ref); err != nil { + return err + } + + case map[string]spec.Parameter: + param := container[entry] + param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + container[entry] = param + + case []spec.Parameter: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", key[1:], err) + } + param := container[idx] + param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + container[idx] = param + + case spec.Definitions: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case map[string]spec.Schema: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case []spec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", key[1:], err) + } + container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case *spec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", key[1:], err) + } + container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case spec.SchemaProperties: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + // NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue) + } + + return nil +} + +// getPointerFromKey retrieves the content of the JSON pointer "key" +func getPointerFromKey(sp interface{}, key string) (string, interface{}, error) { + switch sp.(type) { + case *spec.Schema: + case *spec.Swagger: + default: + panic("unexpected type used in getPointerFromKey") + } + if key == "#/" { + return "", sp, nil + } + // unescape chars in key, e.g. "{}" from path params + pth, _ := url.PathUnescape(key[1:]) + ptr, err := jsonpointer.New(pth) + if err != nil { + return "", nil, err + } + + value, _, err := ptr.Get(sp) + if err != nil { + debugLog("error when getting key: %s with path: %s", key, pth) + + return "", nil, err + } + + return pth, value, nil +} + +// getParentFromKey retrieves the container of the JSON pointer "key" +func getParentFromKey(sp interface{}, key string) (string, string, interface{}, error) { + switch sp.(type) { + case *spec.Schema: + case *spec.Swagger: + default: + panic("unexpected type used in getPointerFromKey") + } + // unescape chars in key, e.g. "{}" from path params + pth, _ := url.PathUnescape(key[1:]) + + parent, entry := path.Dir(pth), path.Base(pth) + debugLog("getting schema holder at: %s, with entry: %s", parent, entry) + + pptr, err := jsonpointer.New(parent) + if err != nil { + return "", "", nil, err + } + pvalue, _, err := pptr.Get(sp) + if err != nil { + return "", "", nil, fmt.Errorf("can't get parent for %s: %w", parent, err) + } + + return parent, entry, pvalue, nil +} + +// UpdateRef replaces a ref by another one +func UpdateRef(sp interface{}, key string, ref spec.Ref) error { + switch sp.(type) { + case *spec.Schema: + case *spec.Swagger: + default: + panic("unexpected type used in getPointerFromKey") + } + debugLog("updating ref for %s with %s", key, ref.String()) + pth, value, err := getPointerFromKey(sp, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *spec.Schema: + refable.Ref = ref + case *spec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case *spec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case spec.Schema: + debugLog("rewriting holder for %T", refable) + _, entry, pvalue, erp := getParentFromKey(sp, key) + if erp != nil { + return err + } + switch container := pvalue.(type) { + case spec.Definitions: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case map[string]spec.Schema: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case []spec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", pth, err) + } + container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case *spec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", pth, err) + } + container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + case spec.SchemaProperties: + container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} + + // NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled container type at %s: %T", key, value) + } + + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +// UpdateRefWithSchema replaces a ref with a schema (i.e. re-inline schema) +func UpdateRefWithSchema(sp *spec.Swagger, key string, sch *spec.Schema) error { + debugLog("updating ref for %s with schema", key) + pth, value, err := getPointerFromKey(sp, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *spec.Schema: + *refable = *sch + case spec.Schema: + _, entry, pvalue, erp := getParentFromKey(sp, key) + if erp != nil { + return err + } + switch container := pvalue.(type) { + case spec.Definitions: + container[entry] = *sch + + case map[string]spec.Schema: + container[entry] = *sch + + case []spec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", pth, err) + } + container[idx] = *sch + + case *spec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %w", pth, err) + } + container.Schemas[idx] = *sch + + case spec.SchemaProperties: + container[entry] = *sch + + // NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled type for parent of [%s]: %T", key, value) + } + case *spec.SchemaOrArray: + *refable.Schema = *sch + // NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema + case *spec.SchemaOrBool: + *refable.Schema = *sch + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +// DeepestRefResult holds the results from DeepestRef analysis +type DeepestRefResult struct { + Ref spec.Ref + Schema *spec.Schema + Warnings []string +} + +// DeepestRef finds the first definition ref, from a cascade of nested refs which are not definitions. +// - if no definition is found, returns the deepest ref. +// - pointers to external files are expanded +// +// NOTE: all external $ref's are assumed to be already expanded at this stage. +func DeepestRef(sp *spec.Swagger, opts *spec.ExpandOptions, ref spec.Ref) (*DeepestRefResult, error) { + if !ref.HasFragmentOnly { + // we found an external $ref, which is odd at this stage: + // do nothing on external $refs + return &DeepestRefResult{Ref: ref}, nil + } + + currentRef := ref + visited := make(map[string]bool, 64) + warnings := make([]string, 0, 2) + +DOWNREF: + for currentRef.String() != "" { + if path.Dir(currentRef.String()) == definitionsPath { + // this is a top-level definition: stop here and return this ref + return &DeepestRefResult{Ref: currentRef}, nil + } + + if _, beenThere := visited[currentRef.String()]; beenThere { + return nil, + fmt.Errorf("cannot resolve cyclic chain of pointers under %s", currentRef.String()) + } + + visited[currentRef.String()] = true + value, _, err := currentRef.GetPointer().Get(sp) + if err != nil { + return nil, err + } + + switch refable := value.(type) { + case *spec.Schema: + if refable.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Ref + + case spec.Schema: + if refable.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Ref + + case *spec.SchemaOrArray: + if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Schema.Ref + + case *spec.SchemaOrBool: + if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Schema.Ref + + case spec.Response: + // a pointer points to a schema initially marshalled in responses section... + // Attempt to convert this to a schema. If this fails, the spec is invalid + asJSON, _ := refable.MarshalJSON() + var asSchema spec.Schema + + err := asSchema.UnmarshalJSON(asJSON) + if err != nil { + return nil, + fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T", + currentRef.String(), value) + } + warnings = append(warnings, fmt.Sprintf("found $ref %q (response) interpreted as schema", currentRef.String())) + + if asSchema.Ref.String() == "" { + break DOWNREF + } + currentRef = asSchema.Ref + + case spec.Parameter: + // a pointer points to a schema initially marshalled in parameters section... + // Attempt to convert this to a schema. If this fails, the spec is invalid + asJSON, _ := refable.MarshalJSON() + var asSchema spec.Schema + if err := asSchema.UnmarshalJSON(asJSON); err != nil { + return nil, + fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T", + currentRef.String(), value) + } + + warnings = append(warnings, fmt.Sprintf("found $ref %q (parameter) interpreted as schema", currentRef.String())) + + if asSchema.Ref.String() == "" { + break DOWNREF + } + currentRef = asSchema.Ref + + default: + return nil, + fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T", + currentRef.String(), value) + } + } + + // assess what schema we're ending with + sch, erv := spec.ResolveRefWithBase(sp, ¤tRef, opts) + if erv != nil { + return nil, erv + } + + if sch == nil { + return nil, fmt.Errorf("no schema found at %s", currentRef.String()) + } + + return &DeepestRefResult{Ref: currentRef, Schema: sch, Warnings: warnings}, nil +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go b/vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go new file mode 100644 index 00000000..4590236e --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go @@ -0,0 +1,29 @@ +// Package schutils provides tools to save or clone a schema +// when flattening a spec. +package schutils + +import ( + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// Save registers a schema as an entry in spec #/definitions +func Save(sp *spec.Swagger, name string, schema *spec.Schema) { + if schema == nil { + return + } + + if sp.Definitions == nil { + sp.Definitions = make(map[string]spec.Schema, 150) + } + + sp.Definitions[name] = *schema +} + +// Clone deep-clones a schema +func Clone(schema *spec.Schema) *spec.Schema { + var sch spec.Schema + _ = swag.FromDynamicJSON(schema, &sch) + + return &sch +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go b/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go new file mode 100644 index 00000000..18e552ea --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go @@ -0,0 +1,201 @@ +package sortref + +import ( + "net/http" + "path" + "strconv" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" +) + +const ( + paths = "paths" + responses = "responses" + parameters = "parameters" + definitions = "definitions" +) + +var ( + ignoredKeys map[string]struct{} + validMethods map[string]struct{} +) + +func init() { + ignoredKeys = map[string]struct{}{ + "schema": {}, + "properties": {}, + "not": {}, + "anyOf": {}, + "oneOf": {}, + } + + validMethods = map[string]struct{}{ + "GET": {}, + "HEAD": {}, + "OPTIONS": {}, + "PATCH": {}, + "POST": {}, + "PUT": {}, + "DELETE": {}, + } +} + +// Key represent a key item constructed from /-separated segments +type Key struct { + Segments int + Key string +} + +// Keys is a sortable collable collection of Keys +type Keys []Key + +func (k Keys) Len() int { return len(k) } +func (k Keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k Keys) Less(i, j int) bool { + return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key) +} + +// KeyParts construct a SplitKey with all its /-separated segments decomposed. It is sortable. +func KeyParts(key string) SplitKey { + var res []string + for _, part := range strings.Split(key[1:], "/") { + if part != "" { + res = append(res, jsonpointer.Unescape(part)) + } + } + + return res +} + +// SplitKey holds of the parts of a /-separated key, soi that their location may be determined. +type SplitKey []string + +// IsDefinition is true when the split key is in the #/definitions section of a spec +func (s SplitKey) IsDefinition() bool { + return len(s) > 1 && s[0] == definitions +} + +// DefinitionName yields the name of the definition +func (s SplitKey) DefinitionName() string { + if !s.IsDefinition() { + return "" + } + + return s[1] +} + +func (s SplitKey) isKeyName(i int) bool { + if i <= 0 { + return false + } + + count := 0 + for idx := i - 1; idx > 0; idx-- { + if s[idx] != "properties" { + break + } + count++ + } + + return count%2 != 0 +} + +// PartAdder know how to construct the components of a new name +type PartAdder func(string) []string + +// BuildName builds a name from segments +func (s SplitKey) BuildName(segments []string, startIndex int, adder PartAdder) string { + for i, part := range s[startIndex:] { + if _, ignored := ignoredKeys[part]; !ignored || s.isKeyName(startIndex+i) { + segments = append(segments, adder(part)...) + } + } + + return strings.Join(segments, " ") +} + +// IsOperation is true when the split key is in the operations section +func (s SplitKey) IsOperation() bool { + return len(s) > 1 && s[0] == paths +} + +// IsSharedOperationParam is true when the split key is in the parameters section of a path +func (s SplitKey) IsSharedOperationParam() bool { + return len(s) > 2 && s[0] == paths && s[2] == parameters +} + +// IsSharedParam is true when the split key is in the #/parameters section of a spec +func (s SplitKey) IsSharedParam() bool { + return len(s) > 1 && s[0] == parameters +} + +// IsOperationParam is true when the split key is in the parameters section of an operation +func (s SplitKey) IsOperationParam() bool { + return len(s) > 3 && s[0] == paths && s[3] == parameters +} + +// IsOperationResponse is true when the split key is in the responses section of an operation +func (s SplitKey) IsOperationResponse() bool { + return len(s) > 3 && s[0] == paths && s[3] == responses +} + +// IsSharedResponse is true when the split key is in the #/responses section of a spec +func (s SplitKey) IsSharedResponse() bool { + return len(s) > 1 && s[0] == responses +} + +// IsDefaultResponse is true when the split key is the default response for an operation +func (s SplitKey) IsDefaultResponse() bool { + return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default" +} + +// IsStatusCodeResponse is true when the split key is an operation response with a status code +func (s SplitKey) IsStatusCodeResponse() bool { + isInt := func() bool { + _, err := strconv.Atoi(s[4]) + + return err == nil + } + + return len(s) > 4 && s[0] == paths && s[3] == responses && isInt() +} + +// ResponseName yields either the status code or "Default" for a response +func (s SplitKey) ResponseName() string { + if s.IsStatusCodeResponse() { + code, _ := strconv.Atoi(s[4]) + + return http.StatusText(code) + } + + if s.IsDefaultResponse() { + return "Default" + } + + return "" +} + +// PathItemRef constructs a $ref object from a split key of the form /{path}/{method} +func (s SplitKey) PathItemRef() spec.Ref { + if len(s) < 3 { + return spec.Ref{} + } + + pth, method := s[1], s[2] + if _, isValidMethod := validMethods[strings.ToUpper(method)]; !isValidMethod && !strings.HasPrefix(method, "x-") { + return spec.Ref{} + } + + return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(pth), strings.ToUpper(method))) +} + +// PathRef constructs a $ref object from a split key of the form /paths/{reference} +func (s SplitKey) PathRef() spec.Ref { + if !s.IsOperation() { + return spec.Ref{} + } + + return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(s[1]))) +} diff --git a/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go b/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go new file mode 100644 index 00000000..73243df8 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go @@ -0,0 +1,141 @@ +package sortref + +import ( + "reflect" + "sort" + "strings" + + "github.com/go-openapi/analysis/internal/flatten/normalize" + "github.com/go-openapi/spec" +) + +var depthGroupOrder = []string{ + "sharedParam", "sharedResponse", "sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition", +} + +type mapIterator struct { + len int + mapIter *reflect.MapIter +} + +func (i *mapIterator) Next() bool { + return i.mapIter.Next() +} + +func (i *mapIterator) Len() int { + return i.len +} + +func (i *mapIterator) Key() string { + return i.mapIter.Key().String() +} + +func mustMapIterator(anyMap interface{}) *mapIterator { + val := reflect.ValueOf(anyMap) + + return &mapIterator{mapIter: val.MapRange(), len: val.Len()} +} + +// DepthFirst sorts a map of anything. It groups keys by category +// (shared params, op param, statuscode response, default response, definitions) +// sort groups internally by number of parts in the key and lexical names +// flatten groups into a single list of keys +func DepthFirst(in interface{}) []string { + iterator := mustMapIterator(in) + sorted := make([]string, 0, iterator.Len()) + grouped := make(map[string]Keys, iterator.Len()) + + for iterator.Next() { + k := iterator.Key() + split := KeyParts(k) + var pk string + + if split.IsSharedOperationParam() { + pk = "sharedOpParam" + } + if split.IsOperationParam() { + pk = "opParam" + } + if split.IsStatusCodeResponse() { + pk = "codeResponse" + } + if split.IsDefaultResponse() { + pk = "defaultResponse" + } + if split.IsDefinition() { + pk = "definition" + } + if split.IsSharedParam() { + pk = "sharedParam" + } + if split.IsSharedResponse() { + pk = "sharedResponse" + } + grouped[pk] = append(grouped[pk], Key{Segments: len(split), Key: k}) + } + + for _, pk := range depthGroupOrder { + res := grouped[pk] + sort.Sort(res) + + for _, v := range res { + sorted = append(sorted, v.Key) + } + } + + return sorted +} + +// topMostRefs is able to sort refs by hierarchical then lexicographic order, +// yielding refs ordered breadth-first. +type topmostRefs []string + +func (k topmostRefs) Len() int { return len(k) } +func (k topmostRefs) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k topmostRefs) Less(i, j int) bool { + li, lj := len(strings.Split(k[i], "/")), len(strings.Split(k[j], "/")) + if li == lj { + return k[i] < k[j] + } + + return li < lj +} + +// TopmostFirst sorts references by depth +func TopmostFirst(refs []string) []string { + res := topmostRefs(refs) + sort.Sort(res) + + return res +} + +// RefRevIdx is a reverse index for references +type RefRevIdx struct { + Ref spec.Ref + Keys []string +} + +// ReverseIndex builds a reverse index for references in schemas +func ReverseIndex(schemas map[string]spec.Ref, basePath string) map[string]RefRevIdx { + collected := make(map[string]RefRevIdx) + for key, schRef := range schemas { + // normalize paths before sorting, + // so we get together keys that are from the same external file + normalizedPath := normalize.Path(schRef, basePath) + + entry, ok := collected[normalizedPath] + if ok { + entry.Keys = append(entry.Keys, key) + collected[normalizedPath] = entry + + continue + } + + collected[normalizedPath] = RefRevIdx{ + Ref: schRef, + Keys: []string{key}, + } + } + + return collected +} diff --git a/vendor/github.com/go-openapi/analysis/mixin.go b/vendor/github.com/go-openapi/analysis/mixin.go new file mode 100644 index 00000000..b2530526 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/mixin.go @@ -0,0 +1,515 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analysis + +import ( + "fmt" + "reflect" + + "github.com/go-openapi/spec" +) + +// Mixin modifies the primary swagger spec by adding the paths and +// definitions from the mixin specs. Top level parameters and +// responses from the mixins are also carried over. Operation id +// collisions are avoided by appending "Mixin" but only if +// needed. +// +// The following parts of primary are subject to merge, filling empty details +// - Info +// - BasePath +// - Host +// - ExternalDocs +// +// Consider calling FixEmptyResponseDescriptions() on the modified primary +// if you read them from storage and they are valid to start with. +// +// Entries in "paths", "definitions", "parameters" and "responses" are +// added to the primary in the order of the given mixins. If the entry +// already exists in primary it is skipped with a warning message. +// +// The count of skipped entries (from collisions) is returned so any +// deviation from the number expected can flag a warning in your build +// scripts. Carefully review the collisions before accepting them; +// consider renaming things if possible. +// +// No key normalization takes place (paths, type defs, +// etc). Ensure they are canonical if your downstream tools do +// key normalization of any form. +// +// Merging schemes (http, https), and consumers/producers do not account for +// collisions. +func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string { + skipped := make([]string, 0, len(mixins)) + opIds := getOpIds(primary) + initPrimary(primary) + + for i, m := range mixins { + skipped = append(skipped, mergeSwaggerProps(primary, m)...) + + skipped = append(skipped, mergeConsumes(primary, m)...) + + skipped = append(skipped, mergeProduces(primary, m)...) + + skipped = append(skipped, mergeTags(primary, m)...) + + skipped = append(skipped, mergeSchemes(primary, m)...) + + skipped = append(skipped, mergeSecurityDefinitions(primary, m)...) + + skipped = append(skipped, mergeSecurityRequirements(primary, m)...) + + skipped = append(skipped, mergeDefinitions(primary, m)...) + + // merging paths requires a map of operationIDs to work with + skipped = append(skipped, mergePaths(primary, m, opIds, i)...) + + skipped = append(skipped, mergeParameters(primary, m)...) + + skipped = append(skipped, mergeResponses(primary, m)...) + } + + return skipped +} + +// getOpIds extracts all the paths..operationIds from the given +// spec and returns them as the keys in a map with 'true' values. +func getOpIds(s *spec.Swagger) map[string]bool { + rv := make(map[string]bool) + if s.Paths == nil { + return rv + } + + for _, v := range s.Paths.Paths { + piops := pathItemOps(v) + + for _, op := range piops { + rv[op.ID] = true + } + } + + return rv +} + +func pathItemOps(p spec.PathItem) []*spec.Operation { + var rv []*spec.Operation + rv = appendOp(rv, p.Get) + rv = appendOp(rv, p.Put) + rv = appendOp(rv, p.Post) + rv = appendOp(rv, p.Delete) + rv = appendOp(rv, p.Head) + rv = appendOp(rv, p.Patch) + + return rv +} + +func appendOp(ops []*spec.Operation, op *spec.Operation) []*spec.Operation { + if op == nil { + return ops + } + + return append(ops, op) +} + +func mergeSecurityDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.SecurityDefinitions { + if _, exists := primary.SecurityDefinitions[k]; exists { + warn := fmt.Sprintf( + "SecurityDefinitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + + continue + } + + primary.SecurityDefinitions[k] = v + } + + return +} + +func mergeSecurityRequirements(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Security { + found := false + for _, vv := range primary.Security { + if reflect.DeepEqual(v, vv) { + found = true + + break + } + } + + if found { + warn := fmt.Sprintf( + "Security requirement: '%v' already exists in primary or higher priority mixin, skipping\n", v) + skipped = append(skipped, warn) + + continue + } + primary.Security = append(primary.Security, v) + } + + return +} + +func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Definitions { + // assume name collisions represent IDENTICAL type. careful. + if _, exists := primary.Definitions[k]; exists { + warn := fmt.Sprintf( + "definitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + + continue + } + primary.Definitions[k] = v + } + + return +} + +func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, mixIndex int) (skipped []string) { + if m.Paths != nil { + for k, v := range m.Paths.Paths { + if _, exists := primary.Paths.Paths[k]; exists { + warn := fmt.Sprintf( + "paths entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + + continue + } + + // Swagger requires that operationIds be + // unique within a spec. If we find a + // collision we append "Mixin0" to the + // operatoinId we are adding, where 0 is mixin + // index. We assume that operationIds with + // all the proivded specs are already unique. + piops := pathItemOps(v) + for _, piop := range piops { + if opIds[piop.ID] { + piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex) + } + opIds[piop.ID] = true + } + primary.Paths.Paths[k] = v + } + } + + return +} + +func mergeParameters(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Parameters { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Parameters[k]; exists { + warn := fmt.Sprintf( + "top level parameters entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + + continue + } + primary.Parameters[k] = v + } + + return +} + +func mergeResponses(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Responses { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Responses[k]; exists { + warn := fmt.Sprintf( + "top level responses entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + + continue + } + primary.Responses[k] = v + } + + return skipped +} + +func mergeConsumes(primary *spec.Swagger, m *spec.Swagger) []string { + for _, v := range m.Consumes { + found := false + for _, vv := range primary.Consumes { + if v == vv { + found = true + + break + } + } + + if found { + // no warning here: we just skip it + continue + } + primary.Consumes = append(primary.Consumes, v) + } + + return []string{} +} + +func mergeProduces(primary *spec.Swagger, m *spec.Swagger) []string { + for _, v := range m.Produces { + found := false + for _, vv := range primary.Produces { + if v == vv { + found = true + + break + } + } + + if found { + // no warning here: we just skip it + continue + } + primary.Produces = append(primary.Produces, v) + } + + return []string{} +} + +func mergeTags(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Tags { + found := false + for _, vv := range primary.Tags { + if v.Name == vv.Name { + found = true + + break + } + } + + if found { + warn := fmt.Sprintf( + "top level tags entry with name '%v' already exists in primary or higher priority mixin, skipping\n", + v.Name, + ) + skipped = append(skipped, warn) + + continue + } + + primary.Tags = append(primary.Tags, v) + } + + return +} + +func mergeSchemes(primary *spec.Swagger, m *spec.Swagger) []string { + for _, v := range m.Schemes { + found := false + for _, vv := range primary.Schemes { + if v == vv { + found = true + + break + } + } + + if found { + // no warning here: we just skip it + continue + } + primary.Schemes = append(primary.Schemes, v) + } + + return []string{} +} + +func mergeSwaggerProps(primary *spec.Swagger, m *spec.Swagger) []string { + var skipped, skippedInfo, skippedDocs []string + + primary.Extensions, skipped = mergeExtensions(primary.Extensions, m.Extensions) + + // merging details in swagger top properties + if primary.Host == "" { + primary.Host = m.Host + } + + if primary.BasePath == "" { + primary.BasePath = m.BasePath + } + + if primary.Info == nil { + primary.Info = m.Info + } else if m.Info != nil { + skippedInfo = mergeInfo(primary.Info, m.Info) + skipped = append(skipped, skippedInfo...) + } + + if primary.ExternalDocs == nil { + primary.ExternalDocs = m.ExternalDocs + } else if m != nil { + skippedDocs = mergeExternalDocs(primary.ExternalDocs, m.ExternalDocs) + skipped = append(skipped, skippedDocs...) + } + + return skipped +} + +// nolint: unparam +func mergeExternalDocs(primary *spec.ExternalDocumentation, m *spec.ExternalDocumentation) []string { + if primary.Description == "" { + primary.Description = m.Description + } + + if primary.URL == "" { + primary.URL = m.URL + } + + return nil +} + +func mergeInfo(primary *spec.Info, m *spec.Info) []string { + var sk, skipped []string + + primary.Extensions, sk = mergeExtensions(primary.Extensions, m.Extensions) + skipped = append(skipped, sk...) + + if primary.Description == "" { + primary.Description = m.Description + } + + if primary.Title == "" { + primary.Description = m.Description + } + + if primary.TermsOfService == "" { + primary.TermsOfService = m.TermsOfService + } + + if primary.Version == "" { + primary.Version = m.Version + } + + if primary.Contact == nil { + primary.Contact = m.Contact + } else if m.Contact != nil { + var csk []string + primary.Contact.Extensions, csk = mergeExtensions(primary.Contact.Extensions, m.Contact.Extensions) + skipped = append(skipped, csk...) + + if primary.Contact.Name == "" { + primary.Contact.Name = m.Contact.Name + } + + if primary.Contact.URL == "" { + primary.Contact.URL = m.Contact.URL + } + + if primary.Contact.Email == "" { + primary.Contact.Email = m.Contact.Email + } + } + + if primary.License == nil { + primary.License = m.License + } else if m.License != nil { + var lsk []string + primary.License.Extensions, lsk = mergeExtensions(primary.License.Extensions, m.License.Extensions) + skipped = append(skipped, lsk...) + + if primary.License.Name == "" { + primary.License.Name = m.License.Name + } + + if primary.License.URL == "" { + primary.License.URL = m.License.URL + } + } + + return skipped +} + +func mergeExtensions(primary spec.Extensions, m spec.Extensions) (result spec.Extensions, skipped []string) { + if primary == nil { + result = m + + return + } + + if m == nil { + result = primary + + return + } + + result = primary + for k, v := range m { + if _, found := primary[k]; found { + skipped = append(skipped, k) + + continue + } + + primary[k] = v + } + + return +} + +func initPrimary(primary *spec.Swagger) { + if primary.SecurityDefinitions == nil { + primary.SecurityDefinitions = make(map[string]*spec.SecurityScheme) + } + + if primary.Security == nil { + primary.Security = make([]map[string][]string, 0, 10) + } + + if primary.Produces == nil { + primary.Produces = make([]string, 0, 10) + } + + if primary.Consumes == nil { + primary.Consumes = make([]string, 0, 10) + } + + if primary.Tags == nil { + primary.Tags = make([]spec.Tag, 0, 10) + } + + if primary.Schemes == nil { + primary.Schemes = make([]string, 0, 10) + } + + if primary.Paths == nil { + primary.Paths = &spec.Paths{Paths: make(map[string]spec.PathItem)} + } + + if primary.Paths.Paths == nil { + primary.Paths.Paths = make(map[string]spec.PathItem) + } + + if primary.Definitions == nil { + primary.Definitions = make(spec.Definitions) + } + + if primary.Parameters == nil { + primary.Parameters = make(map[string]spec.Parameter) + } + + if primary.Responses == nil { + primary.Responses = make(map[string]spec.Response) + } +} diff --git a/vendor/github.com/go-openapi/analysis/schema.go b/vendor/github.com/go-openapi/analysis/schema.go new file mode 100644 index 00000000..fc055095 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/schema.go @@ -0,0 +1,256 @@ +package analysis + +import ( + "fmt" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// SchemaOpts configures the schema analyzer +type SchemaOpts struct { + Schema *spec.Schema + Root interface{} + BasePath string + _ struct{} +} + +// Schema analysis, will classify the schema according to known +// patterns. +func Schema(opts SchemaOpts) (*AnalyzedSchema, error) { + if opts.Schema == nil { + return nil, fmt.Errorf("no schema to analyze") + } + + a := &AnalyzedSchema{ + schema: opts.Schema, + root: opts.Root, + basePath: opts.BasePath, + } + + a.initializeFlags() + a.inferKnownType() + a.inferEnum() + a.inferBaseType() + + if err := a.inferMap(); err != nil { + return nil, err + } + if err := a.inferArray(); err != nil { + return nil, err + } + + a.inferTuple() + + if err := a.inferFromRef(); err != nil { + return nil, err + } + + a.inferSimpleSchema() + + return a, nil +} + +// AnalyzedSchema indicates what the schema represents +type AnalyzedSchema struct { + schema *spec.Schema + root interface{} + basePath string + + hasProps bool + hasAllOf bool + hasItems bool + hasAdditionalProps bool + hasAdditionalItems bool + hasRef bool + + IsKnownType bool + IsSimpleSchema bool + IsArray bool + IsSimpleArray bool + IsMap bool + IsSimpleMap bool + IsExtendedObject bool + IsTuple bool + IsTupleWithExtra bool + IsBaseType bool + IsEnum bool +} + +// Inherits copies value fields from other onto this schema +func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) { + if other == nil { + return + } + a.hasProps = other.hasProps + a.hasAllOf = other.hasAllOf + a.hasItems = other.hasItems + a.hasAdditionalItems = other.hasAdditionalItems + a.hasAdditionalProps = other.hasAdditionalProps + a.hasRef = other.hasRef + + a.IsKnownType = other.IsKnownType + a.IsSimpleSchema = other.IsSimpleSchema + a.IsArray = other.IsArray + a.IsSimpleArray = other.IsSimpleArray + a.IsMap = other.IsMap + a.IsSimpleMap = other.IsSimpleMap + a.IsExtendedObject = other.IsExtendedObject + a.IsTuple = other.IsTuple + a.IsTupleWithExtra = other.IsTupleWithExtra + a.IsBaseType = other.IsBaseType + a.IsEnum = other.IsEnum +} + +func (a *AnalyzedSchema) inferFromRef() error { + if a.hasRef { + sch := new(spec.Schema) + sch.Ref = a.schema.Ref + err := spec.ExpandSchema(sch, a.root, nil) + if err != nil { + return err + } + rsch, err := Schema(SchemaOpts{ + Schema: sch, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + // NOTE(fredbi): currently the only cause for errors is + // unresolved ref. Since spec.ExpandSchema() expands the + // schema recursively, there is no chance to get there, + // until we add more causes for error in this schema analysis. + return err + } + a.inherits(rsch) + } + + return nil +} + +func (a *AnalyzedSchema) inferSimpleSchema() { + a.IsSimpleSchema = a.IsKnownType || a.IsSimpleArray || a.IsSimpleMap +} + +func (a *AnalyzedSchema) inferKnownType() { + tpe := a.schema.Type + format := a.schema.Format + a.IsKnownType = tpe.Contains("boolean") || + tpe.Contains("integer") || + tpe.Contains("number") || + tpe.Contains("string") || + (format != "" && strfmt.Default.ContainsName(format)) || + (a.isObjectType() && !a.hasProps && !a.hasAllOf && !a.hasAdditionalProps && !a.hasAdditionalItems) +} + +func (a *AnalyzedSchema) inferMap() error { + if !a.isObjectType() { + return nil + } + + hasExtra := a.hasProps || a.hasAllOf + a.IsMap = a.hasAdditionalProps && !hasExtra + a.IsExtendedObject = a.hasAdditionalProps && hasExtra + + if !a.IsMap { + return nil + } + + // maps + if a.schema.AdditionalProperties.Schema != nil { + msch, err := Schema(SchemaOpts{ + Schema: a.schema.AdditionalProperties.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleMap = msch.IsSimpleSchema + } else if a.schema.AdditionalProperties.Allows { + a.IsSimpleMap = true + } + + return nil +} + +func (a *AnalyzedSchema) inferArray() error { + // an array has Items defined as an object schema, otherwise we qualify this JSON array as a tuple + // (yes, even if the Items array contains only one element). + // arrays in JSON schema may be unrestricted (i.e no Items specified). + // Note that arrays in Swagger MUST have Items. Nonetheless, we analyze unrestricted arrays. + // + // NOTE: the spec package misses the distinction between: + // items: [] and items: {}, so we consider both arrays here. + a.IsArray = a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Schemas == nil) + if a.IsArray && a.hasItems { + if a.schema.Items.Schema != nil { + itsch, err := Schema(SchemaOpts{ + Schema: a.schema.Items.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + + a.IsSimpleArray = itsch.IsSimpleSchema + } + } + + if a.IsArray && !a.hasItems { + a.IsSimpleArray = true + } + + return nil +} + +func (a *AnalyzedSchema) inferTuple() { + tuple := a.hasItems && a.schema.Items.Schemas != nil + a.IsTuple = tuple && !a.hasAdditionalItems + a.IsTupleWithExtra = tuple && a.hasAdditionalItems +} + +func (a *AnalyzedSchema) inferBaseType() { + if a.isObjectType() { + a.IsBaseType = a.schema.Discriminator != "" + } +} + +func (a *AnalyzedSchema) inferEnum() { + a.IsEnum = len(a.schema.Enum) > 0 +} + +func (a *AnalyzedSchema) initializeFlags() { + a.hasProps = len(a.schema.Properties) > 0 + a.hasAllOf = len(a.schema.AllOf) > 0 + a.hasRef = a.schema.Ref.String() != "" + + a.hasItems = a.schema.Items != nil && + (a.schema.Items.Schema != nil || len(a.schema.Items.Schemas) > 0) + + a.hasAdditionalProps = a.schema.AdditionalProperties != nil && + (a.schema.AdditionalProperties.Schema != nil || a.schema.AdditionalProperties.Allows) + + a.hasAdditionalItems = a.schema.AdditionalItems != nil && + (a.schema.AdditionalItems.Schema != nil || a.schema.AdditionalItems.Allows) +} + +func (a *AnalyzedSchema) isObjectType() bool { + return !a.hasRef && (a.schema.Type == nil || a.schema.Type.Contains("") || a.schema.Type.Contains("object")) +} + +func (a *AnalyzedSchema) isArrayType() bool { + return !a.hasRef && (a.schema.Type != nil && a.schema.Type.Contains("array")) +} + +// isAnalyzedAsComplex determines if an analyzed schema is eligible to flattening (i.e. it is "complex"). +// +// Complex means the schema is any of: +// - a simple type (primitive) +// - an array of something (items are possibly complex ; if this is the case, items will generate a definition) +// - a map of something (additionalProperties are possibly complex ; if this is the case, additionalProperties will +// generate a definition) +func (a *AnalyzedSchema) isAnalyzedAsComplex() bool { + return !a.IsSimpleSchema && !a.IsArray && !a.IsMap +} diff --git a/vendor/github.com/go-openapi/errors/.gitattributes b/vendor/github.com/go-openapi/errors/.gitattributes new file mode 100644 index 00000000..a0717e4b --- /dev/null +++ b/vendor/github.com/go-openapi/errors/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf \ No newline at end of file diff --git a/vendor/github.com/go-openapi/errors/.gitignore b/vendor/github.com/go-openapi/errors/.gitignore new file mode 100644 index 00000000..dd91ed6a --- /dev/null +++ b/vendor/github.com/go-openapi/errors/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/vendor/github.com/go-openapi/errors/.golangci.yml b/vendor/github.com/go-openapi/errors/.golangci.yml new file mode 100644 index 00000000..4e1fc0c7 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/.golangci.yml @@ -0,0 +1,48 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoglobals + - godox + - gocognit + - whitespace + - wsl + - funlen + - gochecknoglobals + - gochecknoinits + - scopelint + - wrapcheck + - exhaustivestruct + - exhaustive + - nlreturn + - testpackage + - gci + - gofumpt + - goerr113 + - gomnd + - tparallel + - nestif + - godot + - errorlint + - paralleltest + - tparallel + - cyclop + - errname + - varnamelen + - exhaustruct + - maintidx diff --git a/vendor/github.com/go-openapi/errors/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/errors/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/errors/LICENSE b/vendor/github.com/go-openapi/errors/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/errors/README.md b/vendor/github.com/go-openapi/errors/README.md new file mode 100644 index 00000000..4aac049e --- /dev/null +++ b/vendor/github.com/go-openapi/errors/README.md @@ -0,0 +1,11 @@ +# OpenAPI errors + +[![Build Status](https://travis-ci.org/go-openapi/errors.svg?branch=master)](https://travis-ci.org/go-openapi/errors) +[![codecov](https://codecov.io/gh/go-openapi/errors/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/errors) +[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/errors/master/LICENSE) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/errors.svg)](https://pkg.go.dev/github.com/go-openapi/errors) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/errors.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/errors)](https://goreportcard.com/report/github.com/go-openapi/errors) + +Shared errors and error interface used throughout the various libraries found in the go-openapi toolkit. diff --git a/vendor/github.com/go-openapi/errors/api.go b/vendor/github.com/go-openapi/errors/api.go new file mode 100644 index 00000000..77f1f92c --- /dev/null +++ b/vendor/github.com/go-openapi/errors/api.go @@ -0,0 +1,182 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" +) + +// DefaultHTTPCode is used when the error Code cannot be used as an HTTP code. +var DefaultHTTPCode = http.StatusUnprocessableEntity + +// Error represents a error interface all swagger framework errors implement +type Error interface { + error + Code() int32 +} + +type apiError struct { + code int32 + message string +} + +func (a *apiError) Error() string { + return a.message +} + +func (a *apiError) Code() int32 { + return a.code +} + +// MarshalJSON implements the JSON encoding interface +func (a apiError) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "code": a.code, + "message": a.message, + }) +} + +// New creates a new API error with a code and a message +func New(code int32, message string, args ...interface{}) Error { + if len(args) > 0 { + return &apiError{code, fmt.Sprintf(message, args...)} + } + return &apiError{code, message} +} + +// NotFound creates a new not found error +func NotFound(message string, args ...interface{}) Error { + if message == "" { + message = "Not found" + } + return New(http.StatusNotFound, fmt.Sprintf(message, args...)) +} + +// NotImplemented creates a new not implemented error +func NotImplemented(message string) Error { + return New(http.StatusNotImplemented, message) +} + +// MethodNotAllowedError represents an error for when the path matches but the method doesn't +type MethodNotAllowedError struct { + code int32 + Allowed []string + message string +} + +func (m *MethodNotAllowedError) Error() string { + return m.message +} + +// Code the error code +func (m *MethodNotAllowedError) Code() int32 { + return m.code +} + +// MarshalJSON implements the JSON encoding interface +func (m MethodNotAllowedError) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "code": m.code, + "message": m.message, + "allowed": m.Allowed, + }) +} + +func errorAsJSON(err Error) []byte { + //nolint:errchkjson + b, _ := json.Marshal(struct { + Code int32 `json:"code"` + Message string `json:"message"` + }{err.Code(), err.Error()}) + return b +} + +func flattenComposite(errs *CompositeError) *CompositeError { + var res []error + for _, er := range errs.Errors { + switch e := er.(type) { + case *CompositeError: + if len(e.Errors) > 0 { + flat := flattenComposite(e) + if len(flat.Errors) > 0 { + res = append(res, flat.Errors...) + } + } + default: + if e != nil { + res = append(res, e) + } + } + } + return CompositeValidationError(res...) +} + +// MethodNotAllowed creates a new method not allowed error +func MethodNotAllowed(requested string, allow []string) Error { + msg := fmt.Sprintf("method %s is not allowed, but [%s] are", requested, strings.Join(allow, ",")) + return &MethodNotAllowedError{code: http.StatusMethodNotAllowed, Allowed: allow, message: msg} +} + +// ServeError the error handler interface implementation +func ServeError(rw http.ResponseWriter, r *http.Request, err error) { + rw.Header().Set("Content-Type", "application/json") + switch e := err.(type) { + case *CompositeError: + er := flattenComposite(e) + // strips composite errors to first element only + if len(er.Errors) > 0 { + ServeError(rw, r, er.Errors[0]) + } else { + // guard against empty CompositeError (invalid construct) + ServeError(rw, r, nil) + } + case *MethodNotAllowedError: + rw.Header().Add("Allow", strings.Join(e.Allowed, ",")) + rw.WriteHeader(asHTTPCode(int(e.Code()))) + if r == nil || r.Method != http.MethodHead { + _, _ = rw.Write(errorAsJSON(e)) + } + case Error: + value := reflect.ValueOf(e) + if value.Kind() == reflect.Ptr && value.IsNil() { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error"))) + return + } + rw.WriteHeader(asHTTPCode(int(e.Code()))) + if r == nil || r.Method != http.MethodHead { + _, _ = rw.Write(errorAsJSON(e)) + } + case nil: + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, "Unknown error"))) + default: + rw.WriteHeader(http.StatusInternalServerError) + if r == nil || r.Method != http.MethodHead { + _, _ = rw.Write(errorAsJSON(New(http.StatusInternalServerError, err.Error()))) + } + } +} + +func asHTTPCode(input int) int { + if input >= 600 { + return DefaultHTTPCode + } + return input +} diff --git a/vendor/github.com/go-openapi/errors/auth.go b/vendor/github.com/go-openapi/errors/auth.go new file mode 100644 index 00000000..0545b501 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/auth.go @@ -0,0 +1,22 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import "net/http" + +// Unauthenticated returns an unauthenticated error +func Unauthenticated(scheme string) Error { + return New(http.StatusUnauthorized, "unauthenticated for %s", scheme) +} diff --git a/vendor/github.com/go-openapi/errors/doc.go b/vendor/github.com/go-openapi/errors/doc.go new file mode 100644 index 00000000..af01190c --- /dev/null +++ b/vendor/github.com/go-openapi/errors/doc.go @@ -0,0 +1,26 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +Package errors provides an Error interface and several concrete types +implementing this interface to manage API errors and JSON-schema validation +errors. + +A middleware handler ServeError() is provided to serve the errors types +it defines. + +It is used throughout the various go-openapi toolkit libraries +(https://github.com/go-openapi). +*/ +package errors diff --git a/vendor/github.com/go-openapi/errors/headers.go b/vendor/github.com/go-openapi/errors/headers.go new file mode 100644 index 00000000..dfebe8f9 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/headers.go @@ -0,0 +1,103 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// Validation represents a failure of a precondition +type Validation struct { + code int32 + Name string + In string + Value interface{} + message string + Values []interface{} +} + +func (e *Validation) Error() string { + return e.message +} + +// Code the error code +func (e *Validation) Code() int32 { + return e.code +} + +// MarshalJSON implements the JSON encoding interface +func (e Validation) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "code": e.code, + "message": e.message, + "in": e.In, + "name": e.Name, + "value": e.Value, + "values": e.Values, + }) +} + +// ValidateName sets the name for a validation or updates it for a nested property +func (e *Validation) ValidateName(name string) *Validation { + if name != "" { + if e.Name == "" { + e.Name = name + e.message = name + e.message + } else { + e.Name = name + "." + e.Name + e.message = name + "." + e.message + } + } + return e +} + +const ( + contentTypeFail = `unsupported media type %q, only %v are allowed` + responseFormatFail = `unsupported media type requested, only %v are available` +) + +// InvalidContentType error for an invalid content type +func InvalidContentType(value string, allowed []string) *Validation { + values := make([]interface{}, 0, len(allowed)) + for _, v := range allowed { + values = append(values, v) + } + return &Validation{ + code: http.StatusUnsupportedMediaType, + Name: "Content-Type", + In: "header", + Value: value, + Values: values, + message: fmt.Sprintf(contentTypeFail, value, allowed), + } +} + +// InvalidResponseFormat error for an unacceptable response format request +func InvalidResponseFormat(value string, allowed []string) *Validation { + values := make([]interface{}, 0, len(allowed)) + for _, v := range allowed { + values = append(values, v) + } + return &Validation{ + code: http.StatusNotAcceptable, + Name: "Accept", + In: "header", + Value: value, + Values: values, + message: fmt.Sprintf(responseFormatFail, allowed), + } +} diff --git a/vendor/github.com/go-openapi/errors/middleware.go b/vendor/github.com/go-openapi/errors/middleware.go new file mode 100644 index 00000000..963472d1 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/middleware.go @@ -0,0 +1,50 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "bytes" + "fmt" + "strings" +) + +// APIVerificationFailed is an error that contains all the missing info for a mismatched section +// between the api registrations and the api spec +type APIVerificationFailed struct { + Section string `json:"section,omitempty"` + MissingSpecification []string `json:"missingSpecification,omitempty"` + MissingRegistration []string `json:"missingRegistration,omitempty"` +} + +func (v *APIVerificationFailed) Error() string { + buf := bytes.NewBuffer(nil) + + hasRegMissing := len(v.MissingRegistration) > 0 + hasSpecMissing := len(v.MissingSpecification) > 0 + + if hasRegMissing { + buf.WriteString(fmt.Sprintf("missing [%s] %s registrations", strings.Join(v.MissingRegistration, ", "), v.Section)) + } + + if hasRegMissing && hasSpecMissing { + buf.WriteString("\n") + } + + if hasSpecMissing { + buf.WriteString(fmt.Sprintf("missing from spec file [%s] %s", strings.Join(v.MissingSpecification, ", "), v.Section)) + } + + return buf.String() +} diff --git a/vendor/github.com/go-openapi/errors/parsing.go b/vendor/github.com/go-openapi/errors/parsing.go new file mode 100644 index 00000000..5096e1ea --- /dev/null +++ b/vendor/github.com/go-openapi/errors/parsing.go @@ -0,0 +1,78 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "encoding/json" + "fmt" +) + +// ParseError represents a parsing error +type ParseError struct { + code int32 + Name string + In string + Value string + Reason error + message string +} + +func (e *ParseError) Error() string { + return e.message +} + +// Code returns the http status code for this error +func (e *ParseError) Code() int32 { + return e.code +} + +// MarshalJSON implements the JSON encoding interface +func (e ParseError) MarshalJSON() ([]byte, error) { + var reason string + if e.Reason != nil { + reason = e.Reason.Error() + } + return json.Marshal(map[string]interface{}{ + "code": e.code, + "message": e.message, + "in": e.In, + "name": e.Name, + "value": e.Value, + "reason": reason, + }) +} + +const ( + parseErrorTemplContent = `parsing %s %s from %q failed, because %s` + parseErrorTemplContentNoIn = `parsing %s from %q failed, because %s` +) + +// NewParseError creates a new parse error +func NewParseError(name, in, value string, reason error) *ParseError { + var msg string + if in == "" { + msg = fmt.Sprintf(parseErrorTemplContentNoIn, name, value, reason) + } else { + msg = fmt.Sprintf(parseErrorTemplContent, name, in, value, reason) + } + return &ParseError{ + code: 400, + Name: name, + In: in, + Value: value, + Reason: reason, + message: msg, + } +} diff --git a/vendor/github.com/go-openapi/errors/schema.go b/vendor/github.com/go-openapi/errors/schema.go new file mode 100644 index 00000000..da5f6c78 --- /dev/null +++ b/vendor/github.com/go-openapi/errors/schema.go @@ -0,0 +1,611 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + invalidType = "%s is an invalid type name" + typeFail = "%s in %s must be of type %s" + typeFailWithData = "%s in %s must be of type %s: %q" + typeFailWithError = "%s in %s must be of type %s, because: %s" + requiredFail = "%s in %s is required" + readOnlyFail = "%s in %s is readOnly" + tooLongMessage = "%s in %s should be at most %d chars long" + tooShortMessage = "%s in %s should be at least %d chars long" + patternFail = "%s in %s should match '%s'" + enumFail = "%s in %s should be one of %v" + multipleOfFail = "%s in %s should be a multiple of %v" + maxIncFail = "%s in %s should be less than or equal to %v" + maxExcFail = "%s in %s should be less than %v" + minIncFail = "%s in %s should be greater than or equal to %v" + minExcFail = "%s in %s should be greater than %v" + uniqueFail = "%s in %s shouldn't contain duplicates" + maxItemsFail = "%s in %s should have at most %d items" + minItemsFail = "%s in %s should have at least %d items" + typeFailNoIn = "%s must be of type %s" + typeFailWithDataNoIn = "%s must be of type %s: %q" + typeFailWithErrorNoIn = "%s must be of type %s, because: %s" + requiredFailNoIn = "%s is required" + readOnlyFailNoIn = "%s is readOnly" + tooLongMessageNoIn = "%s should be at most %d chars long" + tooShortMessageNoIn = "%s should be at least %d chars long" + patternFailNoIn = "%s should match '%s'" + enumFailNoIn = "%s should be one of %v" + multipleOfFailNoIn = "%s should be a multiple of %v" + maxIncFailNoIn = "%s should be less than or equal to %v" + maxExcFailNoIn = "%s should be less than %v" + minIncFailNoIn = "%s should be greater than or equal to %v" + minExcFailNoIn = "%s should be greater than %v" + uniqueFailNoIn = "%s shouldn't contain duplicates" + maxItemsFailNoIn = "%s should have at most %d items" + minItemsFailNoIn = "%s should have at least %d items" + noAdditionalItems = "%s in %s can't have additional items" + noAdditionalItemsNoIn = "%s can't have additional items" + tooFewProperties = "%s in %s should have at least %d properties" + tooFewPropertiesNoIn = "%s should have at least %d properties" + tooManyProperties = "%s in %s should have at most %d properties" + tooManyPropertiesNoIn = "%s should have at most %d properties" + unallowedProperty = "%s.%s in %s is a forbidden property" + unallowedPropertyNoIn = "%s.%s is a forbidden property" + failedAllPatternProps = "%s.%s in %s failed all pattern properties" + failedAllPatternPropsNoIn = "%s.%s failed all pattern properties" + multipleOfMustBePositive = "factor MultipleOf declared for %s must be positive: %v" +) + +// All code responses can be used to differentiate errors for different handling +// by the consuming program +const ( + // CompositeErrorCode remains 422 for backwards-compatibility + // and to separate it from validation errors with cause + CompositeErrorCode = 422 + // InvalidTypeCode is used for any subclass of invalid types + InvalidTypeCode = 600 + iota + RequiredFailCode + TooLongFailCode + TooShortFailCode + PatternFailCode + EnumFailCode + MultipleOfFailCode + MaxFailCode + MinFailCode + UniqueFailCode + MaxItemsFailCode + MinItemsFailCode + NoAdditionalItemsCode + TooFewPropertiesCode + TooManyPropertiesCode + UnallowedPropertyCode + FailedAllPatternPropsCode + MultipleOfMustBePositiveCode + ReadOnlyFailCode +) + +// CompositeError is an error that groups several errors together +type CompositeError struct { + Errors []error + code int32 + message string +} + +// Code for this error +func (c *CompositeError) Code() int32 { + return c.code +} + +func (c *CompositeError) Error() string { + if len(c.Errors) > 0 { + msgs := []string{c.message + ":"} + for _, e := range c.Errors { + msgs = append(msgs, e.Error()) + } + return strings.Join(msgs, "\n") + } + return c.message +} + +// MarshalJSON implements the JSON encoding interface +func (c CompositeError) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "code": c.code, + "message": c.message, + "errors": c.Errors, + }) +} + +// CompositeValidationError an error to wrap a bunch of other errors +func CompositeValidationError(errors ...error) *CompositeError { + return &CompositeError{ + code: CompositeErrorCode, + Errors: append([]error{}, errors...), + message: "validation failure list", + } +} + +// ValidateName recursively sets the name for all validations or updates them for nested properties +func (c *CompositeError) ValidateName(name string) *CompositeError { + for i, e := range c.Errors { + if ve, ok := e.(*Validation); ok { + c.Errors[i] = ve.ValidateName(name) + } else if ce, ok := e.(*CompositeError); ok { + c.Errors[i] = ce.ValidateName(name) + } + } + + return c +} + +// FailedAllPatternProperties an error for when the property doesn't match a pattern +func FailedAllPatternProperties(name, in, key string) *Validation { + msg := fmt.Sprintf(failedAllPatternProps, name, key, in) + if in == "" { + msg = fmt.Sprintf(failedAllPatternPropsNoIn, name, key) + } + return &Validation{ + code: FailedAllPatternPropsCode, + Name: name, + In: in, + Value: key, + message: msg, + } +} + +// PropertyNotAllowed an error for when the property doesn't match a pattern +func PropertyNotAllowed(name, in, key string) *Validation { + msg := fmt.Sprintf(unallowedProperty, name, key, in) + if in == "" { + msg = fmt.Sprintf(unallowedPropertyNoIn, name, key) + } + return &Validation{ + code: UnallowedPropertyCode, + Name: name, + In: in, + Value: key, + message: msg, + } +} + +// TooFewProperties an error for an object with too few properties +func TooFewProperties(name, in string, n int64) *Validation { + msg := fmt.Sprintf(tooFewProperties, name, in, n) + if in == "" { + msg = fmt.Sprintf(tooFewPropertiesNoIn, name, n) + } + return &Validation{ + code: TooFewPropertiesCode, + Name: name, + In: in, + Value: n, + message: msg, + } +} + +// TooManyProperties an error for an object with too many properties +func TooManyProperties(name, in string, n int64) *Validation { + msg := fmt.Sprintf(tooManyProperties, name, in, n) + if in == "" { + msg = fmt.Sprintf(tooManyPropertiesNoIn, name, n) + } + return &Validation{ + code: TooManyPropertiesCode, + Name: name, + In: in, + Value: n, + message: msg, + } +} + +// AdditionalItemsNotAllowed an error for invalid additional items +func AdditionalItemsNotAllowed(name, in string) *Validation { + msg := fmt.Sprintf(noAdditionalItems, name, in) + if in == "" { + msg = fmt.Sprintf(noAdditionalItemsNoIn, name) + } + return &Validation{ + code: NoAdditionalItemsCode, + Name: name, + In: in, + message: msg, + } +} + +// InvalidCollectionFormat another flavor of invalid type error +func InvalidCollectionFormat(name, in, format string) *Validation { + return &Validation{ + code: InvalidTypeCode, + Name: name, + In: in, + Value: format, + message: fmt.Sprintf("the collection format %q is not supported for the %s param %q", format, in, name), + } +} + +// InvalidTypeName an error for when the type is invalid +func InvalidTypeName(typeName string) *Validation { + return &Validation{ + code: InvalidTypeCode, + Value: typeName, + message: fmt.Sprintf(invalidType, typeName), + } +} + +// InvalidType creates an error for when the type is invalid +func InvalidType(name, in, typeName string, value interface{}) *Validation { + var message string + + if in != "" { + switch value.(type) { + case string: + message = fmt.Sprintf(typeFailWithData, name, in, typeName, value) + case error: + message = fmt.Sprintf(typeFailWithError, name, in, typeName, value) + default: + message = fmt.Sprintf(typeFail, name, in, typeName) + } + } else { + switch value.(type) { + case string: + message = fmt.Sprintf(typeFailWithDataNoIn, name, typeName, value) + case error: + message = fmt.Sprintf(typeFailWithErrorNoIn, name, typeName, value) + default: + message = fmt.Sprintf(typeFailNoIn, name, typeName) + } + } + + return &Validation{ + code: InvalidTypeCode, + Name: name, + In: in, + Value: value, + message: message, + } + +} + +// DuplicateItems error for when an array contains duplicates +func DuplicateItems(name, in string) *Validation { + msg := fmt.Sprintf(uniqueFail, name, in) + if in == "" { + msg = fmt.Sprintf(uniqueFailNoIn, name) + } + return &Validation{ + code: UniqueFailCode, + Name: name, + In: in, + message: msg, + } +} + +// TooManyItems error for when an array contains too many items +func TooManyItems(name, in string, max int64, value interface{}) *Validation { + msg := fmt.Sprintf(maxItemsFail, name, in, max) + if in == "" { + msg = fmt.Sprintf(maxItemsFailNoIn, name, max) + } + + return &Validation{ + code: MaxItemsFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// TooFewItems error for when an array contains too few items +func TooFewItems(name, in string, min int64, value interface{}) *Validation { + msg := fmt.Sprintf(minItemsFail, name, in, min) + if in == "" { + msg = fmt.Sprintf(minItemsFailNoIn, name, min) + } + return &Validation{ + code: MinItemsFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// ExceedsMaximumInt error for when maximum validation fails +func ExceedsMaximumInt(name, in string, max int64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := maxIncFailNoIn + if exclusive { + m = maxExcFailNoIn + } + message = fmt.Sprintf(m, name, max) + } else { + m := maxIncFail + if exclusive { + m = maxExcFail + } + message = fmt.Sprintf(m, name, in, max) + } + return &Validation{ + code: MaxFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// ExceedsMaximumUint error for when maximum validation fails +func ExceedsMaximumUint(name, in string, max uint64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := maxIncFailNoIn + if exclusive { + m = maxExcFailNoIn + } + message = fmt.Sprintf(m, name, max) + } else { + m := maxIncFail + if exclusive { + m = maxExcFail + } + message = fmt.Sprintf(m, name, in, max) + } + return &Validation{ + code: MaxFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// ExceedsMaximum error for when maximum validation fails +func ExceedsMaximum(name, in string, max float64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := maxIncFailNoIn + if exclusive { + m = maxExcFailNoIn + } + message = fmt.Sprintf(m, name, max) + } else { + m := maxIncFail + if exclusive { + m = maxExcFail + } + message = fmt.Sprintf(m, name, in, max) + } + return &Validation{ + code: MaxFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// ExceedsMinimumInt error for when minimum validation fails +func ExceedsMinimumInt(name, in string, min int64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := minIncFailNoIn + if exclusive { + m = minExcFailNoIn + } + message = fmt.Sprintf(m, name, min) + } else { + m := minIncFail + if exclusive { + m = minExcFail + } + message = fmt.Sprintf(m, name, in, min) + } + return &Validation{ + code: MinFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// ExceedsMinimumUint error for when minimum validation fails +func ExceedsMinimumUint(name, in string, min uint64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := minIncFailNoIn + if exclusive { + m = minExcFailNoIn + } + message = fmt.Sprintf(m, name, min) + } else { + m := minIncFail + if exclusive { + m = minExcFail + } + message = fmt.Sprintf(m, name, in, min) + } + return &Validation{ + code: MinFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// ExceedsMinimum error for when minimum validation fails +func ExceedsMinimum(name, in string, min float64, exclusive bool, value interface{}) *Validation { + var message string + if in == "" { + m := minIncFailNoIn + if exclusive { + m = minExcFailNoIn + } + message = fmt.Sprintf(m, name, min) + } else { + m := minIncFail + if exclusive { + m = minExcFail + } + message = fmt.Sprintf(m, name, in, min) + } + return &Validation{ + code: MinFailCode, + Name: name, + In: in, + Value: value, + message: message, + } +} + +// NotMultipleOf error for when multiple of validation fails +func NotMultipleOf(name, in string, multiple, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(multipleOfFailNoIn, name, multiple) + } else { + msg = fmt.Sprintf(multipleOfFail, name, in, multiple) + } + return &Validation{ + code: MultipleOfFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// EnumFail error for when an enum validation fails +func EnumFail(name, in string, value interface{}, values []interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(enumFailNoIn, name, values) + } else { + msg = fmt.Sprintf(enumFail, name, in, values) + } + + return &Validation{ + code: EnumFailCode, + Name: name, + In: in, + Value: value, + Values: values, + message: msg, + } +} + +// Required error for when a value is missing +func Required(name, in string, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(requiredFailNoIn, name) + } else { + msg = fmt.Sprintf(requiredFail, name, in) + } + return &Validation{ + code: RequiredFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// ReadOnly error for when a value is present in request +func ReadOnly(name, in string, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(readOnlyFailNoIn, name) + } else { + msg = fmt.Sprintf(readOnlyFail, name, in) + } + return &Validation{ + code: ReadOnlyFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// TooLong error for when a string is too long +func TooLong(name, in string, max int64, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(tooLongMessageNoIn, name, max) + } else { + msg = fmt.Sprintf(tooLongMessage, name, in, max) + } + return &Validation{ + code: TooLongFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// TooShort error for when a string is too short +func TooShort(name, in string, min int64, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(tooShortMessageNoIn, name, min) + } else { + msg = fmt.Sprintf(tooShortMessage, name, in, min) + } + + return &Validation{ + code: TooShortFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// FailedPattern error for when a string fails a regex pattern match +// the pattern that is returned is the ECMA syntax version of the pattern not the golang version. +func FailedPattern(name, in, pattern string, value interface{}) *Validation { + var msg string + if in == "" { + msg = fmt.Sprintf(patternFailNoIn, name, pattern) + } else { + msg = fmt.Sprintf(patternFail, name, in, pattern) + } + + return &Validation{ + code: PatternFailCode, + Name: name, + In: in, + Value: value, + message: msg, + } +} + +// MultipleOfMustBePositive error for when a +// multipleOf factor is negative +func MultipleOfMustBePositive(name, in string, factor interface{}) *Validation { + return &Validation{ + code: MultipleOfMustBePositiveCode, + Name: name, + In: in, + Value: factor, + message: fmt.Sprintf(multipleOfMustBePositive, name, factor), + } +} diff --git a/vendor/github.com/go-openapi/jsonpointer/.editorconfig b/vendor/github.com/go-openapi/jsonpointer/.editorconfig new file mode 100644 index 00000000..3152da69 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/jsonpointer/.gitignore b/vendor/github.com/go-openapi/jsonpointer/.gitignore new file mode 100644 index 00000000..769c2440 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.gitignore @@ -0,0 +1 @@ +secrets.yml diff --git a/vendor/github.com/go-openapi/jsonpointer/.travis.yml b/vendor/github.com/go-openapi/jsonpointer/.travis.yml new file mode 100644 index 00000000..03a22fe0 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.14.x +- 1.15.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/jsonpointer/LICENSE b/vendor/github.com/go-openapi/jsonpointer/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md new file mode 100644 index 00000000..813788af --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -0,0 +1,15 @@ +# gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer) +An implementation of JSON Pointer - Go language + +## Status +Completed YES + +Tested YES + +## References +http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 + +### Note +The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented. diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go new file mode 100644 index 00000000..7df9853d --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -0,0 +1,390 @@ +// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// author sigu-399 +// author-github https://github.com/sigu-399 +// author-mail sigu.399@gmail.com +// +// repository-name jsonpointer +// repository-desc An implementation of JSON Pointer - Go language +// +// description Main and unique file. +// +// created 25-02-2013 + +package jsonpointer + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-openapi/swag" +) + +const ( + emptyPointer = `` + pointerSeparator = `/` + + invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator +) + +var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() +var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() + +// JSONPointable is an interface for structs to implement when they need to customize the +// json pointer process +type JSONPointable interface { + JSONLookup(string) (interface{}, error) +} + +// JSONSetable is an interface for structs to implement when they need to customize the +// json pointer process +type JSONSetable interface { + JSONSet(string, interface{}) error +} + +// New creates a new json pointer for the given string +func New(jsonPointerString string) (Pointer, error) { + + var p Pointer + err := p.parse(jsonPointerString) + return p, err + +} + +// Pointer the json pointer reprsentation +type Pointer struct { + referenceTokens []string +} + +// "Constructor", parses the given string JSON pointer +func (p *Pointer) parse(jsonPointerString string) error { + + var err error + + if jsonPointerString != emptyPointer { + if !strings.HasPrefix(jsonPointerString, pointerSeparator) { + err = errors.New(invalidStart) + } else { + referenceTokens := strings.Split(jsonPointerString, pointerSeparator) + for _, referenceToken := range referenceTokens[1:] { + p.referenceTokens = append(p.referenceTokens, referenceToken) + } + } + } + + return err +} + +// Get uses the pointer to retrieve a value from a JSON document +func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) { + return p.get(document, swag.DefaultJSONNameProvider) +} + +// Set uses the pointer to set a value from a JSON document +func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) { + return document, p.set(document, value, swag.DefaultJSONNameProvider) +} + +// GetForToken gets a value for a json pointer token 1 level deep +func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) { + return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) +} + +// SetForToken gets a value for a json pointer token 1 level deep +func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) { + return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) +} + +func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + if rValue.Type().Implements(jsonPointableType) { + r, err := node.(JSONPointable).JSONLookup(decodedToken) + if err != nil { + return nil, kind, err + } + return r, kind, nil + } + + switch kind { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return nil, kind, fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + return fld.Interface(), kind, nil + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) + + if mv.IsValid() { + return mv.Interface(), kind, nil + } + return nil, kind, fmt.Errorf("object has no key %q", decodedToken) + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return nil, kind, err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + return elem.Interface(), kind, nil + + default: + return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken) + } + +} + +func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error { + rValue := reflect.Indirect(reflect.ValueOf(node)) + + if ns, ok := node.(JSONSetable); ok { // pointer impl + return ns.JSONSet(decodedToken, data) + } + + if rValue.Type().Implements(jsonSetableType) { + return node.(JSONSetable).JSONSet(decodedToken, data) + } + + switch rValue.Kind() { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + if fld.IsValid() { + fld.Set(reflect.ValueOf(data)) + } + return nil + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + rValue.SetMapIndex(kv, reflect.ValueOf(data)) + return nil + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + if !elem.CanSet() { + return fmt.Errorf("can't set slice index %s to %v", decodedToken, data) + } + elem.Set(reflect.ValueOf(data)) + return nil + + default: + return fmt.Errorf("invalid token reference %q", decodedToken) + } + +} + +func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { + + if nameProvider == nil { + nameProvider = swag.DefaultJSONNameProvider + } + + kind := reflect.Invalid + + // Full document when empty + if len(p.referenceTokens) == 0 { + return node, kind, nil + } + + for _, token := range p.referenceTokens { + + decodedToken := Unescape(token) + + r, knd, err := getSingleImpl(node, decodedToken, nameProvider) + if err != nil { + return nil, knd, err + } + node, kind = r, knd + + } + + rValue := reflect.ValueOf(node) + kind = rValue.Kind() + + return node, kind, nil +} + +func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error { + knd := reflect.ValueOf(node).Kind() + + if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { + return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values") + } + + if nameProvider == nil { + nameProvider = swag.DefaultJSONNameProvider + } + + // Full document when empty + if len(p.referenceTokens) == 0 { + return nil + } + + lastI := len(p.referenceTokens) - 1 + for i, token := range p.referenceTokens { + isLastToken := i == lastI + decodedToken := Unescape(token) + + if isLastToken { + + return setSingleImpl(node, data, decodedToken, nameProvider) + } + + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + if rValue.Type().Implements(jsonPointableType) { + r, err := node.(JSONPointable).JSONLookup(decodedToken) + if err != nil { + return err + } + fld := reflect.ValueOf(r) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { + node = fld.Addr().Interface() + continue + } + node = r + continue + } + + switch kind { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { + node = fld.Addr().Interface() + continue + } + node = fld.Interface() + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) + + if !mv.IsValid() { + return fmt.Errorf("object has no key %q", decodedToken) + } + if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr { + node = mv.Addr().Interface() + continue + } + node = mv.Interface() + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr { + node = elem.Addr().Interface() + continue + } + node = elem.Interface() + + default: + return fmt.Errorf("invalid token reference %q", decodedToken) + } + + } + + return nil +} + +// DecodedTokens returns the decoded tokens +func (p *Pointer) DecodedTokens() []string { + result := make([]string, 0, len(p.referenceTokens)) + for _, t := range p.referenceTokens { + result = append(result, Unescape(t)) + } + return result +} + +// IsEmpty returns true if this is an empty json pointer +// this indicates that it points to the root document +func (p *Pointer) IsEmpty() bool { + return len(p.referenceTokens) == 0 +} + +// Pointer to string representation function +func (p *Pointer) String() string { + + if len(p.referenceTokens) == 0 { + return emptyPointer + } + + pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) + + return pointerString +} + +// Specific JSON pointer encoding here +// ~0 => ~ +// ~1 => / +// ... and vice versa + +const ( + encRefTok0 = `~0` + encRefTok1 = `~1` + decRefTok0 = `~` + decRefTok1 = `/` +) + +// Unescape unescapes a json pointer reference token string to the original representation +func Unescape(token string) string { + step1 := strings.Replace(token, encRefTok1, decRefTok1, -1) + step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1) + return step2 +} + +// Escape escapes a pointer reference token string +func Escape(token string) string { + step1 := strings.Replace(token, decRefTok0, encRefTok0, -1) + step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1) + return step2 +} diff --git a/vendor/github.com/go-openapi/jsonreference/.gitignore b/vendor/github.com/go-openapi/jsonreference/.gitignore new file mode 100644 index 00000000..769c2440 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.gitignore @@ -0,0 +1 @@ +secrets.yml diff --git a/vendor/github.com/go-openapi/jsonreference/.golangci.yml b/vendor/github.com/go-openapi/jsonreference/.golangci.yml new file mode 100644 index 00000000..f9381aee --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.golangci.yml @@ -0,0 +1,41 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoglobals + - godox + - gocognit + - whitespace + - wsl + - funlen + - gochecknoglobals + - gochecknoinits + - scopelint + - wrapcheck + - exhaustivestruct + - exhaustive + - nlreturn + - testpackage + - gci + - gofumpt + - goerr113 + - gomnd + - tparallel + - nestif + - godot + - errorlint diff --git a/vendor/github.com/go-openapi/jsonreference/.travis.yml b/vendor/github.com/go-openapi/jsonreference/.travis.yml new file mode 100644 index 00000000..05482f4b --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.travis.yml @@ -0,0 +1,24 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.14.x +- 1.x +install: +- go get gotest.tools/gotestsum +jobs: + include: + # include linting job, but only for latest go version and amd64 arch + - go: 1.x + arch: amd64 + install: + go get github.com/golangci/golangci-lint/cmd/golangci-lint + script: + - golangci-lint run --new-from-rev master +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/jsonreference/LICENSE b/vendor/github.com/go-openapi/jsonreference/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/jsonreference/README.md b/vendor/github.com/go-openapi/jsonreference/README.md new file mode 100644 index 00000000..b94753aa --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/README.md @@ -0,0 +1,15 @@ +# gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference) +An implementation of JSON Reference - Go language + +## Status +Feature complete. Stable API + +## Dependencies +https://github.com/go-openapi/jsonpointer + +## References +http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 + +http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 diff --git a/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go new file mode 100644 index 00000000..8956c308 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go @@ -0,0 +1,63 @@ +package internal + +import ( + "net/url" + "regexp" + "strings" +) + +const ( + defaultHttpPort = ":80" + defaultHttpsPort = ":443" +) + +// Regular expressions used by the normalizations +var rxPort = regexp.MustCompile(`(:\d+)/?$`) +var rxDupSlashes = regexp.MustCompile(`/{2,}`) + +// NormalizeURL will normalize the specified URL +// This was added to replace a previous call to the no longer maintained purell library: +// The call that was used looked like the following: +// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) +// +// To explain all that was included in the call above, purell.FlagsSafe was really just the following: +// - FlagLowercaseScheme +// - FlagLowercaseHost +// - FlagRemoveDefaultPort +// - FlagRemoveDuplicateSlashes (and this was mixed in with the |) +func NormalizeURL(u *url.URL) { + lowercaseScheme(u) + lowercaseHost(u) + removeDefaultPort(u) + removeDuplicateSlashes(u) +} + +func lowercaseScheme(u *url.URL) { + if len(u.Scheme) > 0 { + u.Scheme = strings.ToLower(u.Scheme) + } +} + +func lowercaseHost(u *url.URL) { + if len(u.Host) > 0 { + u.Host = strings.ToLower(u.Host) + } +} + +func removeDefaultPort(u *url.URL) { + if len(u.Host) > 0 { + scheme := strings.ToLower(u.Scheme) + u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { + if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) { + return "" + } + return val + }) + } +} + +func removeDuplicateSlashes(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/") + } +} diff --git a/vendor/github.com/go-openapi/jsonreference/reference.go b/vendor/github.com/go-openapi/jsonreference/reference.go new file mode 100644 index 00000000..cfdef03e --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/reference.go @@ -0,0 +1,158 @@ +// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// author sigu-399 +// author-github https://github.com/sigu-399 +// author-mail sigu.399@gmail.com +// +// repository-name jsonreference +// repository-desc An implementation of JSON Reference - Go language +// +// description Main and unique file. +// +// created 26-02-2013 + +package jsonreference + +import ( + "errors" + "net/url" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/jsonreference/internal" +) + +const ( + fragmentRune = `#` +) + +// New creates a new reference for the given string +func New(jsonReferenceString string) (Ref, error) { + + var r Ref + err := r.parse(jsonReferenceString) + return r, err + +} + +// MustCreateRef parses the ref string and panics when it's invalid. +// Use the New method for a version that returns an error +func MustCreateRef(ref string) Ref { + r, err := New(ref) + if err != nil { + panic(err) + } + return r +} + +// Ref represents a json reference object +type Ref struct { + referenceURL *url.URL + referencePointer jsonpointer.Pointer + + HasFullURL bool + HasURLPathOnly bool + HasFragmentOnly bool + HasFileScheme bool + HasFullFilePath bool +} + +// GetURL gets the URL for this reference +func (r *Ref) GetURL() *url.URL { + return r.referenceURL +} + +// GetPointer gets the json pointer for this reference +func (r *Ref) GetPointer() *jsonpointer.Pointer { + return &r.referencePointer +} + +// String returns the best version of the url for this reference +func (r *Ref) String() string { + + if r.referenceURL != nil { + return r.referenceURL.String() + } + + if r.HasFragmentOnly { + return fragmentRune + r.referencePointer.String() + } + + return r.referencePointer.String() +} + +// IsRoot returns true if this reference is a root document +func (r *Ref) IsRoot() bool { + return r.referenceURL != nil && + !r.IsCanonical() && + !r.HasURLPathOnly && + r.referenceURL.Fragment == "" +} + +// IsCanonical returns true when this pointer starts with http(s):// or file:// +func (r *Ref) IsCanonical() bool { + return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL) +} + +// "Constructor", parses the given string JSON reference +func (r *Ref) parse(jsonReferenceString string) error { + + parsed, err := url.Parse(jsonReferenceString) + if err != nil { + return err + } + + internal.NormalizeURL(parsed) + + r.referenceURL = parsed + refURL := r.referenceURL + + if refURL.Scheme != "" && refURL.Host != "" { + r.HasFullURL = true + } else { + if refURL.Path != "" { + r.HasURLPathOnly = true + } else if refURL.RawQuery == "" && refURL.Fragment != "" { + r.HasFragmentOnly = true + } + } + + r.HasFileScheme = refURL.Scheme == "file" + r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/") + + // invalid json-pointer error means url has no json-pointer fragment. simply ignore error + r.referencePointer, _ = jsonpointer.New(refURL.Fragment) + + return nil +} + +// Inherits creates a new reference from a parent and a child +// If the child cannot inherit from the parent, an error is returned +func (r *Ref) Inherits(child Ref) (*Ref, error) { + childURL := child.GetURL() + parentURL := r.GetURL() + if childURL == nil { + return nil, errors.New("child url is nil") + } + if parentURL == nil { + return &child, nil + } + + ref, err := New(parentURL.ResolveReference(childURL).String()) + if err != nil { + return nil, err + } + return &ref, nil +} diff --git a/vendor/github.com/go-openapi/loads/.editorconfig b/vendor/github.com/go-openapi/loads/.editorconfig new file mode 100644 index 00000000..3152da69 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/loads/.gitignore b/vendor/github.com/go-openapi/loads/.gitignore new file mode 100644 index 00000000..e4f15f17 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.gitignore @@ -0,0 +1,4 @@ +secrets.yml +coverage.out +profile.cov +profile.out diff --git a/vendor/github.com/go-openapi/loads/.golangci.yml b/vendor/github.com/go-openapi/loads/.golangci.yml new file mode 100644 index 00000000..d48b4a51 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.golangci.yml @@ -0,0 +1,44 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoglobals + - gochecknoinits + - godox + - gocognit + - whitespace + - wsl + - funlen + - gochecknoglobals + - gochecknoinits + - scopelint + - wrapcheck + - exhaustivestruct + - exhaustive + - nlreturn + - testpackage + - gci + - gofumpt + - goerr113 + - gomnd + - tparallel + - nestif + - godot + - errorlint + - paralleltest diff --git a/vendor/github.com/go-openapi/loads/.travis.yml b/vendor/github.com/go-openapi/loads/.travis.yml new file mode 100644 index 00000000..cd4a7c33 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.travis.yml @@ -0,0 +1,25 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.16.x +- 1.x +install: +- go get gotest.tools/gotestsum +language: go +arch: +- amd64 +- ppc64le +jobs: + include: + # include linting job, but only for latest go version and amd64 arch + - go: 1.x + arch: amd64 + install: + go get github.com/golangci/golangci-lint/cmd/golangci-lint + script: + - golangci-lint run --new-from-rev master +notifications: + slack: + secure: OxkPwVp35qBTUilgWC8xykSj+sGMcj0h8IIOKD+Rflx2schZVlFfdYdyVBM+s9OqeOfvtuvnR9v1Ye2rPKAvcjWdC4LpRGUsgmItZaI6Um8Aj6+K9udCw5qrtZVfOVmRu8LieH//XznWWKdOultUuniW0MLqw5+II87Gd00RWbCGi0hk0PykHe7uK+PDA2BEbqyZ2WKKYCvfB3j+0nrFOHScXqnh0V05l2E83J4+Sgy1fsPy+1WdX58ZlNBG333ibaC1FS79XvKSmTgKRkx3+YBo97u6ZtUmJa5WZjf2OdLG3KIckGWAv6R5xgxeU31N0Ng8L332w/Edpp2O/M2bZwdnKJ8hJQikXIAQbICbr+lTDzsoNzMdEIYcHpJ5hjPbiUl3Bmd+Jnsjf5McgAZDiWIfpCKZ29tPCEkVwRsOCqkyPRMNMzHHmoja495P5jR+ODS7+J8RFg5xgcnOgpP9D4Wlhztlf5WyZMpkLxTUD+bZq2SRf50HfHFXTkfq22zPl3d1eq0yrLwh/Z/fWKkfb6SyysROL8y6s8u3dpFX1YHSg0BR6i913h4aoZw9B2BG27cafLLTwKYsp2dFo1PWl4O6u9giFJIeqwloZHLKKrwh0cBFhB7RH0I58asxkZpCH6uWjJierahmHe7iS+E6i+9oCHkOZ59hmCYNimIs3hM= +script: +- gotestsum -f short-verbose -- -race -timeout=20m -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/loads/LICENSE b/vendor/github.com/go-openapi/loads/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/loads/README.md b/vendor/github.com/go-openapi/loads/README.md new file mode 100644 index 00000000..df1f6264 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/README.md @@ -0,0 +1,6 @@ +# Loads OAI specs [![Build Status](https://travis-ci.org/go-openapi/loads.svg?branch=master)](https://travis-ci.org/go-openapi/loads) [![codecov](https://codecov.io/gh/go-openapi/loads/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/loads) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![Actions/Go Test Status](https://github.com/go-openapi/loads/workflows/Go%20Test/badge.svg)](https://github.com/go-openapi/loads/actions?query=workflow%3A"Go+Test") + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/loads?status.svg)](http://godoc.org/github.com/go-openapi/loads) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/loads)](https://goreportcard.com/report/github.com/go-openapi/loads) + +Loading of OAI specification documents from local or remote locations. Supports JSON and YAML documents. diff --git a/vendor/github.com/go-openapi/loads/doc.go b/vendor/github.com/go-openapi/loads/doc.go new file mode 100644 index 00000000..3046da4c --- /dev/null +++ b/vendor/github.com/go-openapi/loads/doc.go @@ -0,0 +1,21 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +Package loads provides document loading methods for swagger (OAI) specifications. + +It is used by other go-openapi packages to load and run analysis on local or remote spec documents. + +*/ +package loads diff --git a/vendor/github.com/go-openapi/loads/loaders.go b/vendor/github.com/go-openapi/loads/loaders.go new file mode 100644 index 00000000..44bd32b5 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/loaders.go @@ -0,0 +1,134 @@ +package loads + +import ( + "encoding/json" + "errors" + "net/url" + + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +var ( + // Default chain of loaders, defined at the package level. + // + // By default this matches json and yaml documents. + // + // May be altered with AddLoader(). + loaders *loader +) + +func init() { + jsonLoader := &loader{ + DocLoaderWithMatch: DocLoaderWithMatch{ + Match: func(pth string) bool { + return true + }, + Fn: JSONDoc, + }, + } + + loaders = jsonLoader.WithHead(&loader{ + DocLoaderWithMatch: DocLoaderWithMatch{ + Match: swag.YAMLMatcher, + Fn: swag.YAMLDoc, + }, + }) + + // sets the global default loader for go-openapi/spec + spec.PathLoader = loaders.Load +} + +// DocLoader represents a doc loader type +type DocLoader func(string) (json.RawMessage, error) + +// DocMatcher represents a predicate to check if a loader matches +type DocMatcher func(string) bool + +// DocLoaderWithMatch describes a loading function for a given extension match. +type DocLoaderWithMatch struct { + Fn DocLoader + Match DocMatcher +} + +// NewDocLoaderWithMatch builds a DocLoaderWithMatch to be used in load options +func NewDocLoaderWithMatch(fn DocLoader, matcher DocMatcher) DocLoaderWithMatch { + return DocLoaderWithMatch{ + Fn: fn, + Match: matcher, + } +} + +type loader struct { + DocLoaderWithMatch + Next *loader +} + +// WithHead adds a loader at the head of the current stack +func (l *loader) WithHead(head *loader) *loader { + if head == nil { + return l + } + head.Next = l + return head +} + +// WithNext adds a loader at the trail of the current stack +func (l *loader) WithNext(next *loader) *loader { + l.Next = next + return next +} + +// Load the raw document from path +func (l *loader) Load(path string) (json.RawMessage, error) { + _, erp := url.Parse(path) + if erp != nil { + return nil, erp + } + + var lastErr error = errors.New("no loader matched") // default error if no match was found + for ldr := l; ldr != nil; ldr = ldr.Next { + if ldr.Match != nil && !ldr.Match(path) { + continue + } + + // try then move to next one if there is an error + b, err := ldr.Fn(path) + if err == nil { + return b, nil + } + + lastErr = err + } + + return nil, lastErr +} + +// JSONDoc loads a json document from either a file or a remote url +func JSONDoc(path string) (json.RawMessage, error) { + data, err := swag.LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil +} + +// AddLoader for a document, executed before other previously set loaders. +// +// This sets the configuration at the package level. +// +// NOTE: +// * this updates the default loader used by github.com/go-openapi/spec +// * since this sets package level globals, you shouln't call this concurrently +// +func AddLoader(predicate DocMatcher, load DocLoader) { + loaders = loaders.WithHead(&loader{ + DocLoaderWithMatch: DocLoaderWithMatch{ + Match: predicate, + Fn: load, + }, + }) + + // sets the global default loader for go-openapi/spec + spec.PathLoader = loaders.Load +} diff --git a/vendor/github.com/go-openapi/loads/options.go b/vendor/github.com/go-openapi/loads/options.go new file mode 100644 index 00000000..f8305d56 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/options.go @@ -0,0 +1,61 @@ +package loads + +type options struct { + loader *loader +} + +func defaultOptions() *options { + return &options{ + loader: loaders, + } +} + +func loaderFromOptions(options []LoaderOption) *loader { + opts := defaultOptions() + for _, apply := range options { + apply(opts) + } + + return opts.loader +} + +// LoaderOption allows to fine-tune the spec loader behavior +type LoaderOption func(*options) + +// WithDocLoader sets a custom loader for loading specs +func WithDocLoader(l DocLoader) LoaderOption { + return func(opt *options) { + if l == nil { + return + } + opt.loader = &loader{ + DocLoaderWithMatch: DocLoaderWithMatch{ + Fn: l, + }, + } + } +} + +// WithDocLoaderMatches sets a chain of custom loaders for loading specs +// for different extension matches. +// +// Loaders are executed in the order of provided DocLoaderWithMatch'es. +func WithDocLoaderMatches(l ...DocLoaderWithMatch) LoaderOption { + return func(opt *options) { + var final, prev *loader + for _, ldr := range l { + if ldr.Fn == nil { + continue + } + + if prev == nil { + final = &loader{DocLoaderWithMatch: ldr} + prev = final + continue + } + + prev = prev.WithNext(&loader{DocLoaderWithMatch: ldr}) + } + opt.loader = final + } +} diff --git a/vendor/github.com/go-openapi/loads/spec.go b/vendor/github.com/go-openapi/loads/spec.go new file mode 100644 index 00000000..93c8d4b8 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/spec.go @@ -0,0 +1,266 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package loads + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "fmt" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +func init() { + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}{}) +} + +// Document represents a swagger spec document +type Document struct { + // specAnalyzer + Analyzer *analysis.Spec + spec *spec.Swagger + specFilePath string + origSpec *spec.Swagger + schema *spec.Schema + raw json.RawMessage + pathLoader *loader +} + +// JSONSpec loads a spec from a json document +func JSONSpec(path string, options ...LoaderOption) (*Document, error) { + data, err := JSONDoc(path) + if err != nil { + return nil, err + } + // convert to json + return Analyzed(data, "", options...) +} + +// Embedded returns a Document based on embedded specs. No analysis is required +func Embedded(orig, flat json.RawMessage, options ...LoaderOption) (*Document, error) { + var origSpec, flatSpec spec.Swagger + if err := json.Unmarshal(orig, &origSpec); err != nil { + return nil, err + } + if err := json.Unmarshal(flat, &flatSpec); err != nil { + return nil, err + } + return &Document{ + raw: orig, + origSpec: &origSpec, + spec: &flatSpec, + pathLoader: loaderFromOptions(options), + }, nil +} + +// Spec loads a new spec document from a local or remote path +func Spec(path string, options ...LoaderOption) (*Document, error) { + + ldr := loaderFromOptions(options) + + b, err := ldr.Load(path) + if err != nil { + return nil, err + } + + document, err := Analyzed(b, "", options...) + if err != nil { + return nil, err + } + + if document != nil { + document.specFilePath = path + document.pathLoader = ldr + } + + return document, err +} + +// Analyzed creates a new analyzed spec document for a root json.RawMessage. +func Analyzed(data json.RawMessage, version string, options ...LoaderOption) (*Document, error) { + if version == "" { + version = "2.0" + } + if version != "2.0" { + return nil, fmt.Errorf("spec version %q is not supported", version) + } + + raw, err := trimData(data) // trim blanks, then convert yaml docs into json + if err != nil { + return nil, err + } + + swspec := new(spec.Swagger) + if err = json.Unmarshal(raw, swspec); err != nil { + return nil, err + } + + origsqspec, err := cloneSpec(swspec) + if err != nil { + return nil, err + } + + d := &Document{ + Analyzer: analysis.New(swspec), + schema: spec.MustLoadSwagger20Schema(), + spec: swspec, + raw: raw, + origSpec: origsqspec, + pathLoader: loaderFromOptions(options), + } + + return d, nil +} + +func trimData(in json.RawMessage) (json.RawMessage, error) { + trimmed := bytes.TrimSpace(in) + if len(trimmed) == 0 { + return in, nil + } + + if trimmed[0] == '{' || trimmed[0] == '[' { + return trimmed, nil + } + + // assume yaml doc: convert it to json + yml, err := swag.BytesToYAMLDoc(trimmed) + if err != nil { + return nil, fmt.Errorf("analyzed: %v", err) + } + + d, err := swag.YAMLToJSON(yml) + if err != nil { + return nil, fmt.Errorf("analyzed: %v", err) + } + + return d, nil +} + +// Expanded expands the ref fields in the spec document and returns a new spec document +func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { + + swspec := new(spec.Swagger) + if err := json.Unmarshal(d.raw, swspec); err != nil { + return nil, err + } + + var expandOptions *spec.ExpandOptions + if len(options) > 0 { + expandOptions = options[0] + } else { + expandOptions = &spec.ExpandOptions{ + RelativeBase: d.specFilePath, + } + } + + if expandOptions.PathLoader == nil { + if d.pathLoader != nil { + // use loader from Document options + expandOptions.PathLoader = d.pathLoader.Load + } else { + // use package level loader + expandOptions.PathLoader = loaders.Load + } + } + + if err := spec.ExpandSpec(swspec, expandOptions); err != nil { + return nil, err + } + + dd := &Document{ + Analyzer: analysis.New(swspec), + spec: swspec, + specFilePath: d.specFilePath, + schema: spec.MustLoadSwagger20Schema(), + raw: d.raw, + origSpec: d.origSpec, + } + return dd, nil +} + +// BasePath the base path for this spec +func (d *Document) BasePath() string { + return d.spec.BasePath +} + +// Version returns the version of this spec +func (d *Document) Version() string { + return d.spec.Swagger +} + +// Schema returns the swagger 2.0 schema +func (d *Document) Schema() *spec.Schema { + return d.schema +} + +// Spec returns the swagger spec object model +func (d *Document) Spec() *spec.Swagger { + return d.spec +} + +// Host returns the host for the API +func (d *Document) Host() string { + return d.spec.Host +} + +// Raw returns the raw swagger spec as json bytes +func (d *Document) Raw() json.RawMessage { + return d.raw +} + +// OrigSpec yields the original spec +func (d *Document) OrigSpec() *spec.Swagger { + return d.origSpec +} + +// ResetDefinitions gives a shallow copy with the models reset to the original spec +func (d *Document) ResetDefinitions() *Document { + defs := make(map[string]spec.Schema, len(d.origSpec.Definitions)) + for k, v := range d.origSpec.Definitions { + defs[k] = v + } + + d.spec.Definitions = defs + return d +} + +// Pristine creates a new pristine document instance based on the input data +func (d *Document) Pristine() *Document { + dd, _ := Analyzed(d.Raw(), d.Version()) + dd.pathLoader = d.pathLoader + return dd +} + +// SpecFilePath returns the file path of the spec if one is defined +func (d *Document) SpecFilePath() string { + return d.specFilePath +} + +func cloneSpec(src *spec.Swagger) (*spec.Swagger, error) { + var b bytes.Buffer + if err := gob.NewEncoder(&b).Encode(src); err != nil { + return nil, err + } + + var dst spec.Swagger + if err := gob.NewDecoder(&b).Decode(&dst); err != nil { + return nil, err + } + return &dst, nil +} diff --git a/vendor/github.com/go-openapi/runtime/.editorconfig b/vendor/github.com/go-openapi/runtime/.editorconfig new file mode 100644 index 00000000..3152da69 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/runtime/.gitattributes b/vendor/github.com/go-openapi/runtime/.gitattributes new file mode 100644 index 00000000..d207b180 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/vendor/github.com/go-openapi/runtime/.gitignore b/vendor/github.com/go-openapi/runtime/.gitignore new file mode 100644 index 00000000..fea8b84e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +*.cov +*.out +playground diff --git a/vendor/github.com/go-openapi/runtime/.golangci.yml b/vendor/github.com/go-openapi/runtime/.golangci.yml new file mode 100644 index 00000000..b1aa7928 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.golangci.yml @@ -0,0 +1,44 @@ +linters-settings: + govet: + # Using err repeatedly considered as shadowing. + check-shadowing: false + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 +linters: + disable: + - maligned + - lll + - gochecknoglobals + - godox + - gocognit + - whitespace + - wsl + - funlen + - gochecknoglobals + - gochecknoinits + - scopelint + - wrapcheck + - exhaustivestruct + - exhaustive + - nlreturn + - testpackage + - gci + - gofumpt + - goerr113 + - gomnd + - tparallel + - nestif + - godot + - errorlint + - noctx + - interfacer + - nilerr diff --git a/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/runtime/LICENSE b/vendor/github.com/go-openapi/runtime/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/runtime/README.md b/vendor/github.com/go-openapi/runtime/README.md new file mode 100644 index 00000000..5b1ec649 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/README.md @@ -0,0 +1,7 @@ +# runtime [![Build Status](https://travis-ci.org/go-openapi/runtime.svg?branch=client-context)](https://travis-ci.org/go-openapi/runtime) [![codecov](https://codecov.io/gh/go-openapi/runtime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/runtime) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/runtime/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/runtime?status.svg)](http://godoc.org/github.com/go-openapi/runtime) + +# golang Open-API toolkit - runtime + +The runtime component for use in codegeneration or as untyped usage. diff --git a/vendor/github.com/go-openapi/runtime/bytestream.go b/vendor/github.com/go-openapi/runtime/bytestream.go new file mode 100644 index 00000000..6eb6ceb5 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/bytestream.go @@ -0,0 +1,169 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "bytes" + "encoding" + "errors" + "fmt" + "io" + "reflect" + + "github.com/go-openapi/swag" +) + +func defaultCloser() error { return nil } + +type byteStreamOpt func(opts *byteStreamOpts) + +// ClosesStream when the bytestream consumer or producer is finished +func ClosesStream(opts *byteStreamOpts) { + opts.Close = true +} + +type byteStreamOpts struct { + Close bool +} + +// ByteStreamConsumer creates a consumer for byte streams, +// takes a Writer/BinaryUnmarshaler interface or binary slice by reference, +// and reads from the provided reader +func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { + var vals byteStreamOpts + for _, opt := range opts { + opt(&vals) + } + + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("ByteStreamConsumer requires a reader") // early exit + } + + close := defaultCloser + if vals.Close { + if cl, ok := reader.(io.Closer); ok { + close = cl.Close + } + } + //nolint:errcheck // closing a reader wouldn't fail. + defer close() + + if wrtr, ok := data.(io.Writer); ok { + _, err := io.Copy(wrtr, reader) + return err + } + + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(reader) + if err != nil { + return err + } + b := buf.Bytes() + + if bu, ok := data.(encoding.BinaryUnmarshaler); ok { + return bu.UnmarshalBinary(b) + } + + if data != nil { + if str, ok := data.(*string); ok { + *str = string(b) + return nil + } + } + + if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr { + v := reflect.Indirect(reflect.ValueOf(data)) + if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + v.SetBytes(b) + return nil + } + } + + return fmt.Errorf("%v (%T) is not supported by the ByteStreamConsumer, %s", + data, data, "can be resolved by supporting Writer/BinaryUnmarshaler interface") + }) +} + +// ByteStreamProducer creates a producer for byte streams, +// takes a Reader/BinaryMarshaler interface or binary slice, +// and writes to a writer (essentially a pipe) +func ByteStreamProducer(opts ...byteStreamOpt) Producer { + var vals byteStreamOpts + for _, opt := range opts { + opt(&vals) + } + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("ByteStreamProducer requires a writer") // early exit + } + close := defaultCloser + if vals.Close { + if cl, ok := writer.(io.Closer); ok { + close = cl.Close + } + } + //nolint:errcheck // TODO: closing a writer would fail. + defer close() + + if rc, ok := data.(io.ReadCloser); ok { + defer rc.Close() + } + + if rdr, ok := data.(io.Reader); ok { + _, err := io.Copy(writer, rdr) + return err + } + + if bm, ok := data.(encoding.BinaryMarshaler); ok { + bytes, err := bm.MarshalBinary() + if err != nil { + return err + } + + _, err = writer.Write(bytes) + return err + } + + if data != nil { + if str, ok := data.(string); ok { + _, err := writer.Write([]byte(str)) + return err + } + + if e, ok := data.(error); ok { + _, err := writer.Write([]byte(e.Error())) + return err + } + + v := reflect.Indirect(reflect.ValueOf(data)) + if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + _, err := writer.Write(v.Bytes()) + return err + } + if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { + b, err := swag.WriteJSON(data) + if err != nil { + return err + } + _, err = writer.Write(b) + return err + } + } + + return fmt.Errorf("%v (%T) is not supported by the ByteStreamProducer, %s", + data, data, "can be resolved by supporting Reader/BinaryMarshaler interface") + }) +} diff --git a/vendor/github.com/go-openapi/runtime/client_auth_info.go b/vendor/github.com/go-openapi/runtime/client_auth_info.go new file mode 100644 index 00000000..c6c97d9a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_auth_info.go @@ -0,0 +1,30 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import "github.com/go-openapi/strfmt" + +// A ClientAuthInfoWriterFunc converts a function to a request writer interface +type ClientAuthInfoWriterFunc func(ClientRequest, strfmt.Registry) error + +// AuthenticateRequest adds authentication data to the request +func (fn ClientAuthInfoWriterFunc) AuthenticateRequest(req ClientRequest, reg strfmt.Registry) error { + return fn(req, reg) +} + +// A ClientAuthInfoWriter implementor knows how to write authentication info to a request +type ClientAuthInfoWriter interface { + AuthenticateRequest(ClientRequest, strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/client_operation.go b/vendor/github.com/go-openapi/runtime/client_operation.go new file mode 100644 index 00000000..fa21eacf --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_operation.go @@ -0,0 +1,41 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "context" + "net/http" +) + +// ClientOperation represents the context for a swagger operation to be submitted to the transport +type ClientOperation struct { + ID string + Method string + PathPattern string + ProducesMediaTypes []string + ConsumesMediaTypes []string + Schemes []string + AuthInfo ClientAuthInfoWriter + Params ClientRequestWriter + Reader ClientResponseReader + Context context.Context + Client *http.Client +} + +// A ClientTransport implementor knows how to submit Request objects to some destination +type ClientTransport interface { + //Submit(string, RequestWriter, ResponseReader, AuthInfoWriter) (interface{}, error) + Submit(*ClientOperation) (interface{}, error) +} diff --git a/vendor/github.com/go-openapi/runtime/client_request.go b/vendor/github.com/go-openapi/runtime/client_request.go new file mode 100644 index 00000000..3efda348 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_request.go @@ -0,0 +1,153 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/go-openapi/strfmt" +) + +// ClientRequestWriterFunc converts a function to a request writer interface +type ClientRequestWriterFunc func(ClientRequest, strfmt.Registry) error + +// WriteToRequest adds data to the request +func (fn ClientRequestWriterFunc) WriteToRequest(req ClientRequest, reg strfmt.Registry) error { + return fn(req, reg) +} + +// ClientRequestWriter is an interface for things that know how to write to a request +type ClientRequestWriter interface { + WriteToRequest(ClientRequest, strfmt.Registry) error +} + +// ClientRequest is an interface for things that know how to +// add information to a swagger client request +type ClientRequest interface { + SetHeaderParam(string, ...string) error + + GetHeaderParams() http.Header + + SetQueryParam(string, ...string) error + + SetFormParam(string, ...string) error + + SetPathParam(string, string) error + + GetQueryParams() url.Values + + SetFileParam(string, ...NamedReadCloser) error + + SetBodyParam(interface{}) error + + SetTimeout(time.Duration) error + + GetMethod() string + + GetPath() string + + GetBody() []byte + + GetBodyParam() interface{} + + GetFileParam() map[string][]NamedReadCloser +} + +// NamedReadCloser represents a named ReadCloser interface +type NamedReadCloser interface { + io.ReadCloser + Name() string +} + +// NamedReader creates a NamedReadCloser for use as file upload +func NamedReader(name string, rdr io.Reader) NamedReadCloser { + rc, ok := rdr.(io.ReadCloser) + if !ok { + rc = ioutil.NopCloser(rdr) + } + return &namedReadCloser{ + name: name, + cr: rc, + } +} + +type namedReadCloser struct { + name string + cr io.ReadCloser +} + +func (n *namedReadCloser) Close() error { + return n.cr.Close() +} +func (n *namedReadCloser) Read(p []byte) (int, error) { + return n.cr.Read(p) +} +func (n *namedReadCloser) Name() string { + return n.name +} + +type TestClientRequest struct { + Headers http.Header + Body interface{} +} + +func (t *TestClientRequest) SetHeaderParam(name string, values ...string) error { + if t.Headers == nil { + t.Headers = make(http.Header) + } + t.Headers.Set(name, values[0]) + return nil +} + +func (t *TestClientRequest) SetQueryParam(_ string, _ ...string) error { return nil } + +func (t *TestClientRequest) SetFormParam(_ string, _ ...string) error { return nil } + +func (t *TestClientRequest) SetPathParam(_ string, _ string) error { return nil } + +func (t *TestClientRequest) SetFileParam(_ string, _ ...NamedReadCloser) error { return nil } + +func (t *TestClientRequest) SetBodyParam(body interface{}) error { + t.Body = body + return nil +} + +func (t *TestClientRequest) SetTimeout(time.Duration) error { + return nil +} + +func (t *TestClientRequest) GetQueryParams() url.Values { return nil } + +func (t *TestClientRequest) GetMethod() string { return "" } + +func (t *TestClientRequest) GetPath() string { return "" } + +func (t *TestClientRequest) GetBody() []byte { return nil } + +func (t *TestClientRequest) GetBodyParam() interface{} { + return t.Body +} + +func (t *TestClientRequest) GetFileParam() map[string][]NamedReadCloser { + return nil +} + +func (t *TestClientRequest) GetHeaderParams() http.Header { + return t.Headers +} diff --git a/vendor/github.com/go-openapi/runtime/client_response.go b/vendor/github.com/go-openapi/runtime/client_response.go new file mode 100644 index 00000000..0d169114 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_response.go @@ -0,0 +1,110 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "encoding/json" + "fmt" + "io" +) + +// A ClientResponse represents a client response +// This bridges between responses obtained from different transports +type ClientResponse interface { + Code() int + Message() string + GetHeader(string) string + GetHeaders(string) []string + Body() io.ReadCloser +} + +// A ClientResponseReaderFunc turns a function into a ClientResponseReader interface implementation +type ClientResponseReaderFunc func(ClientResponse, Consumer) (interface{}, error) + +// ReadResponse reads the response +func (read ClientResponseReaderFunc) ReadResponse(resp ClientResponse, consumer Consumer) (interface{}, error) { + return read(resp, consumer) +} + +// A ClientResponseReader is an interface for things want to read a response. +// An application of this is to create structs from response values +type ClientResponseReader interface { + ReadResponse(ClientResponse, Consumer) (interface{}, error) +} + +// NewAPIError creates a new API error +func NewAPIError(opName string, payload interface{}, code int) *APIError { + return &APIError{ + OperationName: opName, + Response: payload, + Code: code, + } +} + +// APIError wraps an error model and captures the status code +type APIError struct { + OperationName string + Response interface{} + Code int +} + +func (o *APIError) Error() string { + var resp []byte + if err, ok := o.Response.(error); ok { + resp = []byte("'" + err.Error() + "'") + } else { + resp, _ = json.Marshal(o.Response) + } + return fmt.Sprintf("%s (status %d): %s", o.OperationName, o.Code, resp) +} + +func (o *APIError) String() string { + return o.Error() +} + +// IsSuccess returns true when this elapse o k response returns a 2xx status code +func (o *APIError) IsSuccess() bool { + return o.Code/100 == 2 +} + +// IsRedirect returns true when this elapse o k response returns a 3xx status code +func (o *APIError) IsRedirect() bool { + return o.Code/100 == 3 +} + +// IsClientError returns true when this elapse o k response returns a 4xx status code +func (o *APIError) IsClientError() bool { + return o.Code/100 == 4 +} + +// IsServerError returns true when this elapse o k response returns a 5xx status code +func (o *APIError) IsServerError() bool { + return o.Code/100 == 5 +} + +// IsCode returns true when this elapse o k response returns a 4xx status code +func (o *APIError) IsCode(code int) bool { + return o.Code == code +} + +// A ClientResponseStatus is a common interface implemented by all responses on the generated code +// You can use this to treat any client response based on status code +type ClientResponseStatus interface { + IsSuccess() bool + IsRedirect() bool + IsClientError() bool + IsServerError() bool + IsCode(int) bool +} diff --git a/vendor/github.com/go-openapi/runtime/constants.go b/vendor/github.com/go-openapi/runtime/constants.go new file mode 100644 index 00000000..51596924 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/constants.go @@ -0,0 +1,49 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +const ( + // HeaderContentType represents a http content-type header, it's value is supposed to be a mime type + HeaderContentType = "Content-Type" + + // HeaderTransferEncoding represents a http transfer-encoding header. + HeaderTransferEncoding = "Transfer-Encoding" + + // HeaderAccept the Accept header + HeaderAccept = "Accept" + // HeaderAuthorization the Authorization header + HeaderAuthorization = "Authorization" + + charsetKey = "charset" + + // DefaultMime the default fallback mime type + DefaultMime = "application/octet-stream" + // JSONMime the json mime type + JSONMime = "application/json" + // YAMLMime the yaml mime type + YAMLMime = "application/x-yaml" + // XMLMime the xml mime type + XMLMime = "application/xml" + // TextMime the text mime type + TextMime = "text/plain" + // HTMLMime the html mime type + HTMLMime = "text/html" + // CSVMime the csv mime type + CSVMime = "text/csv" + // MultipartFormMime the multipart form mime type + MultipartFormMime = "multipart/form-data" + // URLencodedFormMime the url encoded form mime type + URLencodedFormMime = "application/x-www-form-urlencoded" +) diff --git a/vendor/github.com/go-openapi/runtime/csv.go b/vendor/github.com/go-openapi/runtime/csv.go new file mode 100644 index 00000000..d807bd91 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/csv.go @@ -0,0 +1,77 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "bytes" + "encoding/csv" + "errors" + "io" +) + +// CSVConsumer creates a new CSV consumer +func CSVConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("CSVConsumer requires a reader") + } + + csvReader := csv.NewReader(reader) + writer, ok := data.(io.Writer) + if !ok { + return errors.New("data type must be io.Writer") + } + csvWriter := csv.NewWriter(writer) + records, err := csvReader.ReadAll() + if err != nil { + return err + } + for _, r := range records { + if err := csvWriter.Write(r); err != nil { + return err + } + } + csvWriter.Flush() + return nil + }) +} + +// CSVProducer creates a new CSV producer +func CSVProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("CSVProducer requires a writer") + } + + dataBytes, ok := data.([]byte) + if !ok { + return errors.New("data type must be byte array") + } + + csvReader := csv.NewReader(bytes.NewBuffer(dataBytes)) + records, err := csvReader.ReadAll() + if err != nil { + return err + } + csvWriter := csv.NewWriter(writer) + for _, r := range records { + if err := csvWriter.Write(r); err != nil { + return err + } + } + csvWriter.Flush() + return nil + }) +} diff --git a/vendor/github.com/go-openapi/runtime/discard.go b/vendor/github.com/go-openapi/runtime/discard.go new file mode 100644 index 00000000..0d390cfd --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/discard.go @@ -0,0 +1,9 @@ +package runtime + +import "io" + +// DiscardConsumer does absolutely nothing, it's a black hole. +var DiscardConsumer = ConsumerFunc(func(_ io.Reader, _ interface{}) error { return nil }) + +// DiscardProducer does absolutely nothing, it's a black hole. +var DiscardProducer = ProducerFunc(func(_ io.Writer, _ interface{}) error { return nil }) diff --git a/vendor/github.com/go-openapi/runtime/file.go b/vendor/github.com/go-openapi/runtime/file.go new file mode 100644 index 00000000..397d8a45 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/file.go @@ -0,0 +1,19 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import "github.com/go-openapi/swag" + +type File = swag.File diff --git a/vendor/github.com/go-openapi/runtime/headers.go b/vendor/github.com/go-openapi/runtime/headers.go new file mode 100644 index 00000000..4d111db4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/headers.go @@ -0,0 +1,45 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "mime" + "net/http" + + "github.com/go-openapi/errors" +) + +// ContentType parses a content type header +func ContentType(headers http.Header) (string, string, error) { + ct := headers.Get(HeaderContentType) + orig := ct + if ct == "" { + ct = DefaultMime + } + if ct == "" { + return "", "", nil + } + + mt, opts, err := mime.ParseMediaType(ct) + if err != nil { + return "", "", errors.NewParseError(HeaderContentType, "header", orig, err) + } + + if cs, ok := opts[charsetKey]; ok { + return mt, cs, nil + } + + return mt, "", nil +} diff --git a/vendor/github.com/go-openapi/runtime/interfaces.go b/vendor/github.com/go-openapi/runtime/interfaces.go new file mode 100644 index 00000000..e3341286 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/interfaces.go @@ -0,0 +1,112 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "context" + "io" + "net/http" + + "github.com/go-openapi/strfmt" +) + +// OperationHandlerFunc an adapter for a function to the OperationHandler interface +type OperationHandlerFunc func(interface{}) (interface{}, error) + +// Handle implements the operation handler interface +func (s OperationHandlerFunc) Handle(data interface{}) (interface{}, error) { + return s(data) +} + +// OperationHandler a handler for a swagger operation +type OperationHandler interface { + Handle(interface{}) (interface{}, error) +} + +// ConsumerFunc represents a function that can be used as a consumer +type ConsumerFunc func(io.Reader, interface{}) error + +// Consume consumes the reader into the data parameter +func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error { + return fn(reader, data) +} + +// Consumer implementations know how to bind the values on the provided interface to +// data provided by the request body +type Consumer interface { + // Consume performs the binding of request values + Consume(io.Reader, interface{}) error +} + +// ProducerFunc represents a function that can be used as a producer +type ProducerFunc func(io.Writer, interface{}) error + +// Produce produces the response for the provided data +func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error { + return f(writer, data) +} + +// Producer implementations know how to turn the provided interface into a valid +// HTTP response +type Producer interface { + // Produce writes to the http response + Produce(io.Writer, interface{}) error +} + +// AuthenticatorFunc turns a function into an authenticator +type AuthenticatorFunc func(interface{}) (bool, interface{}, error) + +// Authenticate authenticates the request with the provided data +func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) { + return f(params) +} + +// Authenticator represents an authentication strategy +// implementations of Authenticator know how to authenticate the +// request data and translate that into a valid principal object or an error +type Authenticator interface { + Authenticate(interface{}) (bool, interface{}, error) +} + +// AuthorizerFunc turns a function into an authorizer +type AuthorizerFunc func(*http.Request, interface{}) error + +// Authorize authorizes the processing of the request for the principal +func (f AuthorizerFunc) Authorize(r *http.Request, principal interface{}) error { + return f(r, principal) +} + +// Authorizer represents an authorization strategy +// implementations of Authorizer know how to authorize the principal object +// using the request data and returns error if unauthorized +type Authorizer interface { + Authorize(*http.Request, interface{}) error +} + +// Validatable types implementing this interface allow customizing their validation +// this will be used instead of the reflective validation based on the spec document. +// the implementations are assumed to have been generated by the swagger tool so they should +// contain all the validations obtained from the spec +type Validatable interface { + Validate(strfmt.Registry) error +} + +// ContextValidatable types implementing this interface allow customizing their validation +// this will be used instead of the reflective validation based on the spec document. +// the implementations are assumed to have been generated by the swagger tool so they should +// contain all the context validations obtained from the spec +type ContextValidatable interface { + ContextValidate(context.Context, strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/json.go b/vendor/github.com/go-openapi/runtime/json.go new file mode 100644 index 00000000..5a690559 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/json.go @@ -0,0 +1,38 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "encoding/json" + "io" +) + +// JSONConsumer creates a new JSON consumer +func JSONConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + dec := json.NewDecoder(reader) + dec.UseNumber() // preserve number formats + return dec.Decode(data) + }) +} + +// JSONProducer creates a new JSON producer +func JSONProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + enc := json.NewEncoder(writer) + enc.SetEscapeHTML(false) + return enc.Encode(data) + }) +} diff --git a/vendor/github.com/go-openapi/runtime/request.go b/vendor/github.com/go-openapi/runtime/request.go new file mode 100644 index 00000000..078fda17 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/request.go @@ -0,0 +1,139 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "bufio" + "io" + "net/http" + "strings" + + "github.com/go-openapi/swag" +) + +// CanHaveBody returns true if this method can have a body +func CanHaveBody(method string) bool { + mn := strings.ToUpper(method) + return mn == "POST" || mn == "PUT" || mn == "PATCH" || mn == "DELETE" +} + +// IsSafe returns true if this is a request with a safe method +func IsSafe(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn == "GET" || mn == "HEAD" +} + +// AllowsBody returns true if the request allows for a body +func AllowsBody(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn != "HEAD" +} + +// HasBody returns true if this method needs a content-type +func HasBody(r *http.Request) bool { + // happy case: we have a content length set + if r.ContentLength > 0 { + return true + } + + if r.Header.Get("content-length") != "" { + // in this case, no Transfer-Encoding should be present + // we have a header set but it was explicitly set to 0, so we assume no body + return false + } + + rdr := newPeekingReader(r.Body) + r.Body = rdr + return rdr.HasContent() +} + +func newPeekingReader(r io.ReadCloser) *peekingReader { + if r == nil { + return nil + } + return &peekingReader{ + underlying: bufio.NewReader(r), + orig: r, + } +} + +type peekingReader struct { + underlying interface { + Buffered() int + Peek(int) ([]byte, error) + Read([]byte) (int, error) + } + orig io.ReadCloser +} + +func (p *peekingReader) HasContent() bool { + if p == nil { + return false + } + if p.underlying.Buffered() > 0 { + return true + } + b, err := p.underlying.Peek(1) + if err != nil { + return false + } + return len(b) > 0 +} + +func (p *peekingReader) Read(d []byte) (int, error) { + if p == nil { + return 0, io.EOF + } + return p.underlying.Read(d) +} + +func (p *peekingReader) Close() error { + p.underlying = nil + if p.orig != nil { + return p.orig.Close() + } + return nil +} + +// JSONRequest creates a new http request with json headers set +func JSONRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + req.Header.Add(HeaderContentType, JSONMime) + req.Header.Add(HeaderAccept, JSONMime) + return req, nil +} + +// Gettable for things with a method GetOK(string) (data string, hasKey bool, hasValue bool) +type Gettable interface { + GetOK(string) ([]string, bool, bool) +} + +// ReadSingleValue reads a single value from the source +func ReadSingleValue(values Gettable, name string) string { + vv, _, hv := values.GetOK(name) + if hv { + return vv[len(vv)-1] + } + return "" +} + +// ReadCollectionValue reads a collection value from a string data source +func ReadCollectionValue(values Gettable, name, collectionFormat string) []string { + v := ReadSingleValue(values, name) + return swag.SplitByFormat(v, collectionFormat) +} diff --git a/vendor/github.com/go-openapi/runtime/statuses.go b/vendor/github.com/go-openapi/runtime/statuses.go new file mode 100644 index 00000000..3b011a0b --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/statuses.go @@ -0,0 +1,90 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +// Statuses lists the most common HTTP status codes to default message +// taken from https://httpstatuses.com/ +var Statuses = map[int]string{ + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Checkpoint", + 122: "URI too long", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Request Processed", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Request Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 420: "Enhance Your Calm", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 444: "No Response", + 449: "Retry With", + 450: "Blocked by Windows Parental Controls", + 451: "Wrong Exchange Server", + 499: "Client Closed Request", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 509: "Bandwidth Limit Exceeded", + 510: "Not Extended", + 511: "Network Authentication Required", + 598: "Network read timeout error", + 599: "Network connect timeout error", +} diff --git a/vendor/github.com/go-openapi/runtime/text.go b/vendor/github.com/go-openapi/runtime/text.go new file mode 100644 index 00000000..f33320b7 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/text.go @@ -0,0 +1,116 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "bytes" + "encoding" + "errors" + "fmt" + "io" + "reflect" + + "github.com/go-openapi/swag" +) + +// TextConsumer creates a new text consumer +func TextConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("TextConsumer requires a reader") // early exit + } + + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(reader) + if err != nil { + return err + } + b := buf.Bytes() + + // If the buffer is empty, no need to unmarshal it, which causes a panic. + if len(b) == 0 { + return nil + } + + if tu, ok := data.(encoding.TextUnmarshaler); ok { + err := tu.UnmarshalText(b) + if err != nil { + return fmt.Errorf("text consumer: %v", err) + } + + return nil + } + + t := reflect.TypeOf(data) + if data != nil && t.Kind() == reflect.Ptr { + v := reflect.Indirect(reflect.ValueOf(data)) + if t.Elem().Kind() == reflect.String { + v.SetString(string(b)) + return nil + } + } + + return fmt.Errorf("%v (%T) is not supported by the TextConsumer, %s", + data, data, "can be resolved by supporting TextUnmarshaler interface") + }) +} + +// TextProducer creates a new text producer +func TextProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("TextProducer requires a writer") // early exit + } + + if data == nil { + return errors.New("no data given to produce text from") + } + + if tm, ok := data.(encoding.TextMarshaler); ok { + txt, err := tm.MarshalText() + if err != nil { + return fmt.Errorf("text producer: %v", err) + } + _, err = writer.Write(txt) + return err + } + + if str, ok := data.(error); ok { + _, err := writer.Write([]byte(str.Error())) + return err + } + + if str, ok := data.(fmt.Stringer); ok { + _, err := writer.Write([]byte(str.String())) + return err + } + + v := reflect.Indirect(reflect.ValueOf(data)) + if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { + b, err := swag.WriteJSON(data) + if err != nil { + return err + } + _, err = writer.Write(b) + return err + } + if v.Kind() != reflect.String { + return fmt.Errorf("%T is not a supported type by the TextProducer", data) + } + + _, err := writer.Write([]byte(v.String())) + return err + }) +} diff --git a/vendor/github.com/go-openapi/runtime/values.go b/vendor/github.com/go-openapi/runtime/values.go new file mode 100644 index 00000000..11f5732a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/values.go @@ -0,0 +1,19 @@ +package runtime + +// Values typically represent parameters on a http request. +type Values map[string][]string + +// GetOK returns the values collection for the given key. +// When the key is present in the map it will return true for hasKey. +// When the value is not empty it will return true for hasValue. +func (v Values) GetOK(key string) (value []string, hasKey bool, hasValue bool) { + value, hasKey = v[key] + if !hasKey { + return + } + if len(value) == 0 { + return + } + hasValue = true + return +} diff --git a/vendor/github.com/go-openapi/runtime/xml.go b/vendor/github.com/go-openapi/runtime/xml.go new file mode 100644 index 00000000..821c7393 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/xml.go @@ -0,0 +1,36 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "encoding/xml" + "io" +) + +// XMLConsumer creates a new XML consumer +func XMLConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + dec := xml.NewDecoder(reader) + return dec.Decode(data) + }) +} + +// XMLProducer creates a new XML producer +func XMLProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + enc := xml.NewEncoder(writer) + return enc.Encode(data) + }) +} diff --git a/vendor/github.com/go-openapi/spec/.editorconfig b/vendor/github.com/go-openapi/spec/.editorconfig new file mode 100644 index 00000000..3152da69 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/spec/.gitignore b/vendor/github.com/go-openapi/spec/.gitignore new file mode 100644 index 00000000..dd91ed6a --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/vendor/github.com/go-openapi/spec/.golangci.yml b/vendor/github.com/go-openapi/spec/.golangci.yml new file mode 100644 index 00000000..835d55e7 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.golangci.yml @@ -0,0 +1,42 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 45 + maligned: + suggest-new: true + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 2 + +linters: + enable-all: true + disable: + - maligned + - unparam + - lll + - gochecknoinits + - gochecknoglobals + - funlen + - godox + - gocognit + - whitespace + - wsl + - wrapcheck + - testpackage + - nlreturn + - gomnd + - exhaustivestruct + - goerr113 + - errorlint + - nestif + - godot + - gofumpt + - paralleltest + - tparallel + - thelper + - ifshort diff --git a/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/spec/LICENSE b/vendor/github.com/go-openapi/spec/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/spec/README.md b/vendor/github.com/go-openapi/spec/README.md new file mode 100644 index 00000000..18782c6d --- /dev/null +++ b/vendor/github.com/go-openapi/spec/README.md @@ -0,0 +1,34 @@ +# OAI object model + +[![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) + +[![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) +[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/spec.svg)](https://pkg.go.dev/github.com/go-openapi/spec) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec) + +The object model for OpenAPI specification documents. + +### FAQ + +* What does this do? + +> 1. This package knows how to marshal and unmarshal Swagger API specifications into a golang object model +> 2. It knows how to resolve $ref and expand them to make a single root document + +* How does it play with the rest of the go-openapi packages ? + +> 1. This package is at the core of the go-openapi suite of packages and [code generator](https://github.com/go-swagger/go-swagger) +> 2. There is a [spec loading package](https://github.com/go-openapi/loads) to fetch specs as JSON or YAML from local or remote locations +> 3. There is a [spec validation package](https://github.com/go-openapi/validate) built on top of it +> 4. There is a [spec analysis package](https://github.com/go-openapi/analysis) built on top of it, to analyze, flatten, fix and merge spec documents + +* Does this library support OpenAPI 3? + +> No. +> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0). +> There is no plan to make it evolve toward supporting OpenAPI 3.x. +> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story. +> +> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3 diff --git a/vendor/github.com/go-openapi/spec/appveyor.yml b/vendor/github.com/go-openapi/spec/appveyor.yml new file mode 100644 index 00000000..09035939 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/appveyor.yml @@ -0,0 +1,32 @@ +version: "0.1.{build}" + +clone_folder: C:\go-openapi\spec +shallow_clone: true # for startup speed +pull_requests: + do_not_increment_build_number: true + +#skip_tags: true +#skip_branch_with_pr: true + +# appveyor.yml +build: off + +environment: + GOPATH: c:\gopath + +stack: go 1.15 + +test_script: + - go test -v -timeout 20m ./... + +deploy: off + +notifications: + - provider: Slack + incoming_webhook: https://hooks.slack.com/services/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 + auth_token: + secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4= + channel: bots + on_build_success: false + on_build_failure: true + on_build_status_changed: true diff --git a/vendor/github.com/go-openapi/spec/bindata.go b/vendor/github.com/go-openapi/spec/bindata.go new file mode 100644 index 00000000..afc83850 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/bindata.go @@ -0,0 +1,297 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// schemas/jsonschema-draft-04.json (4.357kB) +// schemas/v2/schema.json (40.248kB) + +package spec + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _jsonschemaDraft04Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x57\x3d\x6f\xdb\x3c\x10\xde\xf3\x2b\x08\x26\x63\xf2\x2a\x2f\xd0\xc9\x5b\xd1\x2e\x01\x5a\x34\x43\x37\x23\x03\x6d\x9d\x6c\x06\x14\xa9\x50\x54\x60\xc3\xd0\x7f\x2f\x28\x4a\x14\x29\x91\x92\x2d\xa7\x8d\x97\x28\xbc\xaf\xe7\x8e\xf7\xc5\xd3\x0d\x42\x08\x61\x9a\xe2\x15\xc2\x7b\xa5\x8a\x55\x92\xbc\x96\x82\x3f\x94\xdb\x3d\xe4\xe4\x3f\x21\x77\x49\x2a\x49\xa6\x1e\x1e\xbf\x24\xe6\xec\x16\xdf\x1b\xa1\x3b\xf3\xff\x02\xc9\x14\xca\xad\xa4\x85\xa2\x82\x6b\xe9\x6f\x42\x02\x32\x2c\x28\x07\x45\x5a\x15\x3d\x77\x46\x39\xd5\xcc\x25\x5e\x21\x83\xb8\x21\x18\xb6\xaf\x52\x92\xa3\x47\x68\x88\xea\x58\x80\x56\x4e\x1a\xf2\xbd\x4f\xcc\x29\x7f\x52\x90\x6b\x7d\xff\x0f\x48\xb4\x3d\x3f\x21\x7c\x27\x21\xd3\x2a\x6e\x31\xaa\x2d\x53\xdd\xf3\xe3\x42\x94\x54\xd1\x77\x78\xe2\x0a\x76\x20\xe3\x20\x68\xcb\x30\x86\x41\xf3\x2a\xc7\x2b\xf4\x78\x8e\xfe\xef\x90\x91\x8a\xa9\xc7\xb1\x1d\xc2\xd8\x2f\x0d\x75\xed\xc1\x4e\x9c\xc8\x25\x43\xac\xa8\xbe\xd7\xcc\xa9\xd1\xa9\x21\xa0\x1a\xbd\x04\x61\x94\x34\x2f\x18\xfc\x3e\x16\x50\x8e\x4d\x03\x6f\x1c\x58\xdb\x48\x23\xbc\x11\x82\x01\xe1\xfa\xd3\x3a\x8e\x30\xaf\x18\x33\x7f\xf3\x8d\x39\x11\x9b\x57\xd8\x2a\xfd\x55\x2a\x49\xf9\x0e\xc7\xec\x37\xd4\x25\xf7\xec\x5c\x66\xc7\xd7\x99\xaa\xcf\x4f\x89\x8a\xd3\xb7\x0a\x3a\xaa\x92\x15\xf4\x30\x6f\x1c\xb0\xd6\x46\xe7\x98\x39\x2d\xa4\x28\x40\x2a\x3a\x88\x9e\x29\xba\x88\x37\x2d\xca\x60\x38\xfa\xba\x5b\x20\xac\xa8\x62\xb0\x4c\xd4\xaf\xda\x45\x0a\xba\x5c\x3b\xb9\xc7\x79\xc5\x14\x2d\x18\x34\x19\x1c\x51\xdb\x25\x4d\xb4\x7e\x06\x14\x38\x6c\x59\x55\xd2\x77\xf8\x69\x59\xfc\x7b\x73\xed\x93\x43\xcb\x32\x6d\x3c\x28\xdc\x1b\x9a\xd3\x62\xab\xc2\x27\xf7\x41\xc9\x08\x2b\x23\x08\xad\x13\x57\x21\x9c\xd3\x72\x0d\x42\x72\xf8\x01\x7c\xa7\xf6\x83\xce\x39\xd7\x82\x3c\x1f\x2f\xd6\x60\x1b\xa2\xdf\x35\x89\x52\x20\xe7\x73\x74\xe0\x66\x26\x64\x4e\xb4\x97\x58\xc2\x0e\x0e\xe1\x60\x92\x34\x6d\xa0\x10\xd6\xb5\x83\x61\x27\xe6\x47\xd3\x89\xbd\x63\xfd\x3b\x8d\x03\x3d\x6c\x42\x2d\x5b\x70\xee\xe8\xdf\x4b\xf4\x66\x4e\xe1\x01\x45\x17\x80\x74\xad\x4f\xc3\xf3\xae\xc6\x1d\xc6\xd7\xc2\xce\xc9\xe1\x29\x30\x86\x2f\x4a\xa6\x4b\x15\x84\x73\xc9\x6f\xfd\x7f\xa5\x6e\x9e\xbd\xf1\xb0\xd4\xdd\x45\x5a\xc2\x3e\x4b\x78\xab\xa8\x84\x74\x4a\x91\x3b\x92\x23\x05\xf2\x1c\x1e\x7b\xf3\x09\xf8\xcf\xab\x24\xb6\x60\xa2\xe8\x4c\x9f\x75\x77\xaa\x8c\xe6\x01\x45\x36\x86\xcf\xc3\x63\x3a\xea\xd4\x8d\x7e\x06\xac\x14\x0a\xe0\x29\xf0\xed\x07\x22\x1a\x65\xda\x44\xae\xa2\x73\x1a\xe6\x90\x69\xa2\x8c\x46\xb2\x2f\xde\x49\x38\x08\xed\xfe\xfd\x41\xaf\x9f\xa9\x55\xd7\xdd\x22\x8d\xfa\x45\x63\xc5\x0f\x80\xf3\xb4\x08\xd6\x79\x30\x9e\x93\xee\x59\xa6\xd0\x4b\xee\x22\xe3\x33\xc1\x3a\x27\x68\x36\x78\x7e\x87\x0a\x06\xd5\x2e\x20\xd3\xaf\x15\xfb\xd8\x3b\x73\x14\xbb\x92\xed\x05\x5d\x2e\x29\x38\x2c\x94\xe4\x42\x45\x5e\xd3\xb5\x7d\xdf\x47\xca\x38\xb4\x5c\xaf\xfb\x7d\xdd\x6d\xf4\xa1\x2d\x77\xdd\x2f\xce\x6d\xc4\x7b\x8b\x4e\x67\xa9\x6f\xfe\x04\x00\x00\xff\xff\xb1\xd1\x27\x78\x05\x11\x00\x00") + +func jsonschemaDraft04JsonBytes() ([]byte, error) { + return bindataRead( + _jsonschemaDraft04Json, + "jsonschema-draft-04.json", + ) +} + +func jsonschemaDraft04Json() (*asset, error) { + bytes, err := jsonschemaDraft04JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "jsonschema-draft-04.json", size: 4357, mode: os.FileMode(0640), modTime: time.Unix(1568963823, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe1, 0x48, 0x9d, 0xb, 0x47, 0x55, 0xf0, 0x27, 0x93, 0x30, 0x25, 0x91, 0xd3, 0xfc, 0xb8, 0xf0, 0x7b, 0x68, 0x93, 0xa8, 0x2a, 0x94, 0xf2, 0x48, 0x95, 0xf8, 0xe4, 0xed, 0xf1, 0x1b, 0x82, 0xe2}} + return a, nil +} + +var _v2SchemaJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5d\x4f\x93\xdb\x36\xb2\xbf\xfb\x53\xa0\x14\x57\xd9\xae\xd8\x92\xe3\xf7\x2e\xcf\x97\xd4\xbc\xd8\x49\x66\x37\x5e\x4f\x79\x26\xbb\x87\x78\x5c\x05\x91\x2d\x09\x09\x09\x30\x00\x38\x33\x5a\xef\x7c\xf7\x2d\xf0\x9f\x08\x02\x20\x41\x8a\xd2\xc8\x0e\x0f\xa9\x78\x28\xa0\xd1\xdd\x68\x34\x7e\xdd\xf8\xf7\xf9\x11\x42\x33\x49\x64\x04\xb3\xd7\x68\x76\x86\xfe\x76\xf9\xfe\x1f\xe8\x32\xd8\x40\x8c\xd1\x8a\x71\x74\x79\x8b\xd7\x6b\xe0\xe8\xd5\xfc\x25\x3a\xbb\x38\x9f\xcf\x9e\xab\x0a\x24\x54\xa5\x37\x52\x26\xaf\x17\x0b\x91\x17\x99\x13\xb6\xb8\x79\xb5\x10\x59\xdd\xf9\xef\x82\xd1\x6f\xf2\xc2\x8f\xf3\x4f\xb5\x1a\xea\xc7\x17\x45\x41\xc6\xd7\x8b\x90\xe3\x95\x7c\xf1\xf2\x7f\x8b\xca\x45\x3d\xb9\x4d\x32\xa6\xd8\xf2\x77\x08\x64\xfe\x8d\xc3\x9f\x29\xe1\xa0\x9a\xff\xed\x11\x42\x08\xcd\x8a\xd6\xb3\x9f\x15\x67\x74\xc5\xca\x7f\x27\x58\x6e\xc4\xec\x11\x42\xd7\x59\x5d\x1c\x86\x44\x12\x46\x71\x74\xc1\x59\x02\x5c\x12\x10\xb3\xd7\x68\x85\x23\x01\x59\x81\x04\x4b\x09\x9c\x6a\xbf\x7e\xce\x49\x7d\xba\x7b\x51\xfd\xa1\x44\xe2\xb0\x52\xac\x7d\xb3\x08\x61\x45\x68\x46\x56\x2c\x6e\x80\x86\x8c\xbf\xbd\x93\x40\x05\x61\x74\x96\x95\xbe\x7f\x84\xd0\x7d\x4e\xde\x42\xb7\xe4\xbe\x46\xbb\x14\x5b\x48\x4e\xe8\xba\x90\x05\xa1\x19\xd0\x34\xae\xc4\xce\xbe\xbc\x9a\xbf\x9c\x15\x7f\x5d\x57\xc5\x42\x10\x01\x27\x89\xe2\x48\x51\xb9\xda\x40\xd5\x87\x37\xc0\x15\x5f\x88\xad\x90\xdc\x10\x81\x42\x16\xa4\x31\x50\x39\x2f\x38\xad\xab\xb0\x53\xd8\xac\x94\x56\x6f\xc3\x84\xf4\x11\xa4\x50\xb3\xfa\xe9\xd3\x6f\x9f\x3e\xdf\x2f\xd0\xeb\x8f\x1f\x3f\x7e\xbc\xfe\xf6\xe9\xf7\xaf\x5f\x7f\xfc\x18\x7e\xfb\xec\xfb\xc7\xb3\x36\x79\x54\x43\xe8\x29\xc5\x31\x20\xc6\x11\x49\x9e\xe5\x12\x41\x66\xa0\xe8\xed\x1d\x8e\x93\x08\x5e\xa3\x27\x3b\xc3\x7c\xa2\x73\xba\xc4\x02\x2e\xb0\xdc\xf4\xe5\x76\xd1\xca\x96\xa2\x8a\x94\xcd\x21\xc9\x6c\xec\x2c\x70\x42\x9e\x34\x74\x9d\x19\x7c\xcd\x20\x9c\xea\x2e\x0a\xfe\x42\x84\xd4\x29\x04\x8c\x8a\xb4\x41\xa2\xc1\xdc\x19\x8a\x88\x90\x4a\x49\xef\xce\xdf\xbd\x45\x4a\x52\x81\x70\x10\x40\x22\x21\x44\xcb\x6d\xc5\xec\x4e\x3c\x1c\x45\xef\x57\x9a\xb5\x7d\xae\xfe\xe5\xe4\x31\x86\x90\xe0\xab\x6d\x02\x3b\x2e\xcb\x11\x90\xd9\xa8\xc6\x77\xc2\x59\x98\x06\xfd\xf9\x2e\x78\x45\x01\xa6\xa8\xa0\x71\x5c\xbe\x33\xa7\xd2\xd9\x5f\x95\xef\xd9\xd5\xac\xfd\xdc\x5d\xbf\x5e\xb8\xd1\x3e\xc7\x31\x48\xe0\x5e\x4c\x14\x65\xdf\xb8\xa8\x71\x10\x09\xa3\xc2\xc7\x02\xcb\xa2\x4e\x5a\x02\x82\x94\x13\xb9\xf5\x30\xe6\xb2\xa4\xb5\xfe\x9b\x3e\x7a\xb2\x55\xd2\xa8\x4a\xbc\x16\xb6\x71\x8e\x39\xc7\xdb\x9d\xe1\x10\x09\x71\xbd\x9c\xb3\x41\x89\xd7\xa5\x89\xdc\x57\xb5\x53\x4a\xfe\x4c\xe1\xbc\xa0\x21\x79\x0a\x1a\x0f\x70\xa7\x5c\x08\x8e\xde\xb0\xc0\x43\x24\xad\x74\x63\x0e\xb1\xd9\x90\xe1\xb0\x2d\x13\xa7\x6d\x78\xfd\x04\x14\x38\x8e\x90\xaa\xce\x63\xac\x3e\x23\xbc\x64\xa9\xb4\xf8\x03\x63\xde\xcd\xbe\x16\x13\x4a\x55\xac\x82\x12\xc6\xac\xd4\x35\xf7\x22\xd4\x3a\xff\x22\x73\x0e\x6e\x51\xa0\x75\x1e\xae\x8f\xe8\x5d\xc7\x59\xe6\xe4\x9a\x18\x8d\xd6\x1c\x53\x84\x4d\xb7\x67\x28\x37\x09\x84\x69\x88\x12\x0e\x01\x11\x80\x32\xa2\xf5\xb9\xaa\xc6\xd9\x73\x53\xab\xfb\xb4\x2e\x20\xc6\x54\x92\xa0\x9a\xf3\x69\x1a\x2f\x81\x77\x37\xae\x53\x1a\xce\x40\xc4\xa8\x82\x1c\xb5\xef\xda\x24\x7d\xb9\x61\x69\x14\xa2\x25\xa0\x90\xac\x56\xc0\x81\x4a\xb4\xe2\x2c\xce\x4a\x64\x7a\x9a\x23\xf4\x13\x91\x3f\xa7\x4b\xf4\x63\x84\x6f\x18\x87\x10\xbd\xc3\xfc\x8f\x90\xdd\x52\x44\x04\xc2\x51\xc4\x6e\x21\x74\x48\x21\x81\xc7\xe2\xfd\xea\x12\xf8\x0d\x09\xf6\xe9\x47\x35\xaf\x67\xc4\x14\xf7\x22\x27\x97\xe1\xe2\x76\x2d\x06\x8c\x4a\x1c\x48\x3f\x73\x2d\x0b\x5b\x29\x45\x24\x00\x2a\x0c\x11\xec\x94\xca\xc2\xa6\xc1\x37\x21\x43\x83\x3b\x5f\x97\xf1\x43\x5e\x53\x73\x19\xa5\x36\xd8\x2d\x05\x2e\x34\x0b\xeb\x39\xfc\x1d\x63\x51\x01\xbd\x3d\xbb\x90\x84\x40\x25\x59\x6d\x09\x5d\xa3\x1c\x37\xe6\x5c\x16\x9a\x40\x09\x70\xc1\xe8\x82\xf1\x35\xa6\xe4\xdf\x99\x5c\x8e\x9e\x4d\x79\xb4\x27\x2f\xbf\x7e\xf8\x05\x25\x8c\x50\xa9\x98\x29\x90\x62\x60\xea\x75\xae\x13\xca\xbf\x2b\x1a\x29\x27\x76\xd6\x20\xc6\x64\x5f\xe6\x32\x1a\x08\x87\x21\x07\x21\xbc\xb4\xe4\xe0\x32\x67\xa6\xcd\xf3\x1e\xcd\xd9\x6b\xb6\x6f\x8e\x27\xa7\xed\xdb\xe7\xbc\xcc\x1a\x07\xce\x6f\x87\x33\xf0\xba\x51\x17\x22\x66\x78\x79\x8e\xce\xe5\x13\x81\x80\x06\x2c\xe5\x78\x0d\xa1\xb2\xb8\x54\xa8\x79\x09\xbd\xbf\x3c\x47\x01\x8b\x13\x2c\xc9\x32\xaa\xaa\x1d\xd5\xee\xab\x36\xbd\x6c\xfd\x54\x6c\xc8\x08\x01\x3c\xbd\xe7\x07\x88\xb0\x24\x37\x79\x90\x28\x4a\x1d\x10\x1a\x92\x1b\x12\xa6\x38\x42\x40\xc3\x4c\x43\x62\x8e\xae\x36\xb0\x45\x71\x2a\xa4\x9a\x23\x79\x59\xb1\xa8\xf2\xa4\x0c\x60\x9f\xcc\x8d\x40\xf5\x80\xca\xa8\x99\xc3\xa7\x85\x1f\x31\x25\xa9\x82\xc5\x6d\xbd\xd8\x36\x76\x7c\x02\x28\x97\xf6\x1d\x74\x3b\x11\x7e\x91\xae\x32\xf8\x6c\xf4\xe6\x7b\x9a\xa5\x1f\x62\xc6\x21\xcf\x9a\xe5\xed\x8b\x02\xf3\x2c\x33\x33\xdf\x00\xca\xc9\x09\xb4\x04\xf5\xa5\x08\xd7\xc3\x02\x18\x66\xf1\xab\x1e\x83\x37\x4c\xcd\x12\xc1\x1d\x50\xf6\xaa\xbd\xfe\xe2\x73\x48\x38\x08\xa0\x32\x9b\x18\x44\x86\x0b\x6a\xc1\xaa\x26\x96\x2d\x96\x3c\xa0\x54\x65\x73\xe3\x08\xb5\x8b\x99\xbd\x82\xbc\x9e\xc2\xe8\x53\x46\x83\x3f\x33\x54\x2b\x5b\xad\x92\x79\xd9\x8f\x5d\x93\x98\xf2\xe6\xc6\x1c\xe6\x9a\x9e\xfc\x43\x82\x31\x66\x8e\x53\x77\xfe\x90\xe7\xf3\xf6\xe9\x62\x23\x3f\x10\x93\x18\xae\x72\x1a\x9d\xf9\x48\xcb\xcc\x5a\x65\xc7\x4a\x04\xf0\xf3\xd5\xd5\x05\x8a\x41\x08\xbc\x86\x86\x43\x51\x6c\xe0\x46\x57\xf6\x44\x40\x0d\xfb\xff\xa2\xc3\x7c\x3d\x39\x84\xdc\x09\x22\x64\x4f\x12\xd9\xba\xaa\xf6\xe3\xbd\x56\xdd\x91\x25\x6a\x14\x9c\x89\x34\x8e\x31\xdf\xee\x15\x7e\x2f\x39\x81\x15\x2a\x28\x95\x66\x51\xf5\xfd\x83\xc5\xfe\x15\x07\xcf\xf7\x08\xee\x1d\x8e\xb6\xc5\x52\xcc\x8c\x5a\x93\x66\xc5\xd8\x79\x38\x46\xd6\xa7\x88\x37\xc9\x2e\xe3\xd2\xa5\x7b\x4b\x3a\xdc\xa1\xdc\x9e\x29\xf1\x8c\x8a\x99\x16\x47\x8d\xd4\x78\x8b\xf6\x1c\xe9\x71\x54\x1b\x69\xa8\x4a\x93\x37\xe5\xb2\x2c\x4f\x0c\x92\xab\xa0\x73\x32\x72\x59\xd3\xf0\x2d\x8d\xed\xca\x37\x16\x19\x9e\xdb\x1c\xab\x17\x49\xc3\x0f\x37\xdc\x88\xb1\xb4\xd4\x42\xcb\x58\x5e\x6a\x52\x0b\x15\x10\x0a\xb0\x04\xe7\xf8\x58\x32\x16\x01\xa6\xcd\x01\xb2\xc2\x69\x24\x35\x38\x6f\x30\x6a\xae\x1b\xb4\x71\xaa\xad\x1d\xa0\xd6\x20\x2d\x8b\x3c\xc6\x82\x62\x27\x34\x6d\x15\x84\x7b\x43\xb1\x35\x78\xa6\x24\x77\x28\xc1\x6e\xfc\xe9\x48\x74\xf4\x15\xe3\xe1\x84\x42\x88\x40\x7a\x26\x49\x3b\x48\xb1\xa4\x19\x8e\x0c\xa7\xb5\x01\x6c\x0c\x97\x61\x8a\xc2\x32\xd8\x8c\x44\x69\x24\xbf\x65\x1d\x74\xd6\xe5\x44\xef\xec\x48\x5e\xb7\x8a\xa3\x29\x8e\x41\x64\xce\x1f\x88\xdc\x00\x47\x4b\x40\x98\x6e\xd1\x0d\x8e\x48\x98\x63\x5c\x21\xb1\x4c\x05\x0a\x58\x98\xc5\x6d\x4f\x0a\x77\x53\x4f\x8b\xc4\x44\x1f\xb2\xdf\x8d\x3b\xea\x9f\xfe\xf6\xf2\xc5\xff\x5d\x7f\xfe\x9f\xfb\x67\x8f\xff\xf3\xe9\x69\xd1\xfe\xb3\xc7\xfd\x3c\xf8\x3f\x71\x94\x82\x23\xd1\x72\x00\xb7\x42\x99\x6c\xc0\x60\x7b\x0f\x79\xea\xa8\x53\x4b\x56\x31\xfa\x0b\x52\x9f\x96\xdb\xcd\x2f\xd7\x67\xcd\x04\x19\x85\xfe\xdb\x02\x9a\x59\x03\xad\x63\x3c\xea\xff\x2e\x18\xfd\x00\xd9\xe2\x56\x60\x59\x93\xb9\xb6\xb2\x3e\x3c\x2c\xab\x0f\xa7\xb2\x89\x43\xc7\xf6\xd5\xce\x2e\xad\xa6\xa9\xed\xa6\xc6\x5a\xb4\xa6\x67\xdf\x8c\x26\x7b\x50\x5a\x91\x08\x2e\x6d\xd4\x3a\xc1\x9d\xf2\xdb\xde\x1e\xb2\x2c\x6c\xa5\x64\xc9\x16\xb4\x90\xaa\x4a\xb7\x0c\xde\x13\xc3\x2a\x9a\x11\x9b\x7a\x1b\x3d\x95\x97\x37\x31\x6b\x69\x7e\x34\xc0\x67\x1f\x66\x19\x49\xef\xf1\x25\xf5\xac\x0e\xea\x0a\x28\x8d\x4d\x7e\xd9\x57\x4b\x49\xe5\xc6\xb3\x25\xfd\xe6\x57\x42\x25\xac\xcd\xcf\x36\x74\x8e\xca\x24\x47\xe7\x80\xa8\x92\x72\xbd\x3d\x84\x2d\x65\xe2\x82\x1a\x9c\xc4\x44\x92\x1b\x10\x79\x8a\xc4\x4a\x2f\x60\x51\x04\x81\xaa\xf0\xa3\x95\x27\xd7\x12\x7b\xa3\x96\x03\x45\x96\xc1\x8a\x07\xc9\xb2\xb0\x95\x52\x8c\xef\x48\x9c\xc6\x7e\x94\xca\xc2\x0e\x07\x12\x44\xa9\x20\x37\xf0\xae\x0f\x49\xa3\x96\x9d\x4b\x42\x7b\x70\x59\x14\xee\xe0\xb2\x0f\x49\xa3\x96\x4b\x97\xbf\x00\x5d\x4b\x4f\xfc\xbb\x2b\xee\x92\xb9\x17\xb5\xaa\xb8\x0b\x97\x17\x9b\x43\xfd\xd6\xc2\xb2\xc2\x2e\x29\xcf\xfd\x87\x4a\x55\xda\x25\x63\x1f\x5a\x65\x69\x2b\x2d\x3d\x67\xe9\x41\xae\x5e\xc1\x6e\x2b\xd4\xdb\x3e\xa8\xd3\x26\xd2\x48\x92\x24\xca\x61\x86\x8f\x8c\xbb\xf2\x8e\x91\xdf\x1f\x06\x19\x33\xf3\x03\x4d\xba\xcd\xe2\x2d\xfb\x69\xe9\x16\x15\x13\xd5\x56\x85\x4e\x3c\x5b\x8a\xbf\x25\x72\x83\xee\x5e\x20\x22\xf2\xc8\xaa\x7b\xdb\x8e\xe4\x29\x58\xca\x38\xb7\x3f\x2e\x59\xb8\xbd\xa8\x16\x16\xf7\xdb\x79\x51\x9f\x5a\xb4\x8d\x87\x3a\x6e\xbc\x3e\xc5\xb4\xcd\x58\xf9\xf5\x3c\xb9\x6f\x49\xaf\x57\xc1\xfa\x1c\x5d\x6d\x88\x8a\x8b\xd3\x28\xcc\xb7\xef\x10\x8a\x4a\x74\xa9\x4a\xa7\x62\xbf\x0d\x76\x23\x6f\x59\xd9\x31\xee\x40\x11\xfb\x28\xec\x8d\x22\x1c\x13\x5a\x64\x94\x23\x16\x60\xbb\xd2\x7c\xa0\x98\xb2\xe5\x6e\xbc\x54\x33\xe0\x3e\xb9\x52\x17\xdb\xb7\x1b\xc8\x12\x20\x8c\x23\xca\x64\x7e\x78\xa3\x62\x5b\x75\x56\xd9\x9e\x2a\x91\x27\xb0\x70\x34\x1f\x90\x89\xb5\x86\x73\x7e\x71\xda\x1e\xfb\x3a\x72\xdc\x5e\x79\x88\xcb\x74\x79\xd9\x64\xe4\xd4\xc2\x9e\xce\xb1\xfe\x85\x5a\xc0\xe9\x0c\x34\x3d\xd0\x43\xce\xa1\x36\x39\xd5\xa1\x4e\xf5\xf8\xb1\xa9\x23\x08\x75\x84\xac\x53\x6c\x3a\xc5\xa6\x53\x6c\x3a\xc5\xa6\x7f\xc5\xd8\xf4\x51\xfd\xff\x25\x4e\xfa\x33\x05\xbe\x9d\x60\xd2\x04\x93\x6a\x5f\x33\x9b\x98\x50\xd2\xe1\x50\x52\xc6\xcc\xdb\x38\x91\xdb\xe6\xaa\xa2\x8f\xa1\x6a\xa6\xd4\xc6\x56\xd6\x8c\x40\x02\x68\x48\xe8\x1a\xe1\x9a\xd9\x2e\xb7\x05\xc3\x34\xda\x2a\xbb\xcd\x12\x36\x98\x22\x50\x4c\xa1\x1b\xc5\xd5\x84\xf0\xbe\x24\x84\xf7\x2f\x22\x37\xef\x94\xd7\x9f\xa0\xde\x04\xf5\x26\xa8\x37\x41\x3d\x64\x40\x3d\xe5\xf2\xde\x60\x89\x27\xb4\x37\xa1\xbd\xda\xd7\xd2\x2c\x26\xc0\x37\x01\x3e\x1b\xef\x5f\x06\xe0\x6b\x7c\x5c\x91\x08\x26\x10\x38\x81\xc0\x09\x04\x76\x4a\x3d\x81\xc0\xbf\x12\x08\x4c\xb0\xdc\x7c\x99\x00\xd0\x75\x70\xb4\xf8\x5a\x7c\xea\xde\x3e\x39\x08\x30\x5a\x27\x35\xed\xb4\x65\xad\x69\x74\x10\x88\x79\xe2\x30\x52\x19\xd6\x04\x21\xa7\x95\xd5\x0e\x03\xf8\xda\x20\xd7\x84\xb4\x26\xa4\x35\x21\xad\x09\x69\x21\x03\x69\x51\x46\xff\xff\x18\x9b\x54\xed\x87\x47\x06\x9d\x4e\x73\x6e\x9a\xb3\xa9\xce\x83\x5e\x4b\xc6\x71\x20\x45\xd7\x72\xf5\x40\x72\x0e\x34\x6c\xf4\x6c\xf3\xba\x5e\x4b\x97\x0e\x52\xb8\xbe\x8b\x79\xa0\x10\x86\xa1\x75\xb0\x6f\xec\xc8\xf4\x3d\x4d\x7b\x86\xc2\x02\x31\x12\x51\xbf\x07\x94\xad\x10\xd6\x2e\x79\xcf\xe9\x1c\xf5\x1e\x31\x23\x5c\x18\xfb\x9c\xfb\x70\xe0\x62\xbd\xf7\xb5\x94\xcf\xf3\xf6\xfa\xc5\x4e\x9c\x85\x76\x1d\xae\x37\xbc\xde\xa3\x41\xcb\x29\xd0\x5e\x70\x67\x50\x93\x6d\x98\xa8\xd3\x67\x0f\x68\xb1\xeb\x38\x47\x07\x10\x1b\xd2\xe2\x18\x68\x6d\x40\xbb\xa3\x40\xba\x21\xf2\x8e\x81\xfb\xf6\x92\x77\x2f\x70\xe8\xdb\xb2\x36\xbf\x30\x91\xc5\x21\xe7\x45\xcc\x34\x0c\x48\x8e\xd0\xf2\x9b\x7c\x3c\xbd\x1c\x04\x3e\x07\xe8\x7c\x2f\x84\x7a\x48\x4d\x1f\xba\xe1\x76\x45\x7b\x60\xe0\x01\xca\xee\x04\xca\x31\xbe\x73\x5f\xa3\x70\x0c\xad\x1f\xa5\xf5\x76\xd5\xbb\xd2\x7e\xfb\x30\x90\xcf\xfa\x67\x7a\xe6\xc3\x37\x42\x19\xe2\xc9\x9c\x61\x4c\xe7\xd1\x77\x55\x86\x6e\x8f\x7b\x85\x42\x33\xa3\xaa\x57\xae\xfd\xd5\xcc\x9c\x56\x68\xe2\xde\x0e\xa8\x2c\xa9\xb0\x7d\xf0\x54\x2d\x80\xf2\x48\x39\x3d\x98\x1a\x6d\x0b\x9d\xba\x53\xfb\xce\xf8\xd1\x7e\xbb\x60\x4f\x06\xf5\xce\xda\xab\xeb\xca\xcb\xd5\xac\x20\xda\x72\x3b\xa2\x4b\x38\xd7\xb5\x89\xbe\x42\xd9\xb9\x73\xc4\x0c\x6d\xb7\xd9\xf8\x8d\xbd\x3e\x9c\xf5\x53\x68\x48\x14\x36\x8f\x09\xc5\x92\xf1\x21\xd1\x09\x07\x1c\xbe\xa7\x91\xf3\x6a\xc8\xc1\x57\xb0\xdd\xc5\xc6\x1d\xad\x76\x1d\xa8\x82\x0e\x4c\x38\xfe\xa5\x8c\xc5\x0a\x40\x5d\xa1\xbb\x98\xd1\xfb\x74\x61\xed\x1a\x98\xaf\x3c\x8c\x1e\xe3\xc2\x92\x29\x74\x3e\x99\xd0\xf9\x41\x50\xd0\x38\x4b\x57\x7e\x5b\x7a\x0e\xe6\xce\x4e\xd7\x19\x35\x57\xbb\x3c\x3c\xd2\x5e\x4f\x4b\x4c\xf7\x0f\x4d\x2b\x91\x5d\x94\xa6\x95\xc8\x69\x25\x72\x5a\x89\x7c\xb8\x95\xc8\x07\x80\x8c\xda\x9c\x64\x7b\xb7\x71\xdf\x57\x12\x4b\x9a\x1f\x72\x0c\x13\x03\xad\x3c\xd5\x4e\xde\x8e\x57\x13\x6d\x34\x86\xcf\x97\xe6\xa4\x68\xc4\xb0\xf6\xc9\xc2\xeb\x8d\x0b\xd7\xcd\xfe\xba\xa6\xf5\x30\xeb\x30\x33\xbe\xc7\x56\x27\xab\x08\xd9\x6d\xbb\x09\xee\x7c\x2d\xcf\xee\x87\x38\xac\xc8\xdd\x90\x9a\x58\x4a\x4e\x96\xa9\x79\x79\xf3\xde\x20\xf0\x96\xe3\x24\x19\xeb\xba\xf2\x53\x19\xab\x12\xaf\x47\xb3\xa0\x3e\xef\x9b\x8d\x6d\x6d\x7b\xde\x3b\x3b\x1a\xc0\x3f\x95\x7e\xed\x78\xfb\x76\xb8\xaf\xb3\xdd\xc5\xeb\x95\xed\x5a\x62\x41\x82\xb3\x54\x6e\x80\x4a\x92\x6f\x36\xbd\x34\xae\xde\x6f\xa4\xc0\xbc\x08\xe3\x84\xfc\x1d\xb6\xe3\xd0\x62\x38\x95\x9b\x57\xe7\x71\x12\x91\x80\xc8\x31\x69\x5e\x60\x21\x6e\x19\x0f\xc7\xa4\x79\x96\x28\x3e\x47\x54\x65\x41\x36\x08\x40\x88\x1f\x58\x08\x56\xaa\xd5\xbf\xaf\xad\x96\xd7\xd6\xcf\x87\xf5\x34\x0f\x71\x93\x6e\x26\xed\x98\x5b\x9f\x4f\xcf\x95\x34\xc6\xd7\x11\xfa\xb0\x81\x22\x1a\xdb\xdf\x8e\xdc\xc3\xb9\xf8\xdd\x5d\x3c\x74\xe6\xea\xb7\x8b\xbf\xf5\x6e\xb3\x46\x2e\x64\xf4\xab\x3c\x4e\xcf\x36\x1d\xfe\xfa\xb8\x36\xba\x8a\xd8\xad\xf6\xc6\x41\x2a\x37\x8c\x17\x0f\xda\xfe\xda\xe7\x65\xbc\x71\x2c\x36\x57\x8a\x47\x12\x4c\xf1\xbd\x77\x6b\xa4\x50\x7e\x77\x7b\x22\x60\x89\xef\xcd\xf5\xb9\x0c\x97\x79\x0d\x2b\x35\x43\xcb\x3d\x24\xf1\x78\xfc\xf8\xcb\x1f\x15\x06\xe2\x78\xd8\x51\x21\xd9\x1f\xf0\xf5\x8f\x86\xa4\x50\xfa\xb1\x47\x43\xa5\xdd\x69\x14\xe8\xa3\xc0\x86\x91\xa7\x81\x50\xb4\x7c\xc0\x81\x80\x77\x7a\x9f\xc6\xc2\xa9\x8c\x05\x33\xb0\x3b\x31\xa4\xf4\xd7\x1b\x26\x55\x97\x7c\x65\xf8\x69\x1a\x84\x8e\x41\x78\xd9\xec\xc5\x11\x16\x1e\x74\x91\xf5\x56\xf5\x57\x49\x47\x5c\x92\xa9\x1e\x99\x36\xf4\xdb\xb1\x0e\xd3\x78\x02\xb0\x9b\x25\xcb\xe9\xe9\x1d\x0d\x44\x01\x42\x08\x91\x64\xd9\xdd\x37\x08\x17\xef\xf9\xe5\x0f\xbd\x46\x91\xf5\xf9\x89\x92\x37\xdd\x89\x59\x44\x1f\x9c\xee\x34\x1e\xbe\x47\x83\x32\x72\x8e\x37\xdf\xac\x69\x38\xef\x75\xb0\xda\xdb\xac\x83\x94\x2f\x39\xa6\x62\x05\x1c\x25\x9c\x49\x16\xb0\xa8\x3c\xc7\x7e\x76\x71\x3e\x6f\xb5\x24\xe7\xe8\xb7\xb9\xc7\x6c\x43\x92\xee\x21\xd4\x17\xa1\x7f\xba\x35\xfe\xae\x39\xbc\xde\xba\x69\xd9\x8e\xe1\x62\xde\x64\x7d\x16\x88\x1b\xed\x29\x11\xfd\x4f\xa9\xff\x99\x90\xc4\xf6\xf4\xf9\x6e\xe9\x28\x23\xd7\xca\xe5\xee\xee\x9f\x63\xb1\x5b\xfb\x10\xd7\x2f\x1d\xf2\xe3\xbf\xb9\xb5\x6f\xa4\x6d\x7d\x25\x79\xfb\x24\x31\xea\x56\xbe\x5d\x53\xcd\x2d\x36\xa3\x6d\xdf\xab\x1c\xb8\x6d\x6f\xc0\x98\xa7\xdd\xaa\x86\x8c\x1d\x39\xa3\x9d\x70\x2b\x9b\x68\xd9\xfd\x33\xfe\xa9\xb6\x4a\x2e\x63\x0f\xcf\x68\x27\xd9\x4c\xb9\x46\x6d\xcb\xbe\xa1\xa8\xd6\x5f\xc6\xd6\x9f\xf1\x4f\xf4\xd4\xb4\x78\xd0\xd6\xf4\x13\x3c\x3b\xac\xd0\xdc\x90\x34\xda\xc9\xb4\x9a\x1a\x8d\xbd\x93\x87\xd4\xe2\x21\x1b\xb3\x2b\xd1\xbe\xe7\x69\xd4\x53\x67\xd5\x40\xa0\xe3\x19\x3f\x6d\x1a\xbc\x0e\x86\x3c\x10\xb4\x3d\x2a\xcd\x78\x32\xe6\xab\xbd\x36\xc9\xf4\x3a\x58\xae\xc3\xf4\x47\xea\xbf\xfb\x47\xff\x0d\x00\x00\xff\xff\xd2\x32\x5a\x28\x38\x9d\x00\x00") + +func v2SchemaJsonBytes() ([]byte, error) { + return bindataRead( + _v2SchemaJson, + "v2/schema.json", + ) +} + +func v2SchemaJson() (*asset, error) { + bytes, err := v2SchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "v2/schema.json", size: 40248, mode: os.FileMode(0640), modTime: time.Unix(1568964748, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xab, 0x88, 0x5e, 0xf, 0xbf, 0x17, 0x74, 0x0, 0xb2, 0x5a, 0x7f, 0xbc, 0x58, 0xcd, 0xc, 0x25, 0x73, 0xd5, 0x29, 0x1c, 0x7a, 0xd0, 0xce, 0x79, 0xd4, 0x89, 0x31, 0x27, 0x90, 0xf2, 0xff, 0xe6}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "jsonschema-draft-04.json": jsonschemaDraft04Json, + + "v2/schema.json": v2SchemaJson, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "jsonschema-draft-04.json": {jsonschemaDraft04Json, map[string]*bintree{}}, + "v2": {nil, map[string]*bintree{ + "schema.json": {v2SchemaJson, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} diff --git a/vendor/github.com/go-openapi/spec/cache.go b/vendor/github.com/go-openapi/spec/cache.go new file mode 100644 index 00000000..122993b4 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/cache.go @@ -0,0 +1,98 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "sync" +) + +// ResolutionCache a cache for resolving urls +type ResolutionCache interface { + Get(string) (interface{}, bool) + Set(string, interface{}) +} + +type simpleCache struct { + lock sync.RWMutex + store map[string]interface{} +} + +func (s *simpleCache) ShallowClone() ResolutionCache { + store := make(map[string]interface{}, len(s.store)) + s.lock.RLock() + for k, v := range s.store { + store[k] = v + } + s.lock.RUnlock() + + return &simpleCache{ + store: store, + } +} + +// Get retrieves a cached URI +func (s *simpleCache) Get(uri string) (interface{}, bool) { + s.lock.RLock() + v, ok := s.store[uri] + + s.lock.RUnlock() + return v, ok +} + +// Set caches a URI +func (s *simpleCache) Set(uri string, data interface{}) { + s.lock.Lock() + s.store[uri] = data + s.lock.Unlock() +} + +var ( + // resCache is a package level cache for $ref resolution and expansion. + // It is initialized lazily by methods that have the need for it: no + // memory is allocated unless some expander methods are called. + // + // It is initialized with JSON schema and swagger schema, + // which do not mutate during normal operations. + // + // All subsequent utilizations of this cache are produced from a shallow + // clone of this initial version. + resCache *simpleCache + onceCache sync.Once + + _ ResolutionCache = &simpleCache{} +) + +// initResolutionCache initializes the URI resolution cache. To be wrapped in a sync.Once.Do call. +func initResolutionCache() { + resCache = defaultResolutionCache() +} + +func defaultResolutionCache() *simpleCache { + return &simpleCache{store: map[string]interface{}{ + "http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(), + "http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(), + }} +} + +func cacheOrDefault(cache ResolutionCache) ResolutionCache { + onceCache.Do(initResolutionCache) + + if cache != nil { + return cache + } + + // get a shallow clone of the base cache with swagger and json schema + return resCache.ShallowClone() +} diff --git a/vendor/github.com/go-openapi/spec/contact_info.go b/vendor/github.com/go-openapi/spec/contact_info.go new file mode 100644 index 00000000..2f7bb219 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/contact_info.go @@ -0,0 +1,57 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/swag" +) + +// ContactInfo contact information for the exposed API. +// +// For more information: http://goo.gl/8us55a#contactObject +type ContactInfo struct { + ContactInfoProps + VendorExtensible +} + +// ContactInfoProps hold the properties of a ContactInfo object +type ContactInfoProps struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Email string `json:"email,omitempty"` +} + +// UnmarshalJSON hydrates ContactInfo from json +func (c *ContactInfo) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &c.ContactInfoProps); err != nil { + return err + } + return json.Unmarshal(data, &c.VendorExtensible) +} + +// MarshalJSON produces ContactInfo as json +func (c ContactInfo) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(c.ContactInfoProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(c.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} diff --git a/vendor/github.com/go-openapi/spec/debug.go b/vendor/github.com/go-openapi/spec/debug.go new file mode 100644 index 00000000..fc889f6d --- /dev/null +++ b/vendor/github.com/go-openapi/spec/debug.go @@ -0,0 +1,49 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "fmt" + "log" + "os" + "path" + "runtime" +) + +// Debug is true when the SWAGGER_DEBUG env var is not empty. +// +// It enables a more verbose logging of this package. +var Debug = os.Getenv("SWAGGER_DEBUG") != "" + +var ( + // specLogger is a debug logger for this package + specLogger *log.Logger +) + +func init() { + debugOptions() +} + +func debugOptions() { + specLogger = log.New(os.Stdout, "spec:", log.LstdFlags) +} + +func debugLog(msg string, args ...interface{}) { + // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() + if Debug { + _, file1, pos1, _ := runtime.Caller(1) + specLogger.Printf("%s:%d: %s", path.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } +} diff --git a/vendor/github.com/go-openapi/spec/errors.go b/vendor/github.com/go-openapi/spec/errors.go new file mode 100644 index 00000000..6992c7ba --- /dev/null +++ b/vendor/github.com/go-openapi/spec/errors.go @@ -0,0 +1,19 @@ +package spec + +import "errors" + +// Error codes +var ( + // ErrUnknownTypeForReference indicates that a resolved reference was found in an unsupported container type + ErrUnknownTypeForReference = errors.New("unknown type for the resolved reference") + + // ErrResolveRefNeedsAPointer indicates that a $ref target must be a valid JSON pointer + ErrResolveRefNeedsAPointer = errors.New("resolve ref: target needs to be a pointer") + + // ErrDerefUnsupportedType indicates that a resolved reference was found in an unsupported container type. + // At the moment, $ref are supported only inside: schemas, parameters, responses, path items + ErrDerefUnsupportedType = errors.New("deref: unsupported type") + + // ErrExpandUnsupportedType indicates that $ref expansion is attempted on some invalid type + ErrExpandUnsupportedType = errors.New("expand: unsupported type. Input should be of type *Parameter or *Response") +) diff --git a/vendor/github.com/go-openapi/spec/expander.go b/vendor/github.com/go-openapi/spec/expander.go new file mode 100644 index 00000000..d4ea889d --- /dev/null +++ b/vendor/github.com/go-openapi/spec/expander.go @@ -0,0 +1,594 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "fmt" +) + +// ExpandOptions provides options for the spec expander. +// +// RelativeBase is the path to the root document. This can be a remote URL or a path to a local file. +// +// If left empty, the root document is assumed to be located in the current working directory: +// all relative $ref's will be resolved from there. +// +// PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable. +// +type ExpandOptions struct { + RelativeBase string // the path to the root document to expand. This is a file, not a directory + SkipSchemas bool // do not expand schemas, just paths, parameters and responses + ContinueOnError bool // continue expanding even after and error is found + PathLoader func(string) (json.RawMessage, error) `json:"-"` // the document loading method that takes a path as input and yields a json document + AbsoluteCircularRef bool // circular $ref remaining after expansion remain absolute URLs +} + +func optionsOrDefault(opts *ExpandOptions) *ExpandOptions { + if opts != nil { + clone := *opts // shallow clone to avoid internal changes to be propagated to the caller + if clone.RelativeBase != "" { + clone.RelativeBase = normalizeBase(clone.RelativeBase) + } + // if the relative base is empty, let the schema loader choose a pseudo root document + return &clone + } + return &ExpandOptions{} +} + +// ExpandSpec expands the references in a swagger spec +func ExpandSpec(spec *Swagger, options *ExpandOptions) error { + options = optionsOrDefault(options) + resolver := defaultSchemaLoader(spec, options, nil, nil) + + specBasePath := options.RelativeBase + + if !options.SkipSchemas { + for key, definition := range spec.Definitions { + parentRefs := make([]string, 0, 10) + parentRefs = append(parentRefs, fmt.Sprintf("#/definitions/%s", key)) + + def, err := expandSchema(definition, parentRefs, resolver, specBasePath) + if resolver.shouldStopOnError(err) { + return err + } + if def != nil { + spec.Definitions[key] = *def + } + } + } + + for key := range spec.Parameters { + parameter := spec.Parameters[key] + if err := expandParameterOrResponse(¶meter, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Parameters[key] = parameter + } + + for key := range spec.Responses { + response := spec.Responses[key] + if err := expandParameterOrResponse(&response, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Responses[key] = response + } + + if spec.Paths != nil { + for key := range spec.Paths.Paths { + pth := spec.Paths.Paths[key] + if err := expandPathItem(&pth, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Paths.Paths[key] = pth + } + } + + return nil +} + +const rootBase = ".root" + +// baseForRoot loads in the cache the root document and produces a fake ".root" base path entry +// for further $ref resolution +// +// Setting the cache is optional and this parameter may safely be left to nil. +func baseForRoot(root interface{}, cache ResolutionCache) string { + if root == nil { + return "" + } + + // cache the root document to resolve $ref's + normalizedBase := normalizeBase(rootBase) + cache.Set(normalizedBase, root) + + return normalizedBase +} + +// ExpandSchema expands the refs in the schema object with reference to the root object. +// +// go-openapi/validate uses this function. +// +// Notice that it is impossible to reference a json schema in a different document other than root +// (use ExpandSchemaWithBasePath to resolve external references). +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { + cache = cacheOrDefault(cache) + if root == nil { + root = schema + } + + opts := &ExpandOptions{ + // when a root is specified, cache the root as an in-memory document for $ref retrieval + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + } + + return ExpandSchemaWithBasePath(schema, cache, opts) +} + +// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options. +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error { + if schema == nil { + return nil + } + + cache = cacheOrDefault(cache) + + opts = optionsOrDefault(opts) + + resolver := defaultSchemaLoader(nil, opts, cache, nil) + + parentRefs := make([]string, 0, 10) + s, err := expandSchema(*schema, parentRefs, resolver, opts.RelativeBase) + if err != nil { + return err + } + if s != nil { + // guard for when continuing on error + *schema = *s + } + + return nil +} + +func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Items == nil { + return &target, nil + } + + // array + if target.Items.Schema != nil { + t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + *target.Items.Schema = *t + } + + // tuple + for i := range target.Items.Schemas { + t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + target.Items.Schemas[i] = *t + } + + return &target, nil +} + +func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Ref.String() == "" && target.Ref.IsRoot() { + newRef := normalizeRef(&target.Ref, basePath) + target.Ref = *newRef + return &target, nil + } + + // change the base path of resolution when an ID is encountered + // otherwise the basePath should inherit the parent's + if target.ID != "" { + basePath, _ = resolver.setSchemaID(target, target.ID, basePath) + } + + if target.Ref.String() != "" { + return expandSchemaRef(target, parentRefs, resolver, basePath) + } + + for k := range target.Definitions { + tt, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if tt != nil { + target.Definitions[k] = *tt + } + } + + t, err := expandItems(target, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target = *t + } + + for i := range target.AllOf { + t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.AllOf[i] = *t + } + } + + for i := range target.AnyOf { + t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.AnyOf[i] = *t + } + } + + for i := range target.OneOf { + t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.OneOf[i] = *t + } + } + + if target.Not != nil { + t, err := expandSchema(*target.Not, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.Not = *t + } + } + + for k := range target.Properties { + t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.Properties[k] = *t + } + } + + if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil { + t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.AdditionalProperties.Schema = *t + } + } + + for k := range target.PatternProperties { + t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.PatternProperties[k] = *t + } + } + + for k := range target.Dependencies { + if target.Dependencies[k].Schema != nil { + t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.Dependencies[k].Schema = *t + } + } + } + + if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil { + t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.AdditionalItems.Schema = *t + } + } + return &target, nil +} + +func expandSchemaRef(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + // if a Ref is found, all sibling fields are skipped + // Ref also changes the resolution scope of children expandSchema + + // here the resolution scope is changed because a $ref was encountered + normalizedRef := normalizeRef(&target.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + // this means there is a cycle in the recursion tree: return the Ref + // - circular refs cannot be expanded. We leave them as ref. + // - denormalization means that a new local file ref is set relative to the original basePath + debugLog("short circuit circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s", + basePath, normalizedBasePath, normalizedRef.String()) + if !resolver.options.AbsoluteCircularRef { + target.Ref = denormalizeRef(normalizedRef, resolver.context.basePath, resolver.context.rootID) + } else { + target.Ref = *normalizedRef + } + return &target, nil + } + + var t *Schema + err := resolver.Resolve(&target.Ref, &t, basePath) + if resolver.shouldStopOnError(err) { + return nil, err + } + + if t == nil { + // guard for when continuing on error + return &target, nil + } + + parentRefs = append(parentRefs, normalizedRef.String()) + transitiveResolver := resolver.transitiveResolver(basePath, target.Ref) + + basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath) + + return expandSchema(*t, parentRefs, transitiveResolver, basePath) +} + +func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error { + if pathItem == nil { + return nil + } + + parentRefs := make([]string, 0, 10) + if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) { + return err + } + + if pathItem.Ref.String() != "" { + transitiveResolver := resolver.transitiveResolver(basePath, pathItem.Ref) + basePath = transitiveResolver.updateBasePath(resolver, basePath) + resolver = transitiveResolver + } + + pathItem.Ref = Ref{} + for i := range pathItem.Parameters { + if err := expandParameterOrResponse(&(pathItem.Parameters[i]), resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + } + + ops := []*Operation{ + pathItem.Get, + pathItem.Head, + pathItem.Options, + pathItem.Put, + pathItem.Post, + pathItem.Patch, + pathItem.Delete, + } + for _, op := range ops { + if err := expandOperation(op, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + } + + return nil +} + +func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error { + if op == nil { + return nil + } + + for i := range op.Parameters { + param := op.Parameters[i] + if err := expandParameterOrResponse(¶m, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + op.Parameters[i] = param + } + + if op.Responses == nil { + return nil + } + + responses := op.Responses + if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + + for code := range responses.StatusCodeResponses { + response := responses.StatusCodeResponses[code] + if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + responses.StatusCodeResponses[code] = response + } + + return nil +} + +// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document +// +// Notice that it is impossible to reference a json schema in a different document other than root +// (use ExpandResponse to resolve external references). +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error { + cache = cacheOrDefault(cache) + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + } + resolver := defaultSchemaLoader(root, opts, cache, nil) + + return expandParameterOrResponse(response, resolver, opts.RelativeBase) +} + +// ExpandResponse expands a response based on a basepath +// +// All refs inside response will be resolved relative to basePath +func ExpandResponse(response *Response, basePath string) error { + opts := optionsOrDefault(&ExpandOptions{ + RelativeBase: basePath, + }) + resolver := defaultSchemaLoader(nil, opts, nil, nil) + + return expandParameterOrResponse(response, resolver, opts.RelativeBase) +} + +// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document. +// +// Notice that it is impossible to reference a json schema in a different document other than root +// (use ExpandParameter to resolve external references). +func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error { + cache = cacheOrDefault(cache) + + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + } + resolver := defaultSchemaLoader(root, opts, cache, nil) + + return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) +} + +// ExpandParameter expands a parameter based on a basepath. +// This is the exported version of expandParameter +// all refs inside parameter will be resolved relative to basePath +func ExpandParameter(parameter *Parameter, basePath string) error { + opts := optionsOrDefault(&ExpandOptions{ + RelativeBase: basePath, + }) + resolver := defaultSchemaLoader(nil, opts, nil, nil) + + return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) +} + +func getRefAndSchema(input interface{}) (*Ref, *Schema, error) { + var ( + ref *Ref + sch *Schema + ) + + switch refable := input.(type) { + case *Parameter: + if refable == nil { + return nil, nil, nil + } + ref = &refable.Ref + sch = refable.Schema + case *Response: + if refable == nil { + return nil, nil, nil + } + ref = &refable.Ref + sch = refable.Schema + default: + return nil, nil, fmt.Errorf("unsupported type: %T: %w", input, ErrExpandUnsupportedType) + } + + return ref, sch, nil +} + +func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error { + ref, _, err := getRefAndSchema(input) + if err != nil { + return err + } + + if ref == nil { + return nil + } + + parentRefs := make([]string, 0, 10) + if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) { + return err + } + + ref, sch, _ := getRefAndSchema(input) + if ref.String() != "" { + transitiveResolver := resolver.transitiveResolver(basePath, *ref) + basePath = resolver.updateBasePath(transitiveResolver, basePath) + resolver = transitiveResolver + } + + if sch == nil { + // nothing to be expanded + if ref != nil { + *ref = Ref{} + } + return nil + } + + if sch.Ref.String() != "" { + rebasedRef, ern := NewRef(normalizeURI(sch.Ref.String(), basePath)) + if ern != nil { + return ern + } + + switch { + case resolver.isCircular(&rebasedRef, basePath, parentRefs...): + // this is a circular $ref: stop expansion + if !resolver.options.AbsoluteCircularRef { + sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID) + } else { + sch.Ref = rebasedRef + } + case !resolver.options.SkipSchemas: + // schema expanded to a $ref in another root + sch.Ref = rebasedRef + debugLog("rebased to: %s", sch.Ref.String()) + default: + // skip schema expansion but rebase $ref to schema + sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID) + } + } + + if ref != nil { + *ref = Ref{} + } + + // expand schema + if !resolver.options.SkipSchemas { + s, err := expandSchema(*sch, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return err + } + if s == nil { + // guard for when continuing on error + return nil + } + *sch = *s + } + + return nil +} diff --git a/vendor/github.com/go-openapi/spec/external_docs.go b/vendor/github.com/go-openapi/spec/external_docs.go new file mode 100644 index 00000000..88add91b --- /dev/null +++ b/vendor/github.com/go-openapi/spec/external_docs.go @@ -0,0 +1,24 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +// ExternalDocumentation allows referencing an external resource for +// extended documentation. +// +// For more information: http://goo.gl/8us55a#externalDocumentationObject +type ExternalDocumentation struct { + Description string `json:"description,omitempty"` + URL string `json:"url,omitempty"` +} diff --git a/vendor/github.com/go-openapi/spec/header.go b/vendor/github.com/go-openapi/spec/header.go new file mode 100644 index 00000000..9dfd17b1 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/header.go @@ -0,0 +1,203 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + jsonArray = "array" +) + +// HeaderProps describes a response header +type HeaderProps struct { + Description string `json:"description,omitempty"` +} + +// Header describes a header for a response of the API +// +// For more information: http://goo.gl/8us55a#headerObject +type Header struct { + CommonValidations + SimpleSchema + VendorExtensible + HeaderProps +} + +// ResponseHeader creates a new header instance for use in a response +func ResponseHeader() *Header { + return new(Header) +} + +// WithDescription sets the description on this response, allows for chaining +func (h *Header) WithDescription(description string) *Header { + h.Description = description + return h +} + +// Typed a fluent builder method for the type of parameter +func (h *Header) Typed(tpe, format string) *Header { + h.Type = tpe + h.Format = format + return h +} + +// CollectionOf a fluent builder method for an array item +func (h *Header) CollectionOf(items *Items, format string) *Header { + h.Type = jsonArray + h.Items = items + h.CollectionFormat = format + return h +} + +// WithDefault sets the default value on this item +func (h *Header) WithDefault(defaultValue interface{}) *Header { + h.Default = defaultValue + return h +} + +// WithMaxLength sets a max length value +func (h *Header) WithMaxLength(max int64) *Header { + h.MaxLength = &max + return h +} + +// WithMinLength sets a min length value +func (h *Header) WithMinLength(min int64) *Header { + h.MinLength = &min + return h +} + +// WithPattern sets a pattern value +func (h *Header) WithPattern(pattern string) *Header { + h.Pattern = pattern + return h +} + +// WithMultipleOf sets a multiple of value +func (h *Header) WithMultipleOf(number float64) *Header { + h.MultipleOf = &number + return h +} + +// WithMaximum sets a maximum number value +func (h *Header) WithMaximum(max float64, exclusive bool) *Header { + h.Maximum = &max + h.ExclusiveMaximum = exclusive + return h +} + +// WithMinimum sets a minimum number value +func (h *Header) WithMinimum(min float64, exclusive bool) *Header { + h.Minimum = &min + h.ExclusiveMinimum = exclusive + return h +} + +// WithEnum sets a the enum values (replace) +func (h *Header) WithEnum(values ...interface{}) *Header { + h.Enum = append([]interface{}{}, values...) + return h +} + +// WithMaxItems sets the max items +func (h *Header) WithMaxItems(size int64) *Header { + h.MaxItems = &size + return h +} + +// WithMinItems sets the min items +func (h *Header) WithMinItems(size int64) *Header { + h.MinItems = &size + return h +} + +// UniqueValues dictates that this array can only have unique items +func (h *Header) UniqueValues() *Header { + h.UniqueItems = true + return h +} + +// AllowDuplicates this array can have duplicates +func (h *Header) AllowDuplicates() *Header { + h.UniqueItems = false + return h +} + +// WithValidations is a fluent method to set header validations +func (h *Header) WithValidations(val CommonValidations) *Header { + h.SetValidations(SchemaValidations{CommonValidations: val}) + return h +} + +// MarshalJSON marshal this to JSON +func (h Header) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(h.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(h.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(h.HeaderProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// UnmarshalJSON unmarshals this header from JSON +func (h *Header) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &h.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &h.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &h.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &h.HeaderProps) +} + +// JSONLookup look up a value by the json property name +func (h Header) JSONLookup(token string) (interface{}, error) { + if ex, ok := h.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(h.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.HeaderProps, token) + return r, err +} diff --git a/vendor/github.com/go-openapi/spec/info.go b/vendor/github.com/go-openapi/spec/info.go new file mode 100644 index 00000000..c458b49b --- /dev/null +++ b/vendor/github.com/go-openapi/spec/info.go @@ -0,0 +1,165 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Extensions vendor specific extensions +type Extensions map[string]interface{} + +// Add adds a value to these extensions +func (e Extensions) Add(key string, value interface{}) { + realKey := strings.ToLower(key) + e[realKey] = value +} + +// GetString gets a string value from the extensions +func (e Extensions) GetString(key string) (string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(string) + return str, ok + } + return "", false +} + +// GetBool gets a string value from the extensions +func (e Extensions) GetBool(key string) (bool, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(bool) + return str, ok + } + return false, false +} + +// GetStringSlice gets a string value from the extensions +func (e Extensions) GetStringSlice(key string) ([]string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + arr, isSlice := v.([]interface{}) + if !isSlice { + return nil, false + } + var strs []string + for _, iface := range arr { + str, isString := iface.(string) + if !isString { + return nil, false + } + strs = append(strs, str) + } + return strs, ok + } + return nil, false +} + +// VendorExtensible composition block. +type VendorExtensible struct { + Extensions Extensions +} + +// AddExtension adds an extension to this extensible object +func (v *VendorExtensible) AddExtension(key string, value interface{}) { + if value == nil { + return + } + if v.Extensions == nil { + v.Extensions = make(map[string]interface{}) + } + v.Extensions.Add(key, value) +} + +// MarshalJSON marshals the extensions to json +func (v VendorExtensible) MarshalJSON() ([]byte, error) { + toser := make(map[string]interface{}) + for k, v := range v.Extensions { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + toser[k] = v + } + } + return json.Marshal(toser) +} + +// UnmarshalJSON for this extensible object +func (v *VendorExtensible) UnmarshalJSON(data []byte) error { + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if v.Extensions == nil { + v.Extensions = map[string]interface{}{} + } + v.Extensions[k] = vv + } + } + return nil +} + +// InfoProps the properties for an info definition +type InfoProps struct { + Description string `json:"description,omitempty"` + Title string `json:"title,omitempty"` + TermsOfService string `json:"termsOfService,omitempty"` + Contact *ContactInfo `json:"contact,omitempty"` + License *License `json:"license,omitempty"` + Version string `json:"version,omitempty"` +} + +// Info object provides metadata about the API. +// The metadata can be used by the clients if needed, and can be presented in the Swagger-UI for convenience. +// +// For more information: http://goo.gl/8us55a#infoObject +type Info struct { + VendorExtensible + InfoProps +} + +// JSONLookup look up a value by the json property name +func (i Info) JSONLookup(token string) (interface{}, error) { + if ex, ok := i.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(i.InfoProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (i Info) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.InfoProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (i *Info) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &i.InfoProps); err != nil { + return err + } + return json.Unmarshal(data, &i.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/items.go b/vendor/github.com/go-openapi/spec/items.go new file mode 100644 index 00000000..e2afb213 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/items.go @@ -0,0 +1,234 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + jsonRef = "$ref" +) + +// SimpleSchema describe swagger simple schemas for parameters and headers +type SimpleSchema struct { + Type string `json:"type,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Format string `json:"format,omitempty"` + Items *Items `json:"items,omitempty"` + CollectionFormat string `json:"collectionFormat,omitempty"` + Default interface{} `json:"default,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// TypeName return the type (or format) of a simple schema +func (s *SimpleSchema) TypeName() string { + if s.Format != "" { + return s.Format + } + return s.Type +} + +// ItemsTypeName yields the type of items in a simple schema array +func (s *SimpleSchema) ItemsTypeName() string { + if s.Items == nil { + return "" + } + return s.Items.TypeName() +} + +// Items a limited subset of JSON-Schema's items object. +// It is used by parameter definitions that are not located in "body". +// +// For more information: http://goo.gl/8us55a#items-object +type Items struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible +} + +// NewItems creates a new instance of items +func NewItems() *Items { + return &Items{} +} + +// Typed a fluent builder method for the type of item +func (i *Items) Typed(tpe, format string) *Items { + i.Type = tpe + i.Format = format + return i +} + +// AsNullable flags this schema as nullable. +func (i *Items) AsNullable() *Items { + i.Nullable = true + return i +} + +// CollectionOf a fluent builder method for an array item +func (i *Items) CollectionOf(items *Items, format string) *Items { + i.Type = jsonArray + i.Items = items + i.CollectionFormat = format + return i +} + +// WithDefault sets the default value on this item +func (i *Items) WithDefault(defaultValue interface{}) *Items { + i.Default = defaultValue + return i +} + +// WithMaxLength sets a max length value +func (i *Items) WithMaxLength(max int64) *Items { + i.MaxLength = &max + return i +} + +// WithMinLength sets a min length value +func (i *Items) WithMinLength(min int64) *Items { + i.MinLength = &min + return i +} + +// WithPattern sets a pattern value +func (i *Items) WithPattern(pattern string) *Items { + i.Pattern = pattern + return i +} + +// WithMultipleOf sets a multiple of value +func (i *Items) WithMultipleOf(number float64) *Items { + i.MultipleOf = &number + return i +} + +// WithMaximum sets a maximum number value +func (i *Items) WithMaximum(max float64, exclusive bool) *Items { + i.Maximum = &max + i.ExclusiveMaximum = exclusive + return i +} + +// WithMinimum sets a minimum number value +func (i *Items) WithMinimum(min float64, exclusive bool) *Items { + i.Minimum = &min + i.ExclusiveMinimum = exclusive + return i +} + +// WithEnum sets a the enum values (replace) +func (i *Items) WithEnum(values ...interface{}) *Items { + i.Enum = append([]interface{}{}, values...) + return i +} + +// WithMaxItems sets the max items +func (i *Items) WithMaxItems(size int64) *Items { + i.MaxItems = &size + return i +} + +// WithMinItems sets the min items +func (i *Items) WithMinItems(size int64) *Items { + i.MinItems = &size + return i +} + +// UniqueValues dictates that this array can only have unique items +func (i *Items) UniqueValues() *Items { + i.UniqueItems = true + return i +} + +// AllowDuplicates this array can have duplicates +func (i *Items) AllowDuplicates() *Items { + i.UniqueItems = false + return i +} + +// WithValidations is a fluent method to set Items validations +func (i *Items) WithValidations(val CommonValidations) *Items { + i.SetValidations(SchemaValidations{CommonValidations: val}) + return i +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (i *Items) UnmarshalJSON(data []byte) error { + var validations CommonValidations + if err := json.Unmarshal(data, &validations); err != nil { + return err + } + var ref Refable + if err := json.Unmarshal(data, &ref); err != nil { + return err + } + var simpleSchema SimpleSchema + if err := json.Unmarshal(data, &simpleSchema); err != nil { + return err + } + var vendorExtensible VendorExtensible + if err := json.Unmarshal(data, &vendorExtensible); err != nil { + return err + } + i.Refable = ref + i.CommonValidations = validations + i.SimpleSchema = simpleSchema + i.VendorExtensible = vendorExtensible + return nil +} + +// MarshalJSON converts this items object to JSON +func (i Items) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(i.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b4, b3, b1, b2), nil +} + +// JSONLookup look up a value by the json property name +func (i Items) JSONLookup(token string) (interface{}, error) { + if token == jsonRef { + return &i.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(i.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token) + return r, err +} diff --git a/vendor/github.com/go-openapi/spec/license.go b/vendor/github.com/go-openapi/spec/license.go new file mode 100644 index 00000000..b42f8036 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/license.go @@ -0,0 +1,56 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/swag" +) + +// License information for the exposed API. +// +// For more information: http://goo.gl/8us55a#licenseObject +type License struct { + LicenseProps + VendorExtensible +} + +// LicenseProps holds the properties of a License object +type LicenseProps struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` +} + +// UnmarshalJSON hydrates License from json +func (l *License) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &l.LicenseProps); err != nil { + return err + } + return json.Unmarshal(data, &l.VendorExtensible) +} + +// MarshalJSON produces License as json +func (l License) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(l.LicenseProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(l.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} diff --git a/vendor/github.com/go-openapi/spec/normalizer.go b/vendor/github.com/go-openapi/spec/normalizer.go new file mode 100644 index 00000000..e8b60099 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/normalizer.go @@ -0,0 +1,202 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "net/url" + "path" + "strings" +) + +const fileScheme = "file" + +// normalizeURI ensures that all $ref paths used internally by the expander are canonicalized. +// +// NOTE(windows): there is a tolerance over the strict URI format on windows. +// +// The normalizer accepts relative file URLs like 'Path\File.JSON' as well as absolute file URLs like +// 'C:\Path\file.Yaml'. +// +// Both are canonicalized with a "file://" scheme, slashes and a lower-cased path: +// 'file:///c:/path/file.yaml' +// +// URLs can be specified with a file scheme, like in 'file:///folder/file.json' or +// 'file:///c:\folder\File.json'. +// +// URLs like file://C:\folder are considered invalid (i.e. there is no host 'c:\folder') and a "repair" +// is attempted. +// +// The base path argument is assumed to be canonicalized (e.g. using normalizeBase()). +func normalizeURI(refPath, base string) string { + refURL, err := parseURL(refPath) + if err != nil { + specLogger.Printf("warning: invalid URI in $ref %q: %v", refPath, err) + refURL, refPath = repairURI(refPath) + } + + fixWindowsURI(refURL, refPath) // noop on non-windows OS + + refURL.Path = path.Clean(refURL.Path) + if refURL.Path == "." { + refURL.Path = "" + } + + r := MustCreateRef(refURL.String()) + if r.IsCanonical() { + return refURL.String() + } + + baseURL, _ := parseURL(base) + if path.IsAbs(refURL.Path) { + baseURL.Path = refURL.Path + } else if refURL.Path != "" { + baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path) + } + // copying fragment from ref to base + baseURL.Fragment = refURL.Fragment + + return baseURL.String() +} + +// denormalizeRef returns the simplest notation for a normalized $ref, given the path of the original root document. +// +// When calling this, we assume that: +// * $ref is a canonical URI +// * originalRelativeBase is a canonical URI +// +// denormalizeRef is currently used when we rewrite a $ref after a circular $ref has been detected. +// In this case, expansion stops and normally renders the internal canonical $ref. +// +// This internal $ref is eventually rebased to the original RelativeBase used for the expansion. +// +// There is a special case for schemas that are anchored with an "id": +// in that case, the rebasing is performed // against the id only if this is an anchor for the initial root document. +// All other intermediate "id"'s found along the way are ignored for the purpose of rebasing. +func denormalizeRef(ref *Ref, originalRelativeBase, id string) Ref { + debugLog("denormalizeRef called:\n$ref: %q\noriginal: %s\nroot ID:%s", ref.String(), originalRelativeBase, id) + + if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly { + // short circuit: $ref to current doc + return *ref + } + + if id != "" { + idBaseURL, err := parseURL(id) + if err == nil { // if the schema id is not usable as a URI, ignore it + if ref, ok := rebase(ref, idBaseURL, true); ok { // rebase, but keep references to root unchaged (do not want $ref: "") + // $ref relative to the ID of the schema in the root document + return ref + } + } + } + + originalRelativeBaseURL, _ := parseURL(originalRelativeBase) + + r, _ := rebase(ref, originalRelativeBaseURL, false) + + return r +} + +func rebase(ref *Ref, v *url.URL, notEqual bool) (Ref, bool) { + var newBase url.URL + + u := ref.GetURL() + + if u.Scheme != v.Scheme || u.Host != v.Host { + return *ref, false + } + + docPath := v.Path + v.Path = path.Dir(v.Path) + + if v.Path == "." { + v.Path = "" + } else if !strings.HasSuffix(v.Path, "/") { + v.Path += "/" + } + + newBase.Fragment = u.Fragment + + if strings.HasPrefix(u.Path, docPath) { + newBase.Path = strings.TrimPrefix(u.Path, docPath) + } else { + newBase.Path = strings.TrimPrefix(u.Path, v.Path) + } + + if notEqual && newBase.Path == "" && newBase.Fragment == "" { + // do not want rebasing to end up in an empty $ref + return *ref, false + } + + if path.IsAbs(newBase.Path) { + // whenever we end up with an absolute path, specify the scheme and host + newBase.Scheme = v.Scheme + newBase.Host = v.Host + } + + return MustCreateRef(newBase.String()), true +} + +// normalizeRef canonicalize a Ref, using a canonical relativeBase as its absolute anchor +func normalizeRef(ref *Ref, relativeBase string) *Ref { + r := MustCreateRef(normalizeURI(ref.String(), relativeBase)) + return &r +} + +// normalizeBase performs a normalization of the input base path. +// +// This always yields a canonical URI (absolute), usable for the document cache. +// +// It ensures that all further internal work on basePath may safely assume +// a non-empty, cross-platform, canonical URI (i.e. absolute). +// +// This normalization tolerates windows paths (e.g. C:\x\y\File.dat) and transform this +// in a file:// URL with lower cased drive letter and path. +// +// See also: https://en.wikipedia.org/wiki/File_URI_scheme +func normalizeBase(in string) string { + u, err := parseURL(in) + if err != nil { + specLogger.Printf("warning: invalid URI in RelativeBase %q: %v", in, err) + u, in = repairURI(in) + } + + u.Fragment = "" // any fragment in the base is irrelevant + + fixWindowsURI(u, in) // noop on non-windows OS + + u.Path = path.Clean(u.Path) + if u.Path == "." { // empty after Clean() + u.Path = "" + } + + if u.Scheme != "" { + if path.IsAbs(u.Path) || u.Scheme != fileScheme { + // this is absolute or explicitly not a local file: we're good + return u.String() + } + } + + // no scheme or file scheme with relative path: assume file and make it absolute + // enforce scheme file://... with absolute path. + // + // If the input path is relative, we anchor the path to the current working directory. + // NOTE: we may end up with a host component. Leave it unchanged: e.g. file://host/folder/file.json + + u.Scheme = fileScheme + u.Path = absPath(u.Path) // platform-dependent + u.RawQuery = "" // any query component is irrelevant for a base + return u.String() +} diff --git a/vendor/github.com/go-openapi/spec/normalizer_nonwindows.go b/vendor/github.com/go-openapi/spec/normalizer_nonwindows.go new file mode 100644 index 00000000..2df07231 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/normalizer_nonwindows.go @@ -0,0 +1,44 @@ +//go:build !windows +// +build !windows + +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "net/url" + "path/filepath" +) + +// absPath makes a file path absolute and compatible with a URI path component. +// +// The parameter must be a path, not an URI. +func absPath(in string) string { + anchored, err := filepath.Abs(in) + if err != nil { + specLogger.Printf("warning: could not resolve current working directory: %v", err) + return in + } + return anchored +} + +func repairURI(in string) (*url.URL, string) { + u, _ := parseURL("") + debugLog("repaired URI: original: %q, repaired: %q", in, "") + return u, "" +} + +func fixWindowsURI(u *url.URL, in string) { +} diff --git a/vendor/github.com/go-openapi/spec/normalizer_windows.go b/vendor/github.com/go-openapi/spec/normalizer_windows.go new file mode 100644 index 00000000..a66c532d --- /dev/null +++ b/vendor/github.com/go-openapi/spec/normalizer_windows.go @@ -0,0 +1,154 @@ +// -build windows + +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "net/url" + "os" + "path" + "path/filepath" + "strings" +) + +// absPath makes a file path absolute and compatible with a URI path component +// +// The parameter must be a path, not an URI. +func absPath(in string) string { + // NOTE(windows): filepath.Abs exhibits a special behavior on windows for empty paths. + // See https://github.com/golang/go/issues/24441 + if in == "" { + in = "." + } + + anchored, err := filepath.Abs(in) + if err != nil { + specLogger.Printf("warning: could not resolve current working directory: %v", err) + return in + } + + pth := strings.ReplaceAll(strings.ToLower(anchored), `\`, `/`) + if !strings.HasPrefix(pth, "/") { + pth = "/" + pth + } + + return path.Clean(pth) +} + +// repairURI tolerates invalid file URIs with common typos +// such as 'file://E:\folder\file', that break the regular URL parser. +// +// Adopting the same defaults as for unixes (e.g. return an empty path) would +// result into a counter-intuitive result for that case (e.g. E:\folder\file is +// eventually resolved as the current directory). The repair will detect the missing "/". +// +// Note that this only works for the file scheme. +func repairURI(in string) (*url.URL, string) { + const prefix = fileScheme + "://" + if !strings.HasPrefix(in, prefix) { + // giving up: resolve to empty path + u, _ := parseURL("") + + return u, "" + } + + // attempt the repair, stripping the scheme should be sufficient + u, _ := parseURL(strings.TrimPrefix(in, prefix)) + debugLog("repaired URI: original: %q, repaired: %q", in, u.String()) + + return u, u.String() +} + +// fixWindowsURI tolerates an absolute file path on windows such as C:\Base\File.yaml or \\host\share\Base\File.yaml +// and makes it a canonical URI: file:///c:/base/file.yaml +// +// Catch 22 notes for Windows: +// +// * There may be a drive letter on windows (it is lower-cased) +// * There may be a share UNC, e.g. \\server\folder\data.xml +// * Paths are case insensitive +// * Paths may already contain slashes +// * Paths must be slashed +// +// NOTE: there is no escaping. "/" may be valid separators just like "\". +// We don't use ToSlash() (which escapes everything) because windows now also +// tolerates the use of "/". Hence, both C:\File.yaml and C:/File.yaml will work. +func fixWindowsURI(u *url.URL, in string) { + drive := filepath.VolumeName(in) + + if len(drive) > 0 { + if len(u.Scheme) == 1 && strings.EqualFold(u.Scheme, drive[:1]) { // a path with a drive letter + u.Scheme = fileScheme + u.Host = "" + u.Path = strings.Join([]string{drive, u.Opaque, u.Path}, `/`) // reconstruct the full path component (no fragment, no query) + } else if u.Host == "" && strings.HasPrefix(u.Path, drive) { // a path with a \\host volume + // NOTE: the special host@port syntax for UNC is not supported (yet) + u.Scheme = fileScheme + + // this is a modified version of filepath.Dir() to apply on the VolumeName itself + i := len(drive) - 1 + for i >= 0 && !os.IsPathSeparator(drive[i]) { + i-- + } + host := drive[:i] // \\host\share => host + + u.Path = strings.TrimPrefix(u.Path, host) + u.Host = strings.TrimPrefix(host, `\\`) + } + + u.Opaque = "" + u.Path = strings.ReplaceAll(strings.ToLower(u.Path), `\`, `/`) + + // ensure we form an absolute path + if !strings.HasPrefix(u.Path, "/") { + u.Path = "/" + u.Path + } + + u.Path = path.Clean(u.Path) + + return + } + + if u.Scheme == fileScheme { + // Handle dodgy cases for file://{...} URIs on windows. + // A canonical URI should always be followed by an absolute path. + // + // Examples: + // * file:///folder/file => valid, unchanged + // * file:///c:\folder\file => slashed + // * file:///./folder/file => valid, cleaned to remove the dot + // * file:///.\folder\file => remapped to cwd + // * file:///. => dodgy, remapped to / (consistent with the behavior on unix) + // * file:///.. => dodgy, remapped to / (consistent with the behavior on unix) + if (!path.IsAbs(u.Path) && !filepath.IsAbs(u.Path)) || (strings.HasPrefix(u.Path, `/.`) && strings.Contains(u.Path, `\`)) { + // ensure we form an absolute path + u.Path, _ = filepath.Abs(strings.TrimLeft(u.Path, `/`)) + if !strings.HasPrefix(u.Path, "/") { + u.Path = "/" + u.Path + } + } + u.Path = strings.ToLower(u.Path) + } + + // NOTE: lower case normalization does not propagate to inner resources, + // generated when rebasing: when joining a relative URI with a file to an absolute base, + // only the base is currently lower-cased. + // + // For now, we assume this is good enough for most use cases + // and try not to generate too many differences + // between the output produced on different platforms. + u.Path = path.Clean(strings.ReplaceAll(u.Path, `\`, `/`)) +} diff --git a/vendor/github.com/go-openapi/spec/operation.go b/vendor/github.com/go-openapi/spec/operation.go new file mode 100644 index 00000000..995ce6ac --- /dev/null +++ b/vendor/github.com/go-openapi/spec/operation.go @@ -0,0 +1,397 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "sort" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +func init() { + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}{}) +} + +// OperationProps describes an operation +// +// NOTES: +// - schemes, when present must be from [http, https, ws, wss]: see validate +// - Security is handled as a special case: see MarshalJSON function +type OperationProps struct { + Description string `json:"description,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` + Tags []string `json:"tags,omitempty"` + Summary string `json:"summary,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + ID string `json:"operationId,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` + Security []map[string][]string `json:"security,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` + Responses *Responses `json:"responses,omitempty"` +} + +// MarshalJSON takes care of serializing operation properties to JSON +// +// We use a custom marhaller here to handle a special cases related to +// the Security field. We need to preserve zero length slice +// while omitting the field when the value is nil/unset. +func (op OperationProps) MarshalJSON() ([]byte, error) { + type Alias OperationProps + if op.Security == nil { + return json.Marshal(&struct { + Security []map[string][]string `json:"security,omitempty"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) + } + return json.Marshal(&struct { + Security []map[string][]string `json:"security"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) +} + +// Operation describes a single API operation on a path. +// +// For more information: http://goo.gl/8us55a#operationObject +type Operation struct { + VendorExtensible + OperationProps +} + +// SuccessResponse gets a success response model +func (o *Operation) SuccessResponse() (*Response, int, bool) { + if o.Responses == nil { + return nil, 0, false + } + + responseCodes := make([]int, 0, len(o.Responses.StatusCodeResponses)) + for k := range o.Responses.StatusCodeResponses { + if k >= 200 && k < 300 { + responseCodes = append(responseCodes, k) + } + } + if len(responseCodes) > 0 { + sort.Ints(responseCodes) + v := o.Responses.StatusCodeResponses[responseCodes[0]] + return &v, responseCodes[0], true + } + + return o.Responses.Default, 0, false +} + +// JSONLookup look up a value by the json property name +func (o Operation) JSONLookup(token string) (interface{}, error) { + if ex, ok := o.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(o.OperationProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (o *Operation) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &o.OperationProps); err != nil { + return err + } + return json.Unmarshal(data, &o.VendorExtensible) +} + +// MarshalJSON converts this items object to JSON +func (o Operation) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(o.OperationProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(o.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// NewOperation creates a new operation instance. +// It expects an ID as parameter but not passing an ID is also valid. +func NewOperation(id string) *Operation { + op := new(Operation) + op.ID = id + return op +} + +// WithID sets the ID property on this operation, allows for chaining. +func (o *Operation) WithID(id string) *Operation { + o.ID = id + return o +} + +// WithDescription sets the description on this operation, allows for chaining +func (o *Operation) WithDescription(description string) *Operation { + o.Description = description + return o +} + +// WithSummary sets the summary on this operation, allows for chaining +func (o *Operation) WithSummary(summary string) *Operation { + o.Summary = summary + return o +} + +// WithExternalDocs sets/removes the external docs for/from this operation. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (o *Operation) WithExternalDocs(description, url string) *Operation { + if description == "" && url == "" { + o.ExternalDocs = nil + return o + } + + if o.ExternalDocs == nil { + o.ExternalDocs = &ExternalDocumentation{} + } + o.ExternalDocs.Description = description + o.ExternalDocs.URL = url + return o +} + +// Deprecate marks the operation as deprecated +func (o *Operation) Deprecate() *Operation { + o.Deprecated = true + return o +} + +// Undeprecate marks the operation as not deprected +func (o *Operation) Undeprecate() *Operation { + o.Deprecated = false + return o +} + +// WithConsumes adds media types for incoming body values +func (o *Operation) WithConsumes(mediaTypes ...string) *Operation { + o.Consumes = append(o.Consumes, mediaTypes...) + return o +} + +// WithProduces adds media types for outgoing body values +func (o *Operation) WithProduces(mediaTypes ...string) *Operation { + o.Produces = append(o.Produces, mediaTypes...) + return o +} + +// WithTags adds tags for this operation +func (o *Operation) WithTags(tags ...string) *Operation { + o.Tags = append(o.Tags, tags...) + return o +} + +// AddParam adds a parameter to this operation, when a parameter for that location +// and with that name already exists it will be replaced +func (o *Operation) AddParam(param *Parameter) *Operation { + if param == nil { + return o + } + + for i, p := range o.Parameters { + if p.Name == param.Name && p.In == param.In { + params := append(o.Parameters[:i], *param) + params = append(params, o.Parameters[i+1:]...) + o.Parameters = params + return o + } + } + + o.Parameters = append(o.Parameters, *param) + return o +} + +// RemoveParam removes a parameter from the operation +func (o *Operation) RemoveParam(name, in string) *Operation { + for i, p := range o.Parameters { + if p.Name == name && p.In == in { + o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...) + return o + } + } + return o +} + +// SecuredWith adds a security scope to this operation. +func (o *Operation) SecuredWith(name string, scopes ...string) *Operation { + o.Security = append(o.Security, map[string][]string{name: scopes}) + return o +} + +// WithDefaultResponse adds a default response to the operation. +// Passing a nil value will remove the response +func (o *Operation) WithDefaultResponse(response *Response) *Operation { + return o.RespondsWith(0, response) +} + +// RespondsWith adds a status code response to the operation. +// When the code is 0 the value of the response will be used as default response value. +// When the value of the response is nil it will be removed from the operation +func (o *Operation) RespondsWith(code int, response *Response) *Operation { + if o.Responses == nil { + o.Responses = new(Responses) + } + if code == 0 { + o.Responses.Default = response + return o + } + if response == nil { + delete(o.Responses.StatusCodeResponses, code) + return o + } + if o.Responses.StatusCodeResponses == nil { + o.Responses.StatusCodeResponses = make(map[int]Response) + } + o.Responses.StatusCodeResponses[code] = *response + return o +} + +type opsAlias OperationProps + +type gobAlias struct { + Security []map[string]struct { + List []string + Pad bool + } + Alias *opsAlias + SecurityIsEmpty bool +} + +// GobEncode provides a safe gob encoder for Operation, including empty security requirements +func (o Operation) GobEncode() ([]byte, error) { + raw := struct { + Ext VendorExtensible + Props OperationProps + }{ + Ext: o.VendorExtensible, + Props: o.OperationProps, + } + var b bytes.Buffer + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Operation, including empty security requirements +func (o *Operation) GobDecode(b []byte) error { + var raw struct { + Ext VendorExtensible + Props OperationProps + } + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + o.VendorExtensible = raw.Ext + o.OperationProps = raw.Props + return nil +} + +// GobEncode provides a safe gob encoder for Operation, including empty security requirements +func (op OperationProps) GobEncode() ([]byte, error) { + raw := gobAlias{ + Alias: (*opsAlias)(&op), + } + + var b bytes.Buffer + if op.Security == nil { + // nil security requirement + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + if len(op.Security) == 0 { + // empty, but non-nil security requirement + raw.SecurityIsEmpty = true + raw.Alias.Security = nil + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + raw.Security = make([]map[string]struct { + List []string + Pad bool + }, 0, len(op.Security)) + for _, req := range op.Security { + v := make(map[string]struct { + List []string + Pad bool + }, len(req)) + for k, val := range req { + v[k] = struct { + List []string + Pad bool + }{ + List: val, + } + } + raw.Security = append(raw.Security, v) + } + + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Operation, including empty security requirements +func (op *OperationProps) GobDecode(b []byte) error { + var raw gobAlias + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + if raw.Alias == nil { + return nil + } + + switch { + case raw.SecurityIsEmpty: + // empty, but non-nil security requirement + raw.Alias.Security = []map[string][]string{} + case len(raw.Alias.Security) == 0: + // nil security requirement + raw.Alias.Security = nil + default: + raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security)) + for _, req := range raw.Security { + v := make(map[string][]string, len(req)) + for k, val := range req { + v[k] = make([]string, 0, len(val.List)) + v[k] = append(v[k], val.List...) + } + raw.Alias.Security = append(raw.Alias.Security, v) + } + } + + *op = *(*OperationProps)(raw.Alias) + return nil +} diff --git a/vendor/github.com/go-openapi/spec/parameter.go b/vendor/github.com/go-openapi/spec/parameter.go new file mode 100644 index 00000000..2b2b89b6 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/parameter.go @@ -0,0 +1,326 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// QueryParam creates a query parameter +func QueryParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "query"}} +} + +// HeaderParam creates a header parameter, this is always required by default +func HeaderParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "header", Required: true}} +} + +// PathParam creates a path parameter, this is always required +func PathParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "path", Required: true}} +} + +// BodyParam creates a body parameter +func BodyParam(name string, schema *Schema) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema}} +} + +// FormDataParam creates a body parameter +func FormDataParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}} +} + +// FileParam creates a body parameter +func FileParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}, + SimpleSchema: SimpleSchema{Type: "file"}} +} + +// SimpleArrayParam creates a param for a simple array (string, int, date etc) +func SimpleArrayParam(name, tpe, fmt string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name}, + SimpleSchema: SimpleSchema{Type: jsonArray, CollectionFormat: "csv", + Items: &Items{SimpleSchema: SimpleSchema{Type: tpe, Format: fmt}}}} +} + +// ParamRef creates a parameter that's a json reference +func ParamRef(uri string) *Parameter { + p := new(Parameter) + p.Ref = MustCreateRef(uri) + return p +} + +// ParamProps describes the specific attributes of an operation parameter +// +// NOTE: +// - Schema is defined when "in" == "body": see validate +// - AllowEmptyValue is allowed where "in" == "query" || "formData" +type ParamProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + In string `json:"in,omitempty"` + Required bool `json:"required,omitempty"` + Schema *Schema `json:"schema,omitempty"` + AllowEmptyValue bool `json:"allowEmptyValue,omitempty"` +} + +// Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn). +// +// There are five possible parameter types. +// * Path - Used together with [Path Templating](#pathTemplating), where the parameter value is actually part +// of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`, +// the path parameter is `itemId`. +// * Query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`. +// * Header - Custom headers that are expected as part of the request. +// * Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be +// _one_ body parameter. The name of the body parameter has no effect on the parameter itself and is used for +// documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist +// together for the same operation. +// * Form - Used to describe the payload of an HTTP request when either `application/x-www-form-urlencoded` or +// `multipart/form-data` are used as the content type of the request (in Swagger's definition, +// the [`consumes`](#operationConsumes) property of an operation). This is the only parameter type that can be used +// to send files, thus supporting the `file` type. Since form parameters are sent in the payload, they cannot be +// declared together with a body parameter for the same operation. Form parameters have a different format based on +// the content-type used (for further details, consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4). +// * `application/x-www-form-urlencoded` - Similar to the format of Query parameters but as a payload. +// For example, `foo=1&bar=swagger` - both `foo` and `bar` are form parameters. This is normally used for simple +// parameters that are being transferred. +// * `multipart/form-data` - each parameter takes a section in the payload with an internal header. +// For example, for the header `Content-Disposition: form-data; name="submit-name"` the name of the parameter is +// `submit-name`. This type of form parameters is more commonly used for file transfers. +// +// For more information: http://goo.gl/8us55a#parameterObject +type Parameter struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible + ParamProps +} + +// JSONLookup look up a value by the json property name +func (p Parameter) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == jsonRef { + return &p.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(p.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.ParamProps, token) + return r, err +} + +// WithDescription a fluent builder method for the description of the parameter +func (p *Parameter) WithDescription(description string) *Parameter { + p.Description = description + return p +} + +// Named a fluent builder method to override the name of the parameter +func (p *Parameter) Named(name string) *Parameter { + p.Name = name + return p +} + +// WithLocation a fluent builder method to override the location of the parameter +func (p *Parameter) WithLocation(in string) *Parameter { + p.In = in + return p +} + +// Typed a fluent builder method for the type of the parameter value +func (p *Parameter) Typed(tpe, format string) *Parameter { + p.Type = tpe + p.Format = format + return p +} + +// CollectionOf a fluent builder method for an array parameter +func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { + p.Type = jsonArray + p.Items = items + p.CollectionFormat = format + return p +} + +// WithDefault sets the default value on this parameter +func (p *Parameter) WithDefault(defaultValue interface{}) *Parameter { + p.AsOptional() // with default implies optional + p.Default = defaultValue + return p +} + +// AllowsEmptyValues flags this parameter as being ok with empty values +func (p *Parameter) AllowsEmptyValues() *Parameter { + p.AllowEmptyValue = true + return p +} + +// NoEmptyValues flags this parameter as not liking empty values +func (p *Parameter) NoEmptyValues() *Parameter { + p.AllowEmptyValue = false + return p +} + +// AsOptional flags this parameter as optional +func (p *Parameter) AsOptional() *Parameter { + p.Required = false + return p +} + +// AsRequired flags this parameter as required +func (p *Parameter) AsRequired() *Parameter { + if p.Default != nil { // with a default required makes no sense + return p + } + p.Required = true + return p +} + +// WithMaxLength sets a max length value +func (p *Parameter) WithMaxLength(max int64) *Parameter { + p.MaxLength = &max + return p +} + +// WithMinLength sets a min length value +func (p *Parameter) WithMinLength(min int64) *Parameter { + p.MinLength = &min + return p +} + +// WithPattern sets a pattern value +func (p *Parameter) WithPattern(pattern string) *Parameter { + p.Pattern = pattern + return p +} + +// WithMultipleOf sets a multiple of value +func (p *Parameter) WithMultipleOf(number float64) *Parameter { + p.MultipleOf = &number + return p +} + +// WithMaximum sets a maximum number value +func (p *Parameter) WithMaximum(max float64, exclusive bool) *Parameter { + p.Maximum = &max + p.ExclusiveMaximum = exclusive + return p +} + +// WithMinimum sets a minimum number value +func (p *Parameter) WithMinimum(min float64, exclusive bool) *Parameter { + p.Minimum = &min + p.ExclusiveMinimum = exclusive + return p +} + +// WithEnum sets a the enum values (replace) +func (p *Parameter) WithEnum(values ...interface{}) *Parameter { + p.Enum = append([]interface{}{}, values...) + return p +} + +// WithMaxItems sets the max items +func (p *Parameter) WithMaxItems(size int64) *Parameter { + p.MaxItems = &size + return p +} + +// WithMinItems sets the min items +func (p *Parameter) WithMinItems(size int64) *Parameter { + p.MinItems = &size + return p +} + +// UniqueValues dictates that this array can only have unique items +func (p *Parameter) UniqueValues() *Parameter { + p.UniqueItems = true + return p +} + +// AllowDuplicates this array can have duplicates +func (p *Parameter) AllowDuplicates() *Parameter { + p.UniqueItems = false + return p +} + +// WithValidations is a fluent method to set parameter validations +func (p *Parameter) WithValidations(val CommonValidations) *Parameter { + p.SetValidations(SchemaValidations{CommonValidations: val}) + return p +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Parameter) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &p.ParamProps) +} + +// MarshalJSON converts this items object to JSON +func (p Parameter) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(p.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.ParamProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b3, b1, b2, b4, b5), nil +} diff --git a/vendor/github.com/go-openapi/spec/path_item.go b/vendor/github.com/go-openapi/spec/path_item.go new file mode 100644 index 00000000..68fc8e90 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/path_item.go @@ -0,0 +1,87 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// PathItemProps the path item specific properties +type PathItemProps struct { + Get *Operation `json:"get,omitempty"` + Put *Operation `json:"put,omitempty"` + Post *Operation `json:"post,omitempty"` + Delete *Operation `json:"delete,omitempty"` + Options *Operation `json:"options,omitempty"` + Head *Operation `json:"head,omitempty"` + Patch *Operation `json:"patch,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` +} + +// PathItem describes the operations available on a single path. +// A Path Item may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// The path itself is still exposed to the documentation viewer but they will +// not know which operations and parameters are available. +// +// For more information: http://goo.gl/8us55a#pathItemObject +type PathItem struct { + Refable + VendorExtensible + PathItemProps +} + +// JSONLookup look up a value by the json property name +func (p PathItem) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == jsonRef { + return &p.Ref, nil + } + r, _, err := jsonpointer.GetForToken(p.PathItemProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *PathItem) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &p.PathItemProps) +} + +// MarshalJSON converts this items object to JSON +func (p PathItem) MarshalJSON() ([]byte, error) { + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.PathItemProps) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b3, b4, b5) + return concated, nil +} diff --git a/vendor/github.com/go-openapi/spec/paths.go b/vendor/github.com/go-openapi/spec/paths.go new file mode 100644 index 00000000..9dc82a29 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/paths.go @@ -0,0 +1,97 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/go-openapi/swag" +) + +// Paths holds the relative paths to the individual endpoints. +// The path is appended to the [`basePath`](http://goo.gl/8us55a#swaggerBasePath) in order +// to construct the full URL. +// The Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// +// For more information: http://goo.gl/8us55a#pathsObject +type Paths struct { + VendorExtensible + Paths map[string]PathItem `json:"-"` // custom serializer to flatten this, each entry must start with "/" +} + +// JSONLookup look up a value by the json property name +func (p Paths) JSONLookup(token string) (interface{}, error) { + if pi, ok := p.Paths[token]; ok { + return &pi, nil + } + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Paths) UnmarshalJSON(data []byte) error { + var res map[string]json.RawMessage + if err := json.Unmarshal(data, &res); err != nil { + return err + } + for k, v := range res { + if strings.HasPrefix(strings.ToLower(k), "x-") { + if p.Extensions == nil { + p.Extensions = make(map[string]interface{}) + } + var d interface{} + if err := json.Unmarshal(v, &d); err != nil { + return err + } + p.Extensions[k] = d + } + if strings.HasPrefix(k, "/") { + if p.Paths == nil { + p.Paths = make(map[string]PathItem) + } + var pi PathItem + if err := json.Unmarshal(v, &pi); err != nil { + return err + } + p.Paths[k] = pi + } + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (p Paths) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + + pths := make(map[string]PathItem) + for k, v := range p.Paths { + if strings.HasPrefix(k, "/") { + pths[k] = v + } + } + b2, err := json.Marshal(pths) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} diff --git a/vendor/github.com/go-openapi/spec/properties.go b/vendor/github.com/go-openapi/spec/properties.go new file mode 100644 index 00000000..2af13787 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/properties.go @@ -0,0 +1,91 @@ +package spec + +import ( + "bytes" + "encoding/json" + "reflect" + "sort" +) + +// OrderSchemaItem holds a named schema (e.g. from a property of an object) +type OrderSchemaItem struct { + Name string + Schema +} + +// OrderSchemaItems is a sortable slice of named schemas. +// The ordering is defined by the x-order schema extension. +type OrderSchemaItems []OrderSchemaItem + +// MarshalJSON produces a json object with keys defined by the name schemas +// of the OrderSchemaItems slice, keeping the original order of the slice. +func (items OrderSchemaItems) MarshalJSON() ([]byte, error) { + buf := bytes.NewBuffer(nil) + buf.WriteString("{") + for i := range items { + if i > 0 { + buf.WriteString(",") + } + buf.WriteString("\"") + buf.WriteString(items[i].Name) + buf.WriteString("\":") + bs, err := json.Marshal(&items[i].Schema) + if err != nil { + return nil, err + } + buf.Write(bs) + } + buf.WriteString("}") + return buf.Bytes(), nil +} + +func (items OrderSchemaItems) Len() int { return len(items) } +func (items OrderSchemaItems) Swap(i, j int) { items[i], items[j] = items[j], items[i] } +func (items OrderSchemaItems) Less(i, j int) (ret bool) { + ii, oki := items[i].Extensions.GetString("x-order") + ij, okj := items[j].Extensions.GetString("x-order") + if oki { + if okj { + defer func() { + if err := recover(); err != nil { + defer func() { + if err = recover(); err != nil { + ret = items[i].Name < items[j].Name + } + }() + ret = reflect.ValueOf(ii).String() < reflect.ValueOf(ij).String() + } + }() + return reflect.ValueOf(ii).Int() < reflect.ValueOf(ij).Int() + } + return true + } else if okj { + return false + } + return items[i].Name < items[j].Name +} + +// SchemaProperties is a map representing the properties of a Schema object. +// It knows how to transform its keys into an ordered slice. +type SchemaProperties map[string]Schema + +// ToOrderedSchemaItems transforms the map of properties into a sortable slice +func (properties SchemaProperties) ToOrderedSchemaItems() OrderSchemaItems { + items := make(OrderSchemaItems, 0, len(properties)) + for k, v := range properties { + items = append(items, OrderSchemaItem{ + Name: k, + Schema: v, + }) + } + sort.Sort(items) + return items +} + +// MarshalJSON produces properties as json, keeping their order. +func (properties SchemaProperties) MarshalJSON() ([]byte, error) { + if properties == nil { + return []byte("null"), nil + } + return json.Marshal(properties.ToOrderedSchemaItems()) +} diff --git a/vendor/github.com/go-openapi/spec/ref.go b/vendor/github.com/go-openapi/spec/ref.go new file mode 100644 index 00000000..b0ef9bd9 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/ref.go @@ -0,0 +1,193 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "net/http" + "os" + "path/filepath" + + "github.com/go-openapi/jsonreference" +) + +// Refable is a struct for things that accept a $ref property +type Refable struct { + Ref Ref +} + +// MarshalJSON marshals the ref to json +func (r Refable) MarshalJSON() ([]byte, error) { + return r.Ref.MarshalJSON() +} + +// UnmarshalJSON unmarshalss the ref from json +func (r *Refable) UnmarshalJSON(d []byte) error { + return json.Unmarshal(d, &r.Ref) +} + +// Ref represents a json reference that is potentially resolved +type Ref struct { + jsonreference.Ref +} + +// RemoteURI gets the remote uri part of the ref +func (r *Ref) RemoteURI() string { + if r.String() == "" { + return "" + } + + u := *r.GetURL() + u.Fragment = "" + return u.String() +} + +// IsValidURI returns true when the url the ref points to can be found +func (r *Ref) IsValidURI(basepaths ...string) bool { + if r.String() == "" { + return true + } + + v := r.RemoteURI() + if v == "" { + return true + } + + if r.HasFullURL { + //nolint:noctx,gosec + rr, err := http.Get(v) + if err != nil { + return false + } + defer rr.Body.Close() + + return rr.StatusCode/100 == 2 + } + + if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { + return false + } + + // check for local file + pth := v + if r.HasURLPathOnly { + base := "." + if len(basepaths) > 0 { + base = filepath.Dir(filepath.Join(basepaths...)) + } + p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) + if e != nil { + return false + } + pth = p + } + + fi, err := os.Stat(filepath.ToSlash(pth)) + if err != nil { + return false + } + + return !fi.IsDir() +} + +// Inherits creates a new reference from a parent and a child +// If the child cannot inherit from the parent, an error is returned +func (r *Ref) Inherits(child Ref) (*Ref, error) { + ref, err := r.Ref.Inherits(child.Ref) + if err != nil { + return nil, err + } + return &Ref{Ref: *ref}, nil +} + +// NewRef creates a new instance of a ref object +// returns an error when the reference uri is an invalid uri +func NewRef(refURI string) (Ref, error) { + ref, err := jsonreference.New(refURI) + if err != nil { + return Ref{}, err + } + return Ref{Ref: ref}, nil +} + +// MustCreateRef creates a ref object but panics when refURI is invalid. +// Use the NewRef method for a version that returns an error. +func MustCreateRef(refURI string) Ref { + return Ref{Ref: jsonreference.MustCreateRef(refURI)} +} + +// MarshalJSON marshals this ref into a JSON object +func (r Ref) MarshalJSON() ([]byte, error) { + str := r.String() + if str == "" { + if r.IsRoot() { + return []byte(`{"$ref":""}`), nil + } + return []byte("{}"), nil + } + v := map[string]interface{}{"$ref": str} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshals this ref from a JSON object +func (r *Ref) UnmarshalJSON(d []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(d, &v); err != nil { + return err + } + return r.fromMap(v) +} + +// GobEncode provides a safe gob encoder for Ref +func (r Ref) GobEncode() ([]byte, error) { + var b bytes.Buffer + raw, err := r.MarshalJSON() + if err != nil { + return nil, err + } + err = gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Ref +func (r *Ref) GobDecode(b []byte) error { + var raw []byte + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + return json.Unmarshal(raw, r) +} + +func (r *Ref) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + + if vv, ok := v["$ref"]; ok { + if str, ok := vv.(string); ok { + ref, err := jsonreference.New(str) + if err != nil { + return err + } + *r = Ref{Ref: ref} + } + } + + return nil +} diff --git a/vendor/github.com/go-openapi/spec/resolver.go b/vendor/github.com/go-openapi/spec/resolver.go new file mode 100644 index 00000000..47d1ee13 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/resolver.go @@ -0,0 +1,127 @@ +package spec + +import ( + "fmt" + + "github.com/go-openapi/swag" +) + +func resolveAnyWithBase(root interface{}, ref *Ref, result interface{}, options *ExpandOptions) error { + options = optionsOrDefault(options) + resolver := defaultSchemaLoader(root, options, nil, nil) + + if err := resolver.Resolve(ref, result, options.RelativeBase); err != nil { + return err + } + + return nil +} + +// ResolveRefWithBase resolves a reference against a context root with preservation of base path +func ResolveRefWithBase(root interface{}, ref *Ref, options *ExpandOptions) (*Schema, error) { + result := new(Schema) + + if err := resolveAnyWithBase(root, ref, result, options); err != nil { + return nil, err + } + + return result, nil +} + +// ResolveRef resolves a reference for a schema against a context root +// ref is guaranteed to be in root (no need to go to external files) +// +// ResolveRef is ONLY called from the code generation module +func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { + res, _, err := ref.GetPointer().Get(root) + if err != nil { + return nil, err + } + + switch sch := res.(type) { + case Schema: + return &sch, nil + case *Schema: + return sch, nil + case map[string]interface{}: + newSch := new(Schema) + if err = swag.DynamicJSONToStruct(sch, newSch); err != nil { + return nil, err + } + return newSch, nil + default: + return nil, fmt.Errorf("type: %T: %w", sch, ErrUnknownTypeForReference) + } +} + +// ResolveParameterWithBase resolves a parameter reference against a context root and base path +func ResolveParameterWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Parameter, error) { + result := new(Parameter) + + if err := resolveAnyWithBase(root, &ref, result, options); err != nil { + return nil, err + } + + return result, nil +} + +// ResolveParameter resolves a parameter reference against a context root +func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) { + return ResolveParameterWithBase(root, ref, nil) +} + +// ResolveResponseWithBase resolves response a reference against a context root and base path +func ResolveResponseWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Response, error) { + result := new(Response) + + err := resolveAnyWithBase(root, &ref, result, options) + if err != nil { + return nil, err + } + + return result, nil +} + +// ResolveResponse resolves response a reference against a context root +func ResolveResponse(root interface{}, ref Ref) (*Response, error) { + return ResolveResponseWithBase(root, ref, nil) +} + +// ResolvePathItemWithBase resolves response a path item against a context root and base path +func ResolvePathItemWithBase(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) { + result := new(PathItem) + + if err := resolveAnyWithBase(root, &ref, result, options); err != nil { + return nil, err + } + + return result, nil +} + +// ResolvePathItem resolves response a path item against a context root and base path +// +// Deprecated: use ResolvePathItemWithBase instead +func ResolvePathItem(root interface{}, ref Ref, options *ExpandOptions) (*PathItem, error) { + return ResolvePathItemWithBase(root, ref, options) +} + +// ResolveItemsWithBase resolves parameter items reference against a context root and base path. +// +// NOTE: stricly speaking, this construct is not supported by Swagger 2.0. +// Similarly, $ref are forbidden in response headers. +func ResolveItemsWithBase(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) { + result := new(Items) + + if err := resolveAnyWithBase(root, &ref, result, options); err != nil { + return nil, err + } + + return result, nil +} + +// ResolveItems resolves parameter items reference against a context root and base path. +// +// Deprecated: use ResolveItemsWithBase instead +func ResolveItems(root interface{}, ref Ref, options *ExpandOptions) (*Items, error) { + return ResolveItemsWithBase(root, ref, options) +} diff --git a/vendor/github.com/go-openapi/spec/response.go b/vendor/github.com/go-openapi/spec/response.go new file mode 100644 index 00000000..0340b60d --- /dev/null +++ b/vendor/github.com/go-openapi/spec/response.go @@ -0,0 +1,152 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// ResponseProps properties specific to a response +type ResponseProps struct { + Description string `json:"description"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]interface{} `json:"examples,omitempty"` +} + +// Response describes a single response from an API Operation. +// +// For more information: http://goo.gl/8us55a#responseObject +type Response struct { + Refable + ResponseProps + VendorExtensible +} + +// JSONLookup look up a value by the json property name +func (r Response) JSONLookup(token string) (interface{}, error) { + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if token == "$ref" { + return &r.Ref, nil + } + ptr, _, err := jsonpointer.GetForToken(r.ResponseProps, token) + return ptr, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Response) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponseProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.Refable); err != nil { + return err + } + return json.Unmarshal(data, &r.VendorExtensible) +} + +// MarshalJSON converts this items object to JSON +func (r Response) MarshalJSON() ([]byte, error) { + var ( + b1 []byte + err error + ) + + if r.Ref.String() == "" { + // when there is no $ref, empty description is rendered as an empty string + b1, err = json.Marshal(r.ResponseProps) + } else { + // when there is $ref inside the schema, description should be omitempty-ied + b1, err = json.Marshal(struct { + Description string `json:"description,omitempty"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]interface{} `json:"examples,omitempty"` + }{ + Description: r.ResponseProps.Description, + Schema: r.ResponseProps.Schema, + Examples: r.ResponseProps.Examples, + }) + } + if err != nil { + return nil, err + } + + b2, err := json.Marshal(r.Refable) + if err != nil { + return nil, err + } + b3, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// NewResponse creates a new response instance +func NewResponse() *Response { + return new(Response) +} + +// ResponseRef creates a response as a json reference +func ResponseRef(url string) *Response { + resp := NewResponse() + resp.Ref = MustCreateRef(url) + return resp +} + +// WithDescription sets the description on this response, allows for chaining +func (r *Response) WithDescription(description string) *Response { + r.Description = description + return r +} + +// WithSchema sets the schema on this response, allows for chaining. +// Passing a nil argument removes the schema from this response +func (r *Response) WithSchema(schema *Schema) *Response { + r.Schema = schema + return r +} + +// AddHeader adds a header to this response +func (r *Response) AddHeader(name string, header *Header) *Response { + if header == nil { + return r.RemoveHeader(name) + } + if r.Headers == nil { + r.Headers = make(map[string]Header) + } + r.Headers[name] = *header + return r +} + +// RemoveHeader removes a header from this response +func (r *Response) RemoveHeader(name string) *Response { + delete(r.Headers, name) + return r +} + +// AddExample adds an example to this response +func (r *Response) AddExample(mediaType string, example interface{}) *Response { + if r.Examples == nil { + r.Examples = make(map[string]interface{}) + } + r.Examples[mediaType] = example + return r +} diff --git a/vendor/github.com/go-openapi/spec/responses.go b/vendor/github.com/go-openapi/spec/responses.go new file mode 100644 index 00000000..4efb6f86 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/responses.go @@ -0,0 +1,127 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "github.com/go-openapi/swag" +) + +// Responses is a container for the expected responses of an operation. +// The container maps a HTTP response code to the expected response. +// It is not expected from the documentation to necessarily cover all possible HTTP response codes, +// since they may not be known in advance. However, it is expected from the documentation to cover +// a successful operation response and any known errors. +// +// The `default` can be used a default response object for all HTTP codes that are not covered +// individually by the specification. +// +// The `Responses Object` MUST contain at least one response code, and it SHOULD be the response +// for a successful operation call. +// +// For more information: http://goo.gl/8us55a#responsesObject +type Responses struct { + VendorExtensible + ResponsesProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (r Responses) JSONLookup(token string) (interface{}, error) { + if token == "default" { + return r.Default, nil + } + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if i, err := strconv.Atoi(token); err == nil { + if scr, ok := r.StatusCodeResponses[i]; ok { + return scr, nil + } + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Responses) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponsesProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.VendorExtensible); err != nil { + return err + } + if reflect.DeepEqual(ResponsesProps{}, r.ResponsesProps) { + r.ResponsesProps = ResponsesProps{} + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (r Responses) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(r.ResponsesProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// ResponsesProps describes all responses for an operation. +// It tells what is the default response and maps all responses with a +// HTTP status code. +type ResponsesProps struct { + Default *Response + StatusCodeResponses map[int]Response +} + +// MarshalJSON marshals responses as JSON +func (r ResponsesProps) MarshalJSON() ([]byte, error) { + toser := map[string]Response{} + if r.Default != nil { + toser["default"] = *r.Default + } + for k, v := range r.StatusCodeResponses { + toser[strconv.Itoa(k)] = v + } + return json.Marshal(toser) +} + +// UnmarshalJSON unmarshals responses from JSON +func (r *ResponsesProps) UnmarshalJSON(data []byte) error { + var res map[string]Response + if err := json.Unmarshal(data, &res); err != nil { + return nil + } + if v, ok := res["default"]; ok { + r.Default = &v + delete(res, "default") + } + for k, v := range res { + if nk, err := strconv.Atoi(k); err == nil { + if r.StatusCodeResponses == nil { + r.StatusCodeResponses = map[int]Response{} + } + r.StatusCodeResponses[nk] = v + } + } + return nil +} diff --git a/vendor/github.com/go-openapi/spec/schema.go b/vendor/github.com/go-openapi/spec/schema.go new file mode 100644 index 00000000..4e9be857 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/schema.go @@ -0,0 +1,645 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// BooleanProperty creates a boolean property +func BooleanProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} +} + +// BoolProperty creates a boolean property +func BoolProperty() *Schema { return BooleanProperty() } + +// StringProperty creates a string property +func StringProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// CharProperty creates a string property +func CharProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// Float64Property creates a float64/double property +func Float64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} +} + +// Float32Property creates a float32/float property +func Float32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} +} + +// Int8Property creates an int8 property +func Int8Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} +} + +// Int16Property creates an int16 property +func Int16Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} +} + +// Int32Property creates an int32 property +func Int32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} +} + +// Int64Property creates an int64 property +func Int64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} +} + +// StrFmtProperty creates a property for the named string format +func StrFmtProperty(format string) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} +} + +// DateProperty creates a date property +func DateProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} +} + +// DateTimeProperty creates a date time property +func DateTimeProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} +} + +// MapProperty creates a map property +func MapProperty(property *Schema) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, + AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}}} +} + +// RefProperty creates a ref property +func RefProperty(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// RefSchema creates a ref property +func RefSchema(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// ArrayProperty creates an array property +func ArrayProperty(items *Schema) *Schema { + if items == nil { + return &Schema{SchemaProps: SchemaProps{Type: []string{"array"}}} + } + return &Schema{SchemaProps: SchemaProps{Items: &SchemaOrArray{Schema: items}, Type: []string{"array"}}} +} + +// ComposedSchema creates a schema with allOf +func ComposedSchema(schemas ...Schema) *Schema { + s := new(Schema) + s.AllOf = schemas + return s +} + +// SchemaURL represents a schema url +type SchemaURL string + +// MarshalJSON marshal this to JSON +func (r SchemaURL) MarshalJSON() ([]byte, error) { + if r == "" { + return []byte("{}"), nil + } + v := map[string]interface{}{"$schema": string(r)} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshal this from JSON +func (r *SchemaURL) UnmarshalJSON(data []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + return r.fromMap(v) +} + +func (r *SchemaURL) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + if vv, ok := v["$schema"]; ok { + if str, ok := vv.(string); ok { + u, err := parseURL(str) + if err != nil { + return err + } + + *r = SchemaURL(u.String()) + } + } + return nil +} + +// SchemaProps describes a JSON schema (draft 4) +type SchemaProps struct { + ID string `json:"id,omitempty"` + Ref Ref `json:"-"` + Schema SchemaURL `json:"-"` + Description string `json:"description,omitempty"` + Type StringOrArray `json:"type,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Format string `json:"format,omitempty"` + Title string `json:"title,omitempty"` + Default interface{} `json:"default,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty"` + Required []string `json:"required,omitempty"` + Items *SchemaOrArray `json:"items,omitempty"` + AllOf []Schema `json:"allOf,omitempty"` + OneOf []Schema `json:"oneOf,omitempty"` + AnyOf []Schema `json:"anyOf,omitempty"` + Not *Schema `json:"not,omitempty"` + Properties SchemaProperties `json:"properties,omitempty"` + AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"` + PatternProperties SchemaProperties `json:"patternProperties,omitempty"` + Dependencies Dependencies `json:"dependencies,omitempty"` + AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"` + Definitions Definitions `json:"definitions,omitempty"` +} + +// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4) +type SwaggerSchemaProps struct { + Discriminator string `json:"discriminator,omitempty"` + ReadOnly bool `json:"readOnly,omitempty"` + XML *XMLObject `json:"xml,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// Schema the schema object allows the definition of input and output data types. +// These types can be objects, but also primitives and arrays. +// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) +// and uses a predefined subset of it. +// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. +// +// For more information: http://goo.gl/8us55a#schemaObject +type Schema struct { + VendorExtensible + SchemaProps + SwaggerSchemaProps + ExtraProps map[string]interface{} `json:"-"` +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s Schema) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + if ex, ok := s.ExtraProps[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SchemaProps, token) + if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) { + return r, err + } + r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token) + return r, err +} + +// WithID sets the id for this schema, allows for chaining +func (s *Schema) WithID(id string) *Schema { + s.ID = id + return s +} + +// WithTitle sets the title for this schema, allows for chaining +func (s *Schema) WithTitle(title string) *Schema { + s.Title = title + return s +} + +// WithDescription sets the description for this schema, allows for chaining +func (s *Schema) WithDescription(description string) *Schema { + s.Description = description + return s +} + +// WithProperties sets the properties for this schema +func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { + s.Properties = schemas + return s +} + +// SetProperty sets a property on this schema +func (s *Schema) SetProperty(name string, schema Schema) *Schema { + if s.Properties == nil { + s.Properties = make(map[string]Schema) + } + s.Properties[name] = schema + return s +} + +// WithAllOf sets the all of property +func (s *Schema) WithAllOf(schemas ...Schema) *Schema { + s.AllOf = schemas + return s +} + +// WithMaxProperties sets the max number of properties an object can have +func (s *Schema) WithMaxProperties(max int64) *Schema { + s.MaxProperties = &max + return s +} + +// WithMinProperties sets the min number of properties an object must have +func (s *Schema) WithMinProperties(min int64) *Schema { + s.MinProperties = &min + return s +} + +// Typed sets the type of this schema for a single value item +func (s *Schema) Typed(tpe, format string) *Schema { + s.Type = []string{tpe} + s.Format = format + return s +} + +// AddType adds a type with potential format to the types for this schema +func (s *Schema) AddType(tpe, format string) *Schema { + s.Type = append(s.Type, tpe) + if format != "" { + s.Format = format + } + return s +} + +// AsNullable flags this schema as nullable. +func (s *Schema) AsNullable() *Schema { + s.Nullable = true + return s +} + +// CollectionOf a fluent builder method for an array parameter +func (s *Schema) CollectionOf(items Schema) *Schema { + s.Type = []string{jsonArray} + s.Items = &SchemaOrArray{Schema: &items} + return s +} + +// WithDefault sets the default value on this parameter +func (s *Schema) WithDefault(defaultValue interface{}) *Schema { + s.Default = defaultValue + return s +} + +// WithRequired flags this parameter as required +func (s *Schema) WithRequired(items ...string) *Schema { + s.Required = items + return s +} + +// AddRequired adds field names to the required properties array +func (s *Schema) AddRequired(items ...string) *Schema { + s.Required = append(s.Required, items...) + return s +} + +// WithMaxLength sets a max length value +func (s *Schema) WithMaxLength(max int64) *Schema { + s.MaxLength = &max + return s +} + +// WithMinLength sets a min length value +func (s *Schema) WithMinLength(min int64) *Schema { + s.MinLength = &min + return s +} + +// WithPattern sets a pattern value +func (s *Schema) WithPattern(pattern string) *Schema { + s.Pattern = pattern + return s +} + +// WithMultipleOf sets a multiple of value +func (s *Schema) WithMultipleOf(number float64) *Schema { + s.MultipleOf = &number + return s +} + +// WithMaximum sets a maximum number value +func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { + s.Maximum = &max + s.ExclusiveMaximum = exclusive + return s +} + +// WithMinimum sets a minimum number value +func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { + s.Minimum = &min + s.ExclusiveMinimum = exclusive + return s +} + +// WithEnum sets a the enum values (replace) +func (s *Schema) WithEnum(values ...interface{}) *Schema { + s.Enum = append([]interface{}{}, values...) + return s +} + +// WithMaxItems sets the max items +func (s *Schema) WithMaxItems(size int64) *Schema { + s.MaxItems = &size + return s +} + +// WithMinItems sets the min items +func (s *Schema) WithMinItems(size int64) *Schema { + s.MinItems = &size + return s +} + +// UniqueValues dictates that this array can only have unique items +func (s *Schema) UniqueValues() *Schema { + s.UniqueItems = true + return s +} + +// AllowDuplicates this array can have duplicates +func (s *Schema) AllowDuplicates() *Schema { + s.UniqueItems = false + return s +} + +// AddToAllOf adds a schema to the allOf property +func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { + s.AllOf = append(s.AllOf, schemas...) + return s +} + +// WithDiscriminator sets the name of the discriminator field +func (s *Schema) WithDiscriminator(discriminator string) *Schema { + s.Discriminator = discriminator + return s +} + +// AsReadOnly flags this schema as readonly +func (s *Schema) AsReadOnly() *Schema { + s.ReadOnly = true + return s +} + +// AsWritable flags this schema as writeable (not read-only) +func (s *Schema) AsWritable() *Schema { + s.ReadOnly = false + return s +} + +// WithExample sets the example for this schema +func (s *Schema) WithExample(example interface{}) *Schema { + s.Example = example + return s +} + +// WithExternalDocs sets/removes the external docs for/from this schema. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (s *Schema) WithExternalDocs(description, url string) *Schema { + if description == "" && url == "" { + s.ExternalDocs = nil + return s + } + + if s.ExternalDocs == nil { + s.ExternalDocs = &ExternalDocumentation{} + } + s.ExternalDocs.Description = description + s.ExternalDocs.URL = url + return s +} + +// WithXMLName sets the xml name for the object +func (s *Schema) WithXMLName(name string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Name = name + return s +} + +// WithXMLNamespace sets the xml namespace for the object +func (s *Schema) WithXMLNamespace(namespace string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Namespace = namespace + return s +} + +// WithXMLPrefix sets the xml prefix for the object +func (s *Schema) WithXMLPrefix(prefix string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Prefix = prefix + return s +} + +// AsXMLAttribute flags this object as xml attribute +func (s *Schema) AsXMLAttribute() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = true + return s +} + +// AsXMLElement flags this object as an xml node +func (s *Schema) AsXMLElement() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = false + return s +} + +// AsWrappedXML flags this object as wrapped, this is mostly useful for array types +func (s *Schema) AsWrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = true + return s +} + +// AsUnwrappedXML flags this object as an xml node +func (s *Schema) AsUnwrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = false + return s +} + +// SetValidations defines all schema validations. +// +// NOTE: Required, ReadOnly, AllOf, AnyOf, OneOf and Not are not considered. +func (s *Schema) SetValidations(val SchemaValidations) { + s.Maximum = val.Maximum + s.ExclusiveMaximum = val.ExclusiveMaximum + s.Minimum = val.Minimum + s.ExclusiveMinimum = val.ExclusiveMinimum + s.MaxLength = val.MaxLength + s.MinLength = val.MinLength + s.Pattern = val.Pattern + s.MaxItems = val.MaxItems + s.MinItems = val.MinItems + s.UniqueItems = val.UniqueItems + s.MultipleOf = val.MultipleOf + s.Enum = val.Enum + s.MinProperties = val.MinProperties + s.MaxProperties = val.MaxProperties + s.PatternProperties = val.PatternProperties +} + +// WithValidations is a fluent method to set schema validations +func (s *Schema) WithValidations(val SchemaValidations) *Schema { + s.SetValidations(val) + return s +} + +// Validations returns a clone of the validations for this schema +func (s Schema) Validations() SchemaValidations { + return SchemaValidations{ + CommonValidations: CommonValidations{ + Maximum: s.Maximum, + ExclusiveMaximum: s.ExclusiveMaximum, + Minimum: s.Minimum, + ExclusiveMinimum: s.ExclusiveMinimum, + MaxLength: s.MaxLength, + MinLength: s.MinLength, + Pattern: s.Pattern, + MaxItems: s.MaxItems, + MinItems: s.MinItems, + UniqueItems: s.UniqueItems, + MultipleOf: s.MultipleOf, + Enum: s.Enum, + }, + MinProperties: s.MinProperties, + MaxProperties: s.MaxProperties, + PatternProperties: s.PatternProperties, + } +} + +// MarshalJSON marshal this to JSON +func (s Schema) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SchemaProps) + if err != nil { + return nil, fmt.Errorf("schema props %v", err) + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, fmt.Errorf("vendor props %v", err) + } + b3, err := s.Ref.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("ref prop %v", err) + } + b4, err := s.Schema.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("schema prop %v", err) + } + b5, err := json.Marshal(s.SwaggerSchemaProps) + if err != nil { + return nil, fmt.Errorf("common validations %v", err) + } + var b6 []byte + if s.ExtraProps != nil { + jj, err := json.Marshal(s.ExtraProps) + if err != nil { + return nil, fmt.Errorf("extra props %v", err) + } + b6 = jj + } + return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *Schema) UnmarshalJSON(data []byte) error { + props := struct { + SchemaProps + SwaggerSchemaProps + }{} + if err := json.Unmarshal(data, &props); err != nil { + return err + } + + sch := Schema{ + SchemaProps: props.SchemaProps, + SwaggerSchemaProps: props.SwaggerSchemaProps, + } + + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + _ = sch.Ref.fromMap(d) + _ = sch.Schema.fromMap(d) + + delete(d, "$ref") + delete(d, "$schema") + for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { + delete(d, pn) + } + + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if sch.Extensions == nil { + sch.Extensions = map[string]interface{}{} + } + sch.Extensions[k] = vv + continue + } + if sch.ExtraProps == nil { + sch.ExtraProps = map[string]interface{}{} + } + sch.ExtraProps[k] = vv + } + + *s = sch + + return nil +} diff --git a/vendor/github.com/go-openapi/spec/schema_loader.go b/vendor/github.com/go-openapi/spec/schema_loader.go new file mode 100644 index 00000000..b81175af --- /dev/null +++ b/vendor/github.com/go-openapi/spec/schema_loader.go @@ -0,0 +1,338 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "reflect" + "strings" + + "github.com/go-openapi/swag" +) + +// PathLoader is a function to use when loading remote refs. +// +// This is a package level default. It may be overridden or bypassed by +// specifying the loader in ExpandOptions. +// +// NOTE: if you are using the go-openapi/loads package, it will override +// this value with its own default (a loader to retrieve YAML documents as +// well as JSON ones). +var PathLoader = func(pth string) (json.RawMessage, error) { + data, err := swag.LoadFromFileOrHTTP(pth) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil +} + +// resolverContext allows to share a context during spec processing. +// At the moment, it just holds the index of circular references found. +type resolverContext struct { + // circulars holds all visited circular references, to shortcircuit $ref resolution. + // + // This structure is privately instantiated and needs not be locked against + // concurrent access, unless we chose to implement a parallel spec walking. + circulars map[string]bool + basePath string + loadDoc func(string) (json.RawMessage, error) + rootID string +} + +func newResolverContext(options *ExpandOptions) *resolverContext { + expandOptions := optionsOrDefault(options) + + // path loader may be overridden by options + var loader func(string) (json.RawMessage, error) + if expandOptions.PathLoader == nil { + loader = PathLoader + } else { + loader = expandOptions.PathLoader + } + + return &resolverContext{ + circulars: make(map[string]bool), + basePath: expandOptions.RelativeBase, // keep the root base path in context + loadDoc: loader, + } +} + +type schemaLoader struct { + root interface{} + options *ExpandOptions + cache ResolutionCache + context *resolverContext +} + +func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) *schemaLoader { + if ref.IsRoot() || ref.HasFragmentOnly { + return r + } + + baseRef := MustCreateRef(basePath) + currentRef := normalizeRef(&ref, basePath) + if strings.HasPrefix(currentRef.String(), baseRef.String()) { + return r + } + + // set a new root against which to resolve + rootURL := currentRef.GetURL() + rootURL.Fragment = "" + root, _ := r.cache.Get(rootURL.String()) + + // shallow copy of resolver options to set a new RelativeBase when + // traversing multiple documents + newOptions := r.options + newOptions.RelativeBase = rootURL.String() + + return defaultSchemaLoader(root, newOptions, r.cache, r.context) +} + +func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string) string { + if transitive != r { + if transitive.options != nil && transitive.options.RelativeBase != "" { + return normalizeBase(transitive.options.RelativeBase) + } + } + + return basePath +} + +func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) error { + tgt := reflect.ValueOf(target) + if tgt.Kind() != reflect.Ptr { + return ErrResolveRefNeedsAPointer + } + + if ref.GetURL() == nil { + return nil + } + + var ( + res interface{} + data interface{} + err error + ) + + // Resolve against the root if it isn't nil, and if ref is pointing at the root, or has a fragment only which means + // it is pointing somewhere in the root. + root := r.root + if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" { + if baseRef, erb := NewRef(basePath); erb == nil { + root, _, _, _ = r.load(baseRef.GetURL()) + } + } + + if (ref.IsRoot() || ref.HasFragmentOnly) && root != nil { + data = root + } else { + baseRef := normalizeRef(ref, basePath) + data, _, _, err = r.load(baseRef.GetURL()) + if err != nil { + return err + } + } + + res = data + if ref.String() != "" { + res, _, err = ref.GetPointer().Get(data) + if err != nil { + return err + } + } + return swag.DynamicJSONToStruct(res, target) +} + +func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) { + debugLog("loading schema from url: %s", refURL) + toFetch := *refURL + toFetch.Fragment = "" + + var err error + pth := toFetch.String() + normalized := normalizeBase(pth) + debugLog("loading doc from: %s", normalized) + + unescaped, err := url.PathUnescape(normalized) + if err != nil { + return nil, url.URL{}, false, err + } + + u := url.URL{Path: unescaped} + + data, fromCache := r.cache.Get(u.RequestURI()) + if fromCache { + return data, toFetch, fromCache, nil + } + + b, err := r.context.loadDoc(normalized) + if err != nil { + return nil, url.URL{}, false, err + } + + var doc interface{} + if err := json.Unmarshal(b, &doc); err != nil { + return nil, url.URL{}, false, err + } + r.cache.Set(normalized, doc) + + return doc, toFetch, fromCache, nil +} + +// isCircular detects cycles in sequences of $ref. +// +// It relies on a private context (which needs not be locked). +func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) { + normalizedRef := normalizeURI(ref.String(), basePath) + if _, ok := r.context.circulars[normalizedRef]; ok { + // circular $ref has been already detected in another explored cycle + foundCycle = true + return + } + foundCycle = swag.ContainsStrings(parentRefs, normalizedRef) // normalized windows url's are lower cased + if foundCycle { + r.context.circulars[normalizedRef] = true + } + return +} + +// Resolve resolves a reference against basePath and stores the result in target. +// +// Resolve is not in charge of following references: it only resolves ref by following its URL. +// +// If the schema the ref is referring to holds nested refs, Resolve doesn't resolve them. +// +// If basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct +func (r *schemaLoader) Resolve(ref *Ref, target interface{}, basePath string) error { + return r.resolveRef(ref, target, basePath) +} + +func (r *schemaLoader) deref(input interface{}, parentRefs []string, basePath string) error { + var ref *Ref + switch refable := input.(type) { + case *Schema: + ref = &refable.Ref + case *Parameter: + ref = &refable.Ref + case *Response: + ref = &refable.Ref + case *PathItem: + ref = &refable.Ref + default: + return fmt.Errorf("unsupported type: %T: %w", input, ErrDerefUnsupportedType) + } + + curRef := ref.String() + if curRef == "" { + return nil + } + + normalizedRef := normalizeRef(ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if r.isCircular(normalizedRef, basePath, parentRefs...) { + return nil + } + + if err := r.resolveRef(ref, input, basePath); r.shouldStopOnError(err) { + return err + } + + if ref.String() == "" || ref.String() == curRef { + // done with rereferencing + return nil + } + + parentRefs = append(parentRefs, normalizedRef.String()) + return r.deref(input, parentRefs, normalizedBasePath) +} + +func (r *schemaLoader) shouldStopOnError(err error) bool { + if err != nil && !r.options.ContinueOnError { + return true + } + + if err != nil { + log.Println(err) + } + + return false +} + +func (r *schemaLoader) setSchemaID(target interface{}, id, basePath string) (string, string) { + debugLog("schema has ID: %s", id) + + // handling the case when id is a folder + // remember that basePath has to point to a file + var refPath string + if strings.HasSuffix(id, "/") { + // ensure this is detected as a file, not a folder + refPath = fmt.Sprintf("%s%s", id, "placeholder.json") + } else { + refPath = id + } + + // updates the current base path + // * important: ID can be a relative path + // * registers target to be fetchable from the new base proposed by this id + newBasePath := normalizeURI(refPath, basePath) + + // store found IDs for possible future reuse in $ref + r.cache.Set(newBasePath, target) + + // the root document has an ID: all $ref relative to that ID may + // be rebased relative to the root document + if basePath == r.context.basePath { + debugLog("root document is a schema with ID: %s (normalized as:%s)", id, newBasePath) + r.context.rootID = newBasePath + } + + return newBasePath, refPath +} + +func defaultSchemaLoader( + root interface{}, + expandOptions *ExpandOptions, + cache ResolutionCache, + context *resolverContext) *schemaLoader { + + if expandOptions == nil { + expandOptions = &ExpandOptions{} + } + + cache = cacheOrDefault(cache) + + if expandOptions.RelativeBase == "" { + // if no relative base is provided, assume the root document + // contains all $ref, or at least, that the relative documents + // may be resolved from the current working directory. + expandOptions.RelativeBase = baseForRoot(root, cache) + } + debugLog("effective expander options: %#v", expandOptions) + + if context == nil { + context = newResolverContext(expandOptions) + } + + return &schemaLoader{ + root: root, + options: expandOptions, + cache: cache, + context: context, + } +} diff --git a/vendor/github.com/go-openapi/spec/security_scheme.go b/vendor/github.com/go-openapi/spec/security_scheme.go new file mode 100644 index 00000000..9d0bdae9 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/security_scheme.go @@ -0,0 +1,170 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + basic = "basic" + apiKey = "apiKey" + oauth2 = "oauth2" + implicit = "implicit" + password = "password" + application = "application" + accessCode = "accessCode" +) + +// BasicAuth creates a basic auth security scheme +func BasicAuth() *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: basic}} +} + +// APIKeyAuth creates an api key auth security scheme +func APIKeyAuth(fieldName, valueSource string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: apiKey, Name: fieldName, In: valueSource}} +} + +// OAuth2Implicit creates an implicit flow oauth2 security scheme +func OAuth2Implicit(authorizationURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: implicit, + AuthorizationURL: authorizationURL, + }} +} + +// OAuth2Password creates a password flow oauth2 security scheme +func OAuth2Password(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: password, + TokenURL: tokenURL, + }} +} + +// OAuth2Application creates an application flow oauth2 security scheme +func OAuth2Application(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: application, + TokenURL: tokenURL, + }} +} + +// OAuth2AccessToken creates an access token flow oauth2 security scheme +func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: accessCode, + AuthorizationURL: authorizationURL, + TokenURL: tokenURL, + }} +} + +// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section +type SecuritySchemeProps struct { + Description string `json:"description,omitempty"` + Type string `json:"type"` + Name string `json:"name,omitempty"` // api key + In string `json:"in,omitempty"` // api key + Flow string `json:"flow,omitempty"` // oauth2 + AuthorizationURL string `json:"authorizationUrl"` // oauth2 + TokenURL string `json:"tokenUrl,omitempty"` // oauth2 + Scopes map[string]string `json:"scopes,omitempty"` // oauth2 +} + +// AddScope adds a scope to this security scheme +func (s *SecuritySchemeProps) AddScope(scope, description string) { + if s.Scopes == nil { + s.Scopes = make(map[string]string) + } + s.Scopes[scope] = description +} + +// SecurityScheme allows the definition of a security scheme that can be used by the operations. +// Supported schemes are basic authentication, an API key (either as a header or as a query parameter) +// and OAuth2's common flows (implicit, password, application and access code). +// +// For more information: http://goo.gl/8us55a#securitySchemeObject +type SecurityScheme struct { + VendorExtensible + SecuritySchemeProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SecurityScheme) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SecuritySchemeProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (s SecurityScheme) MarshalJSON() ([]byte, error) { + var ( + b1 []byte + err error + ) + + if s.Type == oauth2 && (s.Flow == "implicit" || s.Flow == "accessCode") { + // when oauth2 for implicit or accessCode flows, empty AuthorizationURL is added as empty string + b1, err = json.Marshal(s.SecuritySchemeProps) + } else { + // when not oauth2, empty AuthorizationURL should be omitted + b1, err = json.Marshal(struct { + Description string `json:"description,omitempty"` + Type string `json:"type"` + Name string `json:"name,omitempty"` // api key + In string `json:"in,omitempty"` // api key + Flow string `json:"flow,omitempty"` // oauth2 + AuthorizationURL string `json:"authorizationUrl,omitempty"` // oauth2 + TokenURL string `json:"tokenUrl,omitempty"` // oauth2 + Scopes map[string]string `json:"scopes,omitempty"` // oauth2 + }{ + Description: s.Description, + Type: s.Type, + Name: s.Name, + In: s.In, + Flow: s.Flow, + AuthorizationURL: s.AuthorizationURL, + TokenURL: s.TokenURL, + Scopes: s.Scopes, + }) + } + if err != nil { + return nil, err + } + + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *SecurityScheme) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil { + return err + } + return json.Unmarshal(data, &s.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/spec.go b/vendor/github.com/go-openapi/spec/spec.go new file mode 100644 index 00000000..7d38b6e6 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/spec.go @@ -0,0 +1,78 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" +) + +//go:generate curl -L --progress -o ./schemas/v2/schema.json http://swagger.io/v2/schema.json +//go:generate curl -L --progress -o ./schemas/jsonschema-draft-04.json http://json-schema.org/draft-04/schema +//go:generate go-bindata -pkg=spec -prefix=./schemas -ignore=.*\.md ./schemas/... +//go:generate perl -pi -e s,Json,JSON,g bindata.go + +const ( + // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs + SwaggerSchemaURL = "http://swagger.io/v2/schema.json#" + // JSONSchemaURL the url for the json schema schema + JSONSchemaURL = "http://json-schema.org/draft-04/schema#" +) + +// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error +func MustLoadJSONSchemaDraft04() *Schema { + d, e := JSONSchemaDraft04() + if e != nil { + panic(e) + } + return d +} + +// JSONSchemaDraft04 loads the json schema document for json shema draft04 +func JSONSchemaDraft04() (*Schema, error) { + b, err := Asset("jsonschema-draft-04.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} + +// MustLoadSwagger20Schema panics when Swagger20Schema returns an error +func MustLoadSwagger20Schema() *Schema { + d, e := Swagger20Schema() + if e != nil { + panic(e) + } + return d +} + +// Swagger20Schema loads the swagger 2.0 schema from the embedded assets +func Swagger20Schema() (*Schema, error) { + + b, err := Asset("v2/schema.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} diff --git a/vendor/github.com/go-openapi/spec/swagger.go b/vendor/github.com/go-openapi/spec/swagger.go new file mode 100644 index 00000000..44722ffd --- /dev/null +++ b/vendor/github.com/go-openapi/spec/swagger.go @@ -0,0 +1,448 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "fmt" + "strconv" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Swagger this is the root document object for the API specification. +// It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier) +// together into one document. +// +// For more information: http://goo.gl/8us55a#swagger-object- +type Swagger struct { + VendorExtensible + SwaggerProps +} + +// JSONLookup look up a value by the json property name +func (s Swagger) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token) + return r, err +} + +// MarshalJSON marshals this swagger structure to json +func (s Swagger) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SwaggerProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON unmarshals a swagger spec from json +func (s *Swagger) UnmarshalJSON(data []byte) error { + var sw Swagger + if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil { + return err + } + if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil { + return err + } + *s = sw + return nil +} + +// GobEncode provides a safe gob encoder for Swagger, including extensions +func (s Swagger) GobEncode() ([]byte, error) { + var b bytes.Buffer + raw := struct { + Props SwaggerProps + Ext VendorExtensible + }{ + Props: s.SwaggerProps, + Ext: s.VendorExtensible, + } + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Swagger, including extensions +func (s *Swagger) GobDecode(b []byte) error { + var raw struct { + Props SwaggerProps + Ext VendorExtensible + } + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + s.SwaggerProps = raw.Props + s.VendorExtensible = raw.Ext + return nil +} + +// SwaggerProps captures the top-level properties of an Api specification +// +// NOTE: validation rules +// - the scheme, when present must be from [http, https, ws, wss] +// - BasePath must start with a leading "/" +// - Paths is required +type SwaggerProps struct { + ID string `json:"id,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` + Swagger string `json:"swagger,omitempty"` + Info *Info `json:"info,omitempty"` + Host string `json:"host,omitempty"` + BasePath string `json:"basePath,omitempty"` + Paths *Paths `json:"paths"` + Definitions Definitions `json:"definitions,omitempty"` + Parameters map[string]Parameter `json:"parameters,omitempty"` + Responses map[string]Response `json:"responses,omitempty"` + SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"` + Security []map[string][]string `json:"security,omitempty"` + Tags []Tag `json:"tags,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +type swaggerPropsAlias SwaggerProps + +type gobSwaggerPropsAlias struct { + Security []map[string]struct { + List []string + Pad bool + } + Alias *swaggerPropsAlias + SecurityIsEmpty bool +} + +// GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements +func (o SwaggerProps) GobEncode() ([]byte, error) { + raw := gobSwaggerPropsAlias{ + Alias: (*swaggerPropsAlias)(&o), + } + + var b bytes.Buffer + if o.Security == nil { + // nil security requirement + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + if len(o.Security) == 0 { + // empty, but non-nil security requirement + raw.SecurityIsEmpty = true + raw.Alias.Security = nil + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + raw.Security = make([]map[string]struct { + List []string + Pad bool + }, 0, len(o.Security)) + for _, req := range o.Security { + v := make(map[string]struct { + List []string + Pad bool + }, len(req)) + for k, val := range req { + v[k] = struct { + List []string + Pad bool + }{ + List: val, + } + } + raw.Security = append(raw.Security, v) + } + + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements +func (o *SwaggerProps) GobDecode(b []byte) error { + var raw gobSwaggerPropsAlias + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + if raw.Alias == nil { + return nil + } + + switch { + case raw.SecurityIsEmpty: + // empty, but non-nil security requirement + raw.Alias.Security = []map[string][]string{} + case len(raw.Alias.Security) == 0: + // nil security requirement + raw.Alias.Security = nil + default: + raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security)) + for _, req := range raw.Security { + v := make(map[string][]string, len(req)) + for k, val := range req { + v[k] = make([]string, 0, len(val.List)) + v[k] = append(v[k], val.List...) + } + raw.Alias.Security = append(raw.Alias.Security, v) + } + } + + *o = *(*SwaggerProps)(raw.Alias) + return nil +} + +// Dependencies represent a dependencies property +type Dependencies map[string]SchemaOrStringArray + +// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property +type SchemaOrBool struct { + Allows bool + Schema *Schema +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) { + if token == "allows" { + return s.Allows, nil + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +var jsTrue = []byte("true") +var jsFalse = []byte("false") + +// MarshalJSON convert this object to JSON +func (s SchemaOrBool) MarshalJSON() ([]byte, error) { + if s.Schema != nil { + return json.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return jsFalse, nil + } + return jsTrue, nil +} + +// UnmarshalJSON converts this bool or schema object from a JSON structure +func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { + var nw SchemaOrBool + if len(data) >= 4 { + if data[0] == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e') + } + *s = nw + return nil +} + +// SchemaOrStringArray represents a schema or a string array +type SchemaOrStringArray struct { + Schema *Schema + Property []string +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) { + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) { + if len(s.Property) > 0 { + return json.Marshal(s.Property) + } + if s.Schema != nil { + return json.Marshal(s.Schema) + } + return []byte("null"), nil +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + var nw SchemaOrStringArray + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Property); err != nil { + return err + } + } + *s = nw + return nil +} + +// Definitions contains the models explicitly defined in this spec +// An object to hold data types that can be consumed and produced by operations. +// These data types can be primitives, arrays or models. +// +// For more information: http://goo.gl/8us55a#definitionsObject +type Definitions map[string]Schema + +// SecurityDefinitions a declaration of the security schemes available to be used in the specification. +// This does not enforce the security schemes on the operations and only serves to provide +// the relevant details for each scheme. +// +// For more information: http://goo.gl/8us55a#securityDefinitionsObject +type SecurityDefinitions map[string]*SecurityScheme + +// StringOrArray represents a value that can either be a string +// or an array of strings. Mainly here for serialization purposes +type StringOrArray []string + +// Contains returns true when the value is contained in the slice +func (s StringOrArray) Contains(value string) bool { + for _, str := range s { + if str == value { + return true + } + } + return false +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) { + if _, err := strconv.Atoi(token); err == nil { + r, _, err := jsonpointer.GetForToken(s.Schemas, token) + return r, err + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string +func (s *StringOrArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + + if first == '[' { + var parsed []string + if err := json.Unmarshal(data, &parsed); err != nil { + return err + } + *s = StringOrArray(parsed) + return nil + } + + var single interface{} + if err := json.Unmarshal(data, &single); err != nil { + return err + } + if single == nil { + return nil + } + switch v := single.(type) { + case string: + *s = StringOrArray([]string{v}) + return nil + default: + return fmt.Errorf("only string or array is allowed, not %T", single) + } +} + +// MarshalJSON converts this string or array to a JSON array or JSON string +func (s StringOrArray) MarshalJSON() ([]byte, error) { + if len(s) == 1 { + return json.Marshal([]string(s)[0]) + } + return json.Marshal([]string(s)) +} + +// SchemaOrArray represents a value that can either be a Schema +// or an array of Schema. Mainly here for serialization purposes +type SchemaOrArray struct { + Schema *Schema + Schemas []Schema +} + +// Len returns the number of schemas in this property +func (s SchemaOrArray) Len() int { + if s.Schema != nil { + return 1 + } + return len(s.Schemas) +} + +// ContainsType returns true when one of the schemas is of the specified type +func (s *SchemaOrArray) ContainsType(name string) bool { + if s.Schema != nil { + return s.Schema.Type != nil && s.Schema.Type.Contains(name) + } + return false +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrArray) MarshalJSON() ([]byte, error) { + if len(s.Schemas) > 0 { + return json.Marshal(s.Schemas) + } + return json.Marshal(s.Schema) +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrArray) UnmarshalJSON(data []byte) error { + var nw SchemaOrArray + var first byte + if len(data) > 1 { + first = data[0] + } + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Schemas); err != nil { + return err + } + } + *s = nw + return nil +} + +// vim:set ft=go noet sts=2 sw=2 ts=2: diff --git a/vendor/github.com/go-openapi/spec/tag.go b/vendor/github.com/go-openapi/spec/tag.go new file mode 100644 index 00000000..faa3d3de --- /dev/null +++ b/vendor/github.com/go-openapi/spec/tag.go @@ -0,0 +1,75 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// TagProps describe a tag entry in the top level tags section of a swagger spec +type TagProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +// NewTag creates a new tag +func NewTag(name, description string, externalDocs *ExternalDocumentation) Tag { + return Tag{TagProps: TagProps{Description: description, Name: name, ExternalDocs: externalDocs}} +} + +// Tag allows adding meta data to a single tag that is used by the +// [Operation Object](http://goo.gl/8us55a#operationObject). +// It is not mandatory to have a Tag Object per tag used there. +// +// For more information: http://goo.gl/8us55a#tagObject +type Tag struct { + VendorExtensible + TagProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (t Tag) JSONLookup(token string) (interface{}, error) { + if ex, ok := t.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(t.TagProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (t Tag) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(t.TagProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(t.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (t *Tag) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &t.TagProps); err != nil { + return err + } + return json.Unmarshal(data, &t.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/url_go18.go b/vendor/github.com/go-openapi/spec/url_go18.go new file mode 100644 index 00000000..60b78515 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/url_go18.go @@ -0,0 +1,8 @@ +//go:build !go1.19 +// +build !go1.19 + +package spec + +import "net/url" + +var parseURL = url.Parse diff --git a/vendor/github.com/go-openapi/spec/url_go19.go b/vendor/github.com/go-openapi/spec/url_go19.go new file mode 100644 index 00000000..392e3e63 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/url_go19.go @@ -0,0 +1,14 @@ +//go:build go1.19 +// +build go1.19 + +package spec + +import "net/url" + +func parseURL(s string) (*url.URL, error) { + u, err := url.Parse(s) + if err == nil { + u.OmitHost = false + } + return u, err +} diff --git a/vendor/github.com/go-openapi/spec/validations.go b/vendor/github.com/go-openapi/spec/validations.go new file mode 100644 index 00000000..6360a8ea --- /dev/null +++ b/vendor/github.com/go-openapi/spec/validations.go @@ -0,0 +1,215 @@ +package spec + +// CommonValidations describe common JSON-schema validations +type CommonValidations struct { + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` +} + +// SetValidations defines all validations for a simple schema. +// +// NOTE: the input is the larger set of validations available for schemas. +// For simple schemas, MinProperties and MaxProperties are ignored. +func (v *CommonValidations) SetValidations(val SchemaValidations) { + v.Maximum = val.Maximum + v.ExclusiveMaximum = val.ExclusiveMaximum + v.Minimum = val.Minimum + v.ExclusiveMinimum = val.ExclusiveMinimum + v.MaxLength = val.MaxLength + v.MinLength = val.MinLength + v.Pattern = val.Pattern + v.MaxItems = val.MaxItems + v.MinItems = val.MinItems + v.UniqueItems = val.UniqueItems + v.MultipleOf = val.MultipleOf + v.Enum = val.Enum +} + +type clearedValidation struct { + Validation string + Value interface{} +} + +type clearedValidations []clearedValidation + +func (c clearedValidations) apply(cbs []func(string, interface{})) { + for _, cb := range cbs { + for _, cleared := range c { + cb(cleared.Validation, cleared.Value) + } + } +} + +// ClearNumberValidations clears all number validations. +// +// Some callbacks may be set by the caller to capture changed values. +func (v *CommonValidations) ClearNumberValidations(cbs ...func(string, interface{})) { + done := make(clearedValidations, 0, 5) + defer func() { + done.apply(cbs) + }() + + if v.Minimum != nil { + done = append(done, clearedValidation{Validation: "minimum", Value: v.Minimum}) + v.Minimum = nil + } + if v.Maximum != nil { + done = append(done, clearedValidation{Validation: "maximum", Value: v.Maximum}) + v.Maximum = nil + } + if v.ExclusiveMaximum { + done = append(done, clearedValidation{Validation: "exclusiveMaximum", Value: v.ExclusiveMaximum}) + v.ExclusiveMaximum = false + } + if v.ExclusiveMinimum { + done = append(done, clearedValidation{Validation: "exclusiveMinimum", Value: v.ExclusiveMinimum}) + v.ExclusiveMinimum = false + } + if v.MultipleOf != nil { + done = append(done, clearedValidation{Validation: "multipleOf", Value: v.MultipleOf}) + v.MultipleOf = nil + } +} + +// ClearStringValidations clears all string validations. +// +// Some callbacks may be set by the caller to capture changed values. +func (v *CommonValidations) ClearStringValidations(cbs ...func(string, interface{})) { + done := make(clearedValidations, 0, 3) + defer func() { + done.apply(cbs) + }() + + if v.Pattern != "" { + done = append(done, clearedValidation{Validation: "pattern", Value: v.Pattern}) + v.Pattern = "" + } + if v.MinLength != nil { + done = append(done, clearedValidation{Validation: "minLength", Value: v.MinLength}) + v.MinLength = nil + } + if v.MaxLength != nil { + done = append(done, clearedValidation{Validation: "maxLength", Value: v.MaxLength}) + v.MaxLength = nil + } +} + +// ClearArrayValidations clears all array validations. +// +// Some callbacks may be set by the caller to capture changed values. +func (v *CommonValidations) ClearArrayValidations(cbs ...func(string, interface{})) { + done := make(clearedValidations, 0, 3) + defer func() { + done.apply(cbs) + }() + + if v.MaxItems != nil { + done = append(done, clearedValidation{Validation: "maxItems", Value: v.MaxItems}) + v.MaxItems = nil + } + if v.MinItems != nil { + done = append(done, clearedValidation{Validation: "minItems", Value: v.MinItems}) + v.MinItems = nil + } + if v.UniqueItems { + done = append(done, clearedValidation{Validation: "uniqueItems", Value: v.UniqueItems}) + v.UniqueItems = false + } +} + +// Validations returns a clone of the validations for a simple schema. +// +// NOTE: in the context of simple schema objects, MinProperties, MaxProperties +// and PatternProperties remain unset. +func (v CommonValidations) Validations() SchemaValidations { + return SchemaValidations{ + CommonValidations: v, + } +} + +// HasNumberValidations indicates if the validations are for numbers or integers +func (v CommonValidations) HasNumberValidations() bool { + return v.Maximum != nil || v.Minimum != nil || v.MultipleOf != nil +} + +// HasStringValidations indicates if the validations are for strings +func (v CommonValidations) HasStringValidations() bool { + return v.MaxLength != nil || v.MinLength != nil || v.Pattern != "" +} + +// HasArrayValidations indicates if the validations are for arrays +func (v CommonValidations) HasArrayValidations() bool { + return v.MaxItems != nil || v.MinItems != nil || v.UniqueItems +} + +// HasEnum indicates if the validation includes some enum constraint +func (v CommonValidations) HasEnum() bool { + return len(v.Enum) > 0 +} + +// SchemaValidations describes the validation properties of a schema +// +// NOTE: at this moment, this is not embedded in SchemaProps because this would induce a breaking change +// in the exported members: all initializers using litterals would fail. +type SchemaValidations struct { + CommonValidations + + PatternProperties SchemaProperties `json:"patternProperties,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty"` +} + +// HasObjectValidations indicates if the validations are for objects +func (v SchemaValidations) HasObjectValidations() bool { + return v.MaxProperties != nil || v.MinProperties != nil || v.PatternProperties != nil +} + +// SetValidations for schema validations +func (v *SchemaValidations) SetValidations(val SchemaValidations) { + v.CommonValidations.SetValidations(val) + v.PatternProperties = val.PatternProperties + v.MaxProperties = val.MaxProperties + v.MinProperties = val.MinProperties +} + +// Validations for a schema +func (v SchemaValidations) Validations() SchemaValidations { + val := v.CommonValidations.Validations() + val.PatternProperties = v.PatternProperties + val.MinProperties = v.MinProperties + val.MaxProperties = v.MaxProperties + return val +} + +// ClearObjectValidations returns a clone of the validations with all object validations cleared. +// +// Some callbacks may be set by the caller to capture changed values. +func (v *SchemaValidations) ClearObjectValidations(cbs ...func(string, interface{})) { + done := make(clearedValidations, 0, 3) + defer func() { + done.apply(cbs) + }() + + if v.MaxProperties != nil { + done = append(done, clearedValidation{Validation: "maxProperties", Value: v.MaxProperties}) + v.MaxProperties = nil + } + if v.MinProperties != nil { + done = append(done, clearedValidation{Validation: "minProperties", Value: v.MinProperties}) + v.MinProperties = nil + } + if v.PatternProperties != nil { + done = append(done, clearedValidation{Validation: "patternProperties", Value: v.PatternProperties}) + v.PatternProperties = nil + } +} diff --git a/vendor/github.com/go-openapi/spec/xml_object.go b/vendor/github.com/go-openapi/spec/xml_object.go new file mode 100644 index 00000000..945a4670 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/xml_object.go @@ -0,0 +1,68 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package spec + +// XMLObject a metadata object that allows for more fine-tuned XML model definitions. +// +// For more information: http://goo.gl/8us55a#xmlObject +type XMLObject struct { + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty"` + Wrapped bool `json:"wrapped,omitempty"` +} + +// WithName sets the xml name for the object +func (x *XMLObject) WithName(name string) *XMLObject { + x.Name = name + return x +} + +// WithNamespace sets the xml namespace for the object +func (x *XMLObject) WithNamespace(namespace string) *XMLObject { + x.Namespace = namespace + return x +} + +// WithPrefix sets the xml prefix for the object +func (x *XMLObject) WithPrefix(prefix string) *XMLObject { + x.Prefix = prefix + return x +} + +// AsAttribute flags this object as xml attribute +func (x *XMLObject) AsAttribute() *XMLObject { + x.Attribute = true + return x +} + +// AsElement flags this object as an xml node +func (x *XMLObject) AsElement() *XMLObject { + x.Attribute = false + return x +} + +// AsWrapped flags this object as wrapped, this is mostly useful for array types +func (x *XMLObject) AsWrapped() *XMLObject { + x.Wrapped = true + return x +} + +// AsUnwrapped flags this object as an xml node +func (x *XMLObject) AsUnwrapped() *XMLObject { + x.Wrapped = false + return x +} diff --git a/vendor/github.com/go-openapi/strfmt/.editorconfig b/vendor/github.com/go-openapi/strfmt/.editorconfig new file mode 100644 index 00000000..3152da69 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/strfmt/.gitattributes b/vendor/github.com/go-openapi/strfmt/.gitattributes new file mode 100644 index 00000000..d020be8e --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/.gitattributes @@ -0,0 +1,2 @@ +*.go text eol=lf + diff --git a/vendor/github.com/go-openapi/strfmt/.gitignore b/vendor/github.com/go-openapi/strfmt/.gitignore new file mode 100644 index 00000000..dd91ed6a --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/vendor/github.com/go-openapi/strfmt/.golangci.yml b/vendor/github.com/go-openapi/strfmt/.golangci.yml new file mode 100644 index 00000000..d36b2566 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/.golangci.yml @@ -0,0 +1,50 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 31 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoinits + - gochecknoglobals + - godox + - gocognit + - whitespace + - wsl + - funlen + - wrapcheck + - testpackage + - nlreturn + - gofumpt + - goerr113 + - gci + - gomnd + - godot + - exhaustivestruct + - paralleltest + - varnamelen + - ireturn + - exhaustruct + #- thelper + +issues: + exclude-rules: + - path: bson.go + text: "should be .*ObjectID" + linters: + - golint + - stylecheck + diff --git a/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..9322b065 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/strfmt/LICENSE b/vendor/github.com/go-openapi/strfmt/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/strfmt/README.md b/vendor/github.com/go-openapi/strfmt/README.md new file mode 100644 index 00000000..0cf89d77 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/README.md @@ -0,0 +1,88 @@ +# Strfmt [![Build Status](https://travis-ci.org/go-openapi/strfmt.svg?branch=master)](https://travis-ci.org/go-openapi/strfmt) [![codecov](https://codecov.io/gh/go-openapi/strfmt/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/strfmt) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/strfmt/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/strfmt?status.svg)](http://godoc.org/github.com/go-openapi/strfmt) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/strfmt.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/strfmt)](https://goreportcard.com/report/github.com/go-openapi/strfmt) + +This package exposes a registry of data types to support string formats in the go-openapi toolkit. + +strfmt represents a well known string format such as credit card or email. The go toolkit for OpenAPI specifications knows how to deal with those. + +## Supported data formats +go-openapi/strfmt follows the swagger 2.0 specification with the following formats +defined [here](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types). + +It also provides convenient extensions to go-openapi users. + +- [x] JSON-schema draft 4 formats + - date-time + - email + - hostname + - ipv4 + - ipv6 + - uri +- [x] swagger 2.0 format extensions + - binary + - byte (e.g. base64 encoded string) + - date (e.g. "1970-01-01") + - password +- [x] go-openapi custom format extensions + - bsonobjectid (BSON objectID) + - creditcard + - duration (e.g. "3 weeks", "1ms") + - hexcolor (e.g. "#FFFFFF") + - isbn, isbn10, isbn13 + - mac (e.g "01:02:03:04:05:06") + - rgbcolor (e.g. "rgb(100,100,100)") + - ssn + - uuid, uuid3, uuid4, uuid5 + - cidr (e.g. "192.0.2.1/24", "2001:db8:a0b:12f0::1/32") + - ulid (e.g. "00000PP9HGSBSSDZ1JTEXBJ0PW", [spec](https://github.com/ulid/spec)) + +> NOTE: as the name stands for, this package is intended to support string formatting only. +> It does not provide validation for numerical values with swagger format extension for JSON types "number" or +> "integer" (e.g. float, double, int32...). + +## Type conversion + +All types defined here are stringers and may be converted to strings with `.String()`. +Note that most types defined by this package may be converted directly to string like `string(Email{})`. + +`Date` and `DateTime` may be converted directly to `time.Time` like `time.Time(Time{})`. +Similarly, you can convert `Duration` to `time.Duration` as in `time.Duration(Duration{})` + +## Using pointers + +The `conv` subpackage provides helpers to convert the types to and from pointers, just like `go-openapi/swag` does +with primitive types. + +## Format types +Types defined in strfmt expose marshaling and validation capabilities. + +List of defined types: +- Base64 +- CreditCard +- Date +- DateTime +- Duration +- Email +- HexColor +- Hostname +- IPv4 +- IPv6 +- CIDR +- ISBN +- ISBN10 +- ISBN13 +- MAC +- ObjectId +- Password +- RGBColor +- SSN +- URI +- UUID +- UUID3 +- UUID4 +- UUID5 +- [ULID](https://github.com/ulid/spec) diff --git a/vendor/github.com/go-openapi/strfmt/bson.go b/vendor/github.com/go-openapi/strfmt/bson.go new file mode 100644 index 00000000..8740b150 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/bson.go @@ -0,0 +1,165 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strfmt + +import ( + "database/sql/driver" + "fmt" + + "go.mongodb.org/mongo-driver/bson" + + "go.mongodb.org/mongo-driver/bson/bsontype" + bsonprim "go.mongodb.org/mongo-driver/bson/primitive" +) + +func init() { + var id ObjectId + // register this format in the default registry + Default.Add("bsonobjectid", &id, IsBSONObjectID) +} + +// IsBSONObjectID returns true when the string is a valid BSON.ObjectId +func IsBSONObjectID(str string) bool { + _, err := bsonprim.ObjectIDFromHex(str) + return err == nil +} + +// ObjectId represents a BSON object ID (alias to go.mongodb.org/mongo-driver/bson/primitive.ObjectID) +// +// swagger:strfmt bsonobjectid +type ObjectId bsonprim.ObjectID //nolint:revive + +// NewObjectId creates a ObjectId from a Hex String +func NewObjectId(hex string) ObjectId { //nolint:revive + oid, err := bsonprim.ObjectIDFromHex(hex) + if err != nil { + panic(err) + } + return ObjectId(oid) +} + +// MarshalText turns this instance into text +func (id ObjectId) MarshalText() ([]byte, error) { + oid := bsonprim.ObjectID(id) + if oid == bsonprim.NilObjectID { + return nil, nil + } + return []byte(oid.Hex()), nil +} + +// UnmarshalText hydrates this instance from text +func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performed later on + if len(data) == 0 { + *id = ObjectId(bsonprim.NilObjectID) + return nil + } + oidstr := string(data) + oid, err := bsonprim.ObjectIDFromHex(oidstr) + if err != nil { + return err + } + *id = ObjectId(oid) + return nil +} + +// Scan read a value from a database driver +func (id *ObjectId) Scan(raw interface{}) error { + var data []byte + switch v := raw.(type) { + case []byte: + data = v + case string: + data = []byte(v) + default: + return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v", v) + } + + return id.UnmarshalText(data) +} + +// Value converts a value to a database driver value +func (id ObjectId) Value() (driver.Value, error) { + return driver.Value(bsonprim.ObjectID(id).Hex()), nil +} + +func (id ObjectId) String() string { + return bsonprim.ObjectID(id).Hex() +} + +// MarshalJSON returns the ObjectId as JSON +func (id ObjectId) MarshalJSON() ([]byte, error) { + return bsonprim.ObjectID(id).MarshalJSON() +} + +// UnmarshalJSON sets the ObjectId from JSON +func (id *ObjectId) UnmarshalJSON(data []byte) error { + var obj bsonprim.ObjectID + if err := obj.UnmarshalJSON(data); err != nil { + return err + } + *id = ObjectId(obj) + return nil +} + +// MarshalBSON renders the object id as a BSON document +func (id ObjectId) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) +} + +// UnmarshalBSON reads the objectId from a BSON document +func (id *ObjectId) UnmarshalBSON(data []byte) error { + var obj struct { + Data bsonprim.ObjectID + } + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + *id = ObjectId(obj.Data) + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { + oid := bsonprim.ObjectID(id) + return bsontype.ObjectID, oid[:], nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (id *ObjectId) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { + var oid bsonprim.ObjectID + copy(oid[:], data) + *id = ObjectId(oid) + return nil +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (id *ObjectId) DeepCopyInto(out *ObjectId) { + *out = *id +} + +// DeepCopy copies the receiver into a new ObjectId. +func (id *ObjectId) DeepCopy() *ObjectId { + if id == nil { + return nil + } + out := new(ObjectId) + id.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/go-openapi/strfmt/date.go b/vendor/github.com/go-openapi/strfmt/date.go new file mode 100644 index 00000000..f0b31096 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/date.go @@ -0,0 +1,187 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strfmt + +import ( + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "time" + + "go.mongodb.org/mongo-driver/bson" +) + +func init() { + d := Date{} + // register this format in the default registry + Default.Add("date", &d, IsDate) +} + +// IsDate returns true when the string is a valid date +func IsDate(str string) bool { + _, err := time.Parse(RFC3339FullDate, str) + return err == nil +} + +const ( + // RFC3339FullDate represents a full-date as specified by RFC3339 + // See: http://goo.gl/xXOvVd + RFC3339FullDate = "2006-01-02" +) + +// Date represents a date from the API +// +// swagger:strfmt date +type Date time.Time + +// String converts this date into a string +func (d Date) String() string { + return time.Time(d).Format(RFC3339FullDate) +} + +// UnmarshalText parses a text representation into a date type +func (d *Date) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + dd, err := time.Parse(RFC3339FullDate, string(text)) + if err != nil { + return err + } + *d = Date(dd) + return nil +} + +// MarshalText serializes this date type to string +func (d Date) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +// Scan scans a Date value from database driver type. +func (d *Date) Scan(raw interface{}) error { + switch v := raw.(type) { + case []byte: + return d.UnmarshalText(v) + case string: + return d.UnmarshalText([]byte(v)) + case time.Time: + *d = Date(v) + return nil + case nil: + *d = Date{} + return nil + default: + return fmt.Errorf("cannot sql.Scan() strfmt.Date from: %#v", v) + } +} + +// Value converts Date to a primitive value ready to written to a database. +func (d Date) Value() (driver.Value, error) { + return driver.Value(d.String()), nil +} + +// MarshalJSON returns the Date as JSON +func (d Date) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Time(d).Format(RFC3339FullDate)) +} + +// UnmarshalJSON sets the Date from JSON +func (d *Date) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + var strdate string + if err := json.Unmarshal(data, &strdate); err != nil { + return err + } + tt, err := time.Parse(RFC3339FullDate, strdate) + if err != nil { + return err + } + *d = Date(tt) + return nil +} + +func (d Date) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Date) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := time.Parse(RFC3339FullDate, data) + if err != nil { + return err + } + *d = Date(rd) + return nil + } + + return errors.New("couldn't unmarshal bson bytes value as Date") +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (d *Date) DeepCopyInto(out *Date) { + *out = *d +} + +// DeepCopy copies the receiver into a new Date. +func (d *Date) DeepCopy() *Date { + if d == nil { + return nil + } + out := new(Date) + d.DeepCopyInto(out) + return out +} + +// GobEncode implements the gob.GobEncoder interface. +func (d Date) GobEncode() ([]byte, error) { + return d.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface. +func (d *Date) GobDecode(data []byte) error { + return d.UnmarshalBinary(data) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d Date) MarshalBinary() ([]byte, error) { + return time.Time(d).MarshalBinary() +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Date) UnmarshalBinary(data []byte) error { + var original time.Time + + err := original.UnmarshalBinary(data) + if err != nil { + return err + } + + *d = Date(original) + + return nil +} + +// Equal checks if two Date instances are equal +func (d Date) Equal(d2 Date) bool { + return time.Time(d).Equal(time.Time(d2)) +} diff --git a/vendor/github.com/go-openapi/strfmt/default.go b/vendor/github.com/go-openapi/strfmt/default.go new file mode 100644 index 00000000..a89a4de3 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/default.go @@ -0,0 +1,2035 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strfmt + +import ( + "database/sql/driver" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/mail" + "regexp" + "strings" + + "github.com/asaskevich/govalidator" + "go.mongodb.org/mongo-driver/bson" +) + +const ( + // HostnamePattern http://json-schema.org/latest/json-schema-validation.html#anchor114 + // A string instance is valid against this attribute if it is a valid + // representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. + // http://tools.ietf.org/html/rfc1034#section-3.5 + // ::= any one of the ten digits 0 through 9 + // var digit = /[0-9]/; + // ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case + // var letter = /[a-zA-Z]/; + // ::= | + // var letDig = /[0-9a-zA-Z]/; + // ::= | "-" + // var letDigHyp = /[-0-9a-zA-Z]/; + // ::= | + // var ldhStr = /[-0-9a-zA-Z]+/; + //