From 03bdcf5cd3658a08f77278158563b9eb652b39cf Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Sat, 22 Apr 2023 13:34:20 -0400 Subject: [PATCH 1/2] add WalkMetasFS and WalkMetasReader functions These function are like WalkFS except that callers provide a function that handles a Meta object at a time, rather than an entire file's worth of FBC objects. For callers that can operate on a single meta at a time, these functions will provide significant reductions in memory usage. Signed-off-by: Joe Lanford --- alpha/declcfg/declcfg.go | 3 ++ alpha/declcfg/load.go | 108 ++++++++++++++++++++++++++----------- alpha/declcfg/load_test.go | 67 +++++++++++++++++++++++ 3 files changed, 148 insertions(+), 30 deletions(-) diff --git a/alpha/declcfg/declcfg.go b/alpha/declcfg/declcfg.go index d575987b1..8e2b761c0 100644 --- a/alpha/declcfg/declcfg.go +++ b/alpha/declcfg/declcfg.go @@ -84,6 +84,7 @@ type RelatedImage struct { type Meta struct { Schema string Package string + Name string Blob json.RawMessage } @@ -96,6 +97,7 @@ func (m *Meta) UnmarshalJSON(blob []byte) error { type tmp struct { Schema string `json:"schema"` Package string `json:"package,omitempty"` + Name string `json:"name,omitempty"` Properties []property.Property `json:"properties,omitempty"` } var t tmp @@ -104,6 +106,7 @@ func (m *Meta) UnmarshalJSON(blob []byte) error { } m.Schema = t.Schema m.Package = t.Package + m.Name = t.Name m.Blob = blob return nil } diff --git a/alpha/declcfg/load.go b/alpha/declcfg/load.go index 98b25da02..577115be5 100644 --- a/alpha/declcfg/load.go +++ b/alpha/declcfg/load.go @@ -22,6 +22,52 @@ const ( indexIgnoreFilename = ".indexignore" ) +type WalkMetasFSFunc func(path string, meta *Meta, err error) error + +func WalkMetasFS(root fs.FS, walkFn WalkMetasFSFunc) error { + return walkFiles(root, func(root fs.FS, path string, err error) error { + if err != nil { + return walkFn(path, nil, err) + } + + f, err := root.Open(path) + if err != nil { + return walkFn(path, nil, err) + } + defer f.Close() + + return WalkMetasReader(f, func(meta *Meta, err error) error { + return walkFn(path, meta, err) + }) + }) +} + +type WalkMetasReaderFunc func(meta *Meta, err error) error + +func WalkMetasReader(r io.Reader, walkFn WalkMetasReaderFunc) error { + dec := yaml.NewYAMLOrJSONDecoder(r, 4096) + for { + doc := json.RawMessage{} + if err := dec.Decode(&doc); err != nil { + if errors.Is(err, io.EOF) { + break + } + return walkFn(nil, err) + } + doc = []byte(strings.NewReplacer(`\u003c`, "<", `\u003e`, ">", `\u0026`, "&").Replace(string(doc))) + + var in Meta + if err := json.Unmarshal(doc, &in); err != nil { + return walkFn(nil, fmt.Errorf("unmarshal error: %s", resolveUnmarshalErr(doc, err))) + } + + if err := walkFn(&in, nil); err != nil { + return err + } + } + return nil +} + type WalkFunc func(path string, cfg *DeclarativeConfig, err error) error // WalkFS walks root using a gitignore-style filename matcher to skip files @@ -29,6 +75,21 @@ type WalkFunc func(path string, cfg *DeclarativeConfig, err error) error // It calls walkFn for each declarative config file it finds. If WalkFS encounters // an error loading or parsing any file, the error will be immediately returned. func WalkFS(root fs.FS, walkFn WalkFunc) error { + return walkFiles(root, func(root fs.FS, path string, err error) error { + if err != nil { + return walkFn(path, nil, err) + } + + cfg, err := LoadFile(root, path) + if err != nil { + return walkFn(path, cfg, err) + } + + return walkFn(path, cfg, nil) + }) +} + +func walkFiles(root fs.FS, fn func(root fs.FS, path string, err error) error) error { if root == nil { return fmt.Errorf("no declarative config filesystem provided") } @@ -40,7 +101,7 @@ func WalkFS(root fs.FS, walkFn WalkFunc) error { return fs.WalkDir(root, ".", func(path string, info fs.DirEntry, err error) error { if err != nil { - return walkFn(path, nil, err) + return fn(root, path, err) } // avoid validating a directory, an .indexignore file, or any file that matches // an ignore pattern outlined in a .indexignore file. @@ -48,12 +109,7 @@ func WalkFS(root fs.FS, walkFn WalkFunc) error { return nil } - cfg, err := LoadFile(root, path) - if err != nil { - return walkFn(path, cfg, err) - } - - return walkFn(path, cfg, err) + return fn(root, path, nil) }) } @@ -123,46 +179,38 @@ func extractCSV(objs []string) string { // Path references will not be de-referenced so callers are responsible for de-referencing if necessary. func LoadReader(r io.Reader) (*DeclarativeConfig, error) { cfg := &DeclarativeConfig{} - dec := yaml.NewYAMLOrJSONDecoder(r, 4096) - for { - doc := json.RawMessage{} - if err := dec.Decode(&doc); err != nil { - if errors.Is(err, io.EOF) { - break - } - return nil, err - } - doc = []byte(strings.NewReplacer(`\u003c`, "<", `\u003e`, ">", `\u0026`, "&").Replace(string(doc))) - var in Meta - if err := json.Unmarshal(doc, &in); err != nil { - return nil, fmt.Errorf("unmarshal error: %s", resolveUnmarshalErr(doc, err)) + if err := WalkMetasReader(r, func(in *Meta, err error) error { + if err != nil { + return err } - switch in.Schema { case SchemaPackage: var p Package - if err := json.Unmarshal(doc, &p); err != nil { - return nil, fmt.Errorf("parse package: %v", err) + if err := json.Unmarshal(in.Blob, &p); err != nil { + return fmt.Errorf("parse package: %v", err) } cfg.Packages = append(cfg.Packages, p) case SchemaChannel: var c Channel - if err := json.Unmarshal(doc, &c); err != nil { - return nil, fmt.Errorf("parse channel: %v", err) + if err := json.Unmarshal(in.Blob, &c); err != nil { + return fmt.Errorf("parse channel: %v", err) } cfg.Channels = append(cfg.Channels, c) case SchemaBundle: var b Bundle - if err := json.Unmarshal(doc, &b); err != nil { - return nil, fmt.Errorf("parse bundle: %v", err) + if err := json.Unmarshal(in.Blob, &b); err != nil { + return fmt.Errorf("parse bundle: %v", err) } cfg.Bundles = append(cfg.Bundles, b) case "": - return nil, fmt.Errorf("object '%s' is missing root schema field", string(doc)) + return fmt.Errorf("object '%s' is missing root schema field", string(in.Blob)) default: - cfg.Others = append(cfg.Others, in) + cfg.Others = append(cfg.Others, *in) } + return nil + }); err != nil { + return nil, err } return cfg, nil } @@ -213,10 +261,10 @@ func formatUnmarshallErrorString(data []byte, errmsg string, offset int64) strin var pOffset, origOffset int64 origOffset = 0 for origOffset = 0; origOffset < offset; { - pOffset++ if pString[pOffset] != '\n' && pString[pOffset] != ' ' { origOffset++ } + pOffset++ } _, _ = sb.WriteString(pString[:pOffset]) _, _ = sb.WriteString(" <== ") diff --git a/alpha/declcfg/load_test.go b/alpha/declcfg/load_test.go index 301002f16..3dc0772f9 100644 --- a/alpha/declcfg/load_test.go +++ b/alpha/declcfg/load_test.go @@ -92,6 +92,73 @@ func TestLoadReader(t *testing.T) { } } +func TestWalkMetasFS(t *testing.T) { + type spec struct { + name string + fsys fs.FS + assertion require.ErrorAssertionFunc + expectNumPackages int + expectNumChannels int + expectNumBundles int + expectNumOthers int + } + specs := []spec{ + { + name: "Error/NilFS", + fsys: nil, + assertion: require.Error, + }, + { + name: "Error/NonExistentDir", + fsys: os.DirFS("non/existent/dir/"), + assertion: require.Error, + }, + { + name: "Error/Invalid", + fsys: invalidFS, + assertion: require.Error, + }, + { + name: "Success/ValidDir", + fsys: validFS, + assertion: require.NoError, + expectNumPackages: 3, + expectNumChannels: 0, + expectNumBundles: 12, + expectNumOthers: 1, + }, + } + + for _, s := range specs { + t.Run(s.name, func(t *testing.T) { + numPackages, numChannels, numBundles, numOthers := 0, 0, 0, 0 + err := WalkMetasFS(s.fsys, func(path string, meta *Meta, err error) error { + if err != nil { + return err + } + switch meta.Schema { + case SchemaPackage: + numPackages++ + case SchemaChannel: + numChannels++ + case SchemaBundle: + numBundles++ + default: + numOthers++ + } + return nil + }) + s.assertion(t, err) + if err == nil { + assert.Equal(t, s.expectNumPackages, numPackages, "unexpected package count") + assert.Equal(t, s.expectNumChannels, numChannels, "unexpected channel count") + assert.Equal(t, s.expectNumBundles, numBundles, "unexpected bundle count") + assert.Equal(t, s.expectNumOthers, numOthers, "unexpected others count") + } + }) + } +} + func TestLoadFS(t *testing.T) { type spec struct { name string From 7e4f23abdd99b74f58508b40ddad7cb776bf3f13 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Mon, 24 Apr 2023 22:44:32 -0400 Subject: [PATCH 2/2] address PR feedback Signed-off-by: Joe Lanford --- alpha/declcfg/declcfg.go | 57 +++++++++++++++++++++++++++++++++++++++- alpha/declcfg/load.go | 56 ++------------------------------------- go.mod | 1 + go.sum | 10 +++++++ 4 files changed, 69 insertions(+), 55 deletions(-) diff --git a/alpha/declcfg/declcfg.go b/alpha/declcfg/declcfg.go index 8e2b761c0..80579c3a2 100644 --- a/alpha/declcfg/declcfg.go +++ b/alpha/declcfg/declcfg.go @@ -1,7 +1,13 @@ package declcfg import ( + "bytes" "encoding/json" + "errors" + "fmt" + "strings" + + "go4.org/bytereplacer" "github.com/operator-framework/operator-registry/alpha/property" ) @@ -94,6 +100,8 @@ func (m Meta) MarshalJSON() ([]byte, error) { } func (m *Meta) UnmarshalJSON(blob []byte) error { + blob = bytereplacer.New(`\u003c`, "<", `\u003e`, ">", `\u0026`, "&").Replace(blob) + type tmp struct { Schema string `json:"schema"` Package string `json:"package,omitempty"` @@ -102,7 +110,10 @@ func (m *Meta) UnmarshalJSON(blob []byte) error { } var t tmp if err := json.Unmarshal(blob, &t); err != nil { - return err + // TODO: return an error that includes the the full JSON message, + // the offset of the error, and the error message. Let callers + // decide how to format it. + return errors.New(resolveUnmarshalErr(blob, err)) } m.Schema = t.Schema m.Package = t.Package @@ -110,3 +121,47 @@ func (m *Meta) UnmarshalJSON(blob []byte) error { m.Blob = blob return nil } + +func resolveUnmarshalErr(data []byte, err error) string { + var te *json.UnmarshalTypeError + if errors.As(err, &te) { + return formatUnmarshallErrorString(data, te.Error(), te.Offset) + } + var se *json.SyntaxError + if errors.As(err, &se) { + return formatUnmarshallErrorString(data, se.Error(), se.Offset) + } + return err.Error() +} + +func formatUnmarshallErrorString(data []byte, errmsg string, offset int64) string { + sb := new(strings.Builder) + _, _ = sb.WriteString(fmt.Sprintf("%s at offset %d (indicated by <==)\n ", errmsg, offset)) + // attempt to present the erroneous JSON in indented, human-readable format + // errors result in presenting the original, unformatted output + var pretty bytes.Buffer + err := json.Indent(&pretty, data, "", " ") + if err == nil { + pString := pretty.String() + // calc the prettified string offset which correlates to the original string offset + var pOffset, origOffset int64 + origOffset = 0 + for origOffset = 0; origOffset < offset; { + if pString[pOffset] != '\n' && pString[pOffset] != ' ' { + origOffset++ + } + pOffset++ + } + _, _ = sb.WriteString(pString[:pOffset]) + _, _ = sb.WriteString(" <== ") + _, _ = sb.WriteString(pString[pOffset:]) + } else { + for i := int64(0); i < offset; i++ { + _ = sb.WriteByte(data[i]) + } + _, _ = sb.WriteString(" <== ") + _, _ = sb.Write(data[offset:]) + } + + return sb.String() +} diff --git a/alpha/declcfg/load.go b/alpha/declcfg/load.go index 577115be5..1f723bcc5 100644 --- a/alpha/declcfg/load.go +++ b/alpha/declcfg/load.go @@ -1,14 +1,12 @@ package declcfg import ( - "bytes" "encoding/json" "errors" "fmt" "io" "io/fs" "path/filepath" - "strings" "github.com/joelanford/ignore" "github.com/operator-framework/api/pkg/operators" @@ -47,19 +45,13 @@ type WalkMetasReaderFunc func(meta *Meta, err error) error func WalkMetasReader(r io.Reader, walkFn WalkMetasReaderFunc) error { dec := yaml.NewYAMLOrJSONDecoder(r, 4096) for { - doc := json.RawMessage{} - if err := dec.Decode(&doc); err != nil { + var in Meta + if err := dec.Decode(&in); err != nil { if errors.Is(err, io.EOF) { break } return walkFn(nil, err) } - doc = []byte(strings.NewReplacer(`\u003c`, "<", `\u003e`, ">", `\u0026`, "&").Replace(string(doc))) - - var in Meta - if err := json.Unmarshal(doc, &in); err != nil { - return walkFn(nil, fmt.Errorf("unmarshal error: %s", resolveUnmarshalErr(doc, err))) - } if err := walkFn(&in, nil); err != nil { return err @@ -235,47 +227,3 @@ func LoadFile(root fs.FS, path string) (*DeclarativeConfig, error) { return cfg, nil } - -func resolveUnmarshalErr(data []byte, err error) string { - var te *json.UnmarshalTypeError - if errors.As(err, &te) { - return formatUnmarshallErrorString(data, te.Error(), te.Offset) - } - var se *json.SyntaxError - if errors.As(err, &se) { - return formatUnmarshallErrorString(data, se.Error(), se.Offset) - } - return err.Error() -} - -func formatUnmarshallErrorString(data []byte, errmsg string, offset int64) string { - sb := new(strings.Builder) - _, _ = sb.WriteString(fmt.Sprintf("%s at offset %d (indicated by <==)\n ", errmsg, offset)) - // attempt to present the erroneous JSON in indented, human-readable format - // errors result in presenting the original, unformatted output - var pretty bytes.Buffer - err := json.Indent(&pretty, data, "", " ") - if err == nil { - pString := pretty.String() - // calc the prettified string offset which correlates to the original string offset - var pOffset, origOffset int64 - origOffset = 0 - for origOffset = 0; origOffset < offset; { - if pString[pOffset] != '\n' && pString[pOffset] != ' ' { - origOffset++ - } - pOffset++ - } - _, _ = sb.WriteString(pString[:pOffset]) - _, _ = sb.WriteString(" <== ") - _, _ = sb.WriteString(pString[pOffset:]) - } else { - for i := int64(0); i < offset; i++ { - _ = sb.WriteByte(data[i]) - } - _, _ = sb.WriteString(" <== ") - _, _ = sb.Write(data[offset:]) - } - - return sb.String() -} diff --git a/go.mod b/go.mod index 8fcb8f303..8772a2d1a 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/spf13/cobra v1.6.0 github.com/stretchr/testify v1.8.0 go.etcd.io/bbolt v1.3.6 + go4.org v0.0.0-20230225012048-214862532bf5 golang.org/x/mod v0.6.0 golang.org/x/net v0.7.0 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 diff --git a/go.sum b/go.sum index 8f1398c83..77209b6ab 100644 --- a/go.sum +++ b/go.sum @@ -654,6 +654,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/spec v1.2.0 h1:1Jwdf9jSfDl9NVmt8ndHqbTZ7XCCPbh1jI3hkDBHVYA= @@ -721,6 +722,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.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/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U= github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= @@ -773,6 +775,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -785,6 +789,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -818,6 +823,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -866,6 +872,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -957,6 +964,8 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1031,6 +1040,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=