From e0765ad68664ac56f60c326563f764970b184ffd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:25:08 +0000 Subject: [PATCH] Bump github.com/conductorone/baton-sdk from 0.1.38 to 0.2.7 Bumps [github.com/conductorone/baton-sdk](https://github.com/conductorone/baton-sdk) from 0.1.38 to 0.2.7. - [Release notes](https://github.com/conductorone/baton-sdk/releases) - [Commits](https://github.com/conductorone/baton-sdk/compare/v0.1.38...v0.2.7) --- updated-dependencies: - dependency-name: github.com/conductorone/baton-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 13 +- go.sum | 51 +- .../connector/v2/annotation_entitlement.pb.go | 164 ++++++ .../v2/annotation_entitlement.pb.validate.go | 169 ++++++ .../v2/annotation_external_ticket.pb.go | 148 ++++++ .../annotation_external_ticket.pb.validate.go | 140 +++++ .../pb/c1/connector/v2/annotation_grant.pb.go | 102 +++- .../v2/annotation_grant.pb.validate.go | 131 +++++ .../baton-sdk/pb/c1/connector/v2/ticket.pb.go | 88 ++-- .../pb/c1/connector/v2/ticket.pb.validate.go | 58 ++ .../baton-sdk/pkg/annotations/annotations.go | 4 + .../conductorone/baton-sdk/pkg/cli/cli.go | 495 ------------------ .../baton-sdk/pkg/cli/commands.go | 323 ++++++++++++ .../conductorone/baton-sdk/pkg/cli/config.go | 146 ------ .../baton-sdk/pkg/cli/service_unix.go | 17 +- .../baton-sdk/pkg/cli/service_windows.go | 73 ++- .../pkg/connectorbuilder/connectorbuilder.go | 159 +++++- .../pkg/connectorstore/connectorstore.go | 9 +- .../baton-sdk/pkg/dotc1z/assets.go | 5 - .../baton-sdk/pkg/dotc1z/c1file.go | 11 + .../baton-sdk/pkg/dotc1z/entitlements.go | 40 +- .../baton-sdk/pkg/dotc1z/grants.go | 56 +- .../baton-sdk/pkg/dotc1z/resouce_types.go | 35 +- .../baton-sdk/pkg/dotc1z/resources.go | 66 +-- .../baton-sdk/pkg/dotc1z/sql_helpers.go | 69 ++- .../pkg/field/default_relationships.go | 33 ++ .../baton-sdk/pkg/field/defaults.go | 92 ++++ .../baton-sdk/pkg/field/field_options.go | 42 ++ .../baton-sdk/pkg/field/fields.go | 149 ++++++ .../baton-sdk/pkg/field/relationships.go | 35 ++ .../baton-sdk/pkg/field/struct.go | 13 + .../baton-sdk/pkg/field/validation.go | 124 +++++ .../baton-sdk/pkg/helpers/helpers.go | 107 +++- .../baton-sdk/pkg/logging/logging.go | 1 + .../baton-sdk/pkg/metrics/instrumentor.go | 39 ++ .../baton-sdk/pkg/metrics/metrics.go | 32 ++ .../baton-sdk/pkg/metrics/noop.go | 39 ++ .../baton-sdk/pkg/metrics/otel.go | 183 +++++++ .../conductorone/baton-sdk/pkg/sync/expand.go | 401 -------------- .../baton-sdk/pkg/sync/expand/cycle.go | 213 ++++++++ .../baton-sdk/pkg/sync/expand/graph.go | 283 ++++++++++ .../baton-sdk/pkg/sync/expand/validate.go | 134 +++++ .../conductorone/baton-sdk/pkg/sync/state.go | 24 +- .../conductorone/baton-sdk/pkg/sync/syncer.go | 101 ++-- .../pkg/tasks/c1api/create_ticket.go | 13 +- .../baton-sdk/pkg/tasks/c1api/get_ticket.go | 21 +- .../pkg/tasks/c1api/list_ticket_schemas.go | 2 +- .../baton-sdk/pkg/tasks/c1api/manager.go | 25 +- .../baton-sdk/pkg/tasks/local/ticket.go | 43 +- .../conductorone/baton-sdk/pkg/tasks/tasks.go | 113 ++-- .../baton-sdk/pkg/types/tasks/tasks.go | 74 +++ .../pkg/types/ticket/custom_fields.go | 107 ++-- .../baton-sdk/pkg/uhttp/authcredentials.go | 3 +- .../baton-sdk/pkg/uhttp/wrapper.go | 9 + .../deckarep/golang-set/v2/.gitignore | 23 + .../github.com/deckarep/golang-set/v2/LICENSE | 22 + .../deckarep/golang-set/v2/README.md | 181 +++++++ .../deckarep/golang-set/v2/iterator.go | 58 ++ .../deckarep/golang-set/v2/new_improved.jpeg | Bin 0 -> 120935 bytes .../github.com/deckarep/golang-set/v2/set.go | 255 +++++++++ .../deckarep/golang-set/v2/sorted.go | 42 ++ .../deckarep/golang-set/v2/threadsafe.go | 299 +++++++++++ .../deckarep/golang-set/v2/threadunsafe.go | 330 ++++++++++++ .../goqu/v9/dialect/sqlite3/sqlite3.go | 76 +++ vendor/go.opentelemetry.io/otel/LICENSE | 201 +++++++ .../otel/attribute/README.md | 3 + .../go.opentelemetry.io/otel/attribute/doc.go | 5 + .../otel/attribute/encoder.go | 135 +++++ .../otel/attribute/filter.go | 49 ++ .../otel/attribute/iterator.go | 150 ++++++ .../go.opentelemetry.io/otel/attribute/key.go | 123 +++++ .../go.opentelemetry.io/otel/attribute/kv.go | 75 +++ .../go.opentelemetry.io/otel/attribute/set.go | 431 +++++++++++++++ .../otel/attribute/type_string.go | 31 ++ .../otel/attribute/value.go | 271 ++++++++++ .../otel/internal/attribute/attribute.go | 100 ++++ .../go.opentelemetry.io/otel/internal/gen.go | 18 + .../otel/internal/rawhelpers.go | 44 ++ .../go.opentelemetry.io/otel/metric/LICENSE | 201 +++++++ .../go.opentelemetry.io/otel/metric/README.md | 3 + .../otel/metric/asyncfloat64.go | 260 +++++++++ .../otel/metric/asyncint64.go | 258 +++++++++ .../go.opentelemetry.io/otel/metric/config.go | 81 +++ vendor/go.opentelemetry.io/otel/metric/doc.go | 159 ++++++ .../otel/metric/embedded/README.md | 3 + .../otel/metric/embedded/embedded.go | 243 +++++++++ .../otel/metric/instrument.go | 368 +++++++++++++ .../go.opentelemetry.io/otel/metric/meter.go | 209 ++++++++ .../otel/metric/syncfloat64.go | 226 ++++++++ .../otel/metric/syncint64.go | 226 ++++++++ vendor/golang.org/x/crypto/ssh/keys.go | 8 + vendor/golang.org/x/crypto/ssh/server.go | 30 ++ vendor/golang.org/x/net/http2/http2.go | 19 +- vendor/golang.org/x/net/http2/server.go | 94 +++- vendor/golang.org/x/net/http2/testsync.go | 331 ------------ vendor/golang.org/x/net/http2/timer.go | 20 + vendor/golang.org/x/net/http2/transport.go | 310 ++++------- .../x/net/http2/writesched_priority.go | 4 +- vendor/golang.org/x/sys/unix/mkerrors.sh | 2 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 20 +- .../x/sys/unix/zerrors_linux_386.go | 1 + .../x/sys/unix/zerrors_linux_amd64.go | 1 + .../x/sys/unix/zerrors_linux_arm64.go | 1 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 37 +- .../x/sys/windows/security_windows.go | 1 + .../x/sys/windows/zsyscall_windows.go | 9 + vendor/modules.txt | 27 +- 107 files changed, 8978 insertions(+), 2123 deletions(-) create mode 100644 vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.validate.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.validate.go delete mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/cli/cli.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go delete mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/cli/config.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/field_options.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/fields.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/relationships.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/struct.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/field/validation.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/metrics/instrumentor.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/metrics/metrics.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/metrics/noop.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/metrics/otel.go delete mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/sync/expand.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/cycle.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/validate.go create mode 100644 vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/.gitignore create mode 100644 vendor/github.com/deckarep/golang-set/v2/LICENSE create mode 100644 vendor/github.com/deckarep/golang-set/v2/README.md create mode 100644 vendor/github.com/deckarep/golang-set/v2/iterator.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg create mode 100644 vendor/github.com/deckarep/golang-set/v2/set.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/sorted.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadsafe.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadunsafe.go create mode 100644 vendor/github.com/doug-martin/goqu/v9/dialect/sqlite3/sqlite3.go create mode 100644 vendor/go.opentelemetry.io/otel/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/attribute/README.md create mode 100644 vendor/go.opentelemetry.io/otel/attribute/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/encoder.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/filter.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/iterator.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/key.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/kv.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/set.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/type_string.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/value.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/attribute/attribute.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/gen.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/rawhelpers.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/metric/README.md create mode 100644 vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/asyncint64.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/config.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/embedded/README.md create mode 100644 vendor/go.opentelemetry.io/otel/metric/embedded/embedded.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/instrument.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/meter.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/syncfloat64.go create mode 100644 vendor/go.opentelemetry.io/otel/metric/syncint64.go delete mode 100644 vendor/golang.org/x/net/http2/testsync.go create mode 100644 vendor/golang.org/x/net/http2/timer.go diff --git a/go.mod b/go.mod index c5ecfa4..4de9c23 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.2 require ( buf.build/gen/go/formal/core/protocolbuffers/go v1.33.0-20240417020633-67c08acefeb5.1 connectrpc.com/connect v1.16.1 - github.com/conductorone/baton-sdk v0.1.38 + github.com/conductorone/baton-sdk v0.2.7 github.com/formalco/go-sdk/sdk/v2 v2.0.4 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/spf13/cobra v1.8.0 @@ -40,6 +40,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect github.com/aws/smithy-go v1.20.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/doug-martin/goqu/v9 v9.19.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect @@ -76,15 +77,17 @@ require ( github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/otel v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240506185236-b8a5c65736ae // indirect google.golang.org/grpc v1.63.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index a99d79a..9b80430 100644 --- a/go.sum +++ b/go.sum @@ -59,13 +59,15 @@ github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/conductorone/baton-sdk v0.1.38 h1:KPFguiXeQZEtgA+g0CLzHduGe/1vrEkpVsM4XJqsqbE= -github.com/conductorone/baton-sdk v0.1.38/go.mod h1:yzfAXTvKzGJfhfrNTQ/Ipzt9+KPCzHrNO/Ro1gsxyDk= +github.com/conductorone/baton-sdk v0.2.7 h1:mzp7H0zVeZLMvdGj3Yx31mpzM6ytNKI6QmpzRuiPlVE= +github.com/conductorone/baton-sdk v0.2.7/go.mod h1:cg5FyUcJnD7xK5SPbHe/KNpwUVVlpHJ9rnmd3UwxSkU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/doug-martin/goqu/v9 v9.19.0 h1:PD7t1X3tRcUiSdc5TEyOFKujZA5gs3VSA7wxSvBx7qo= github.com/doug-martin/goqu/v9 v9.19.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= @@ -89,6 +91,10 @@ github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7 github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -146,6 +152,7 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -222,6 +229,18 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -243,8 +262,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= @@ -256,8 +275,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -270,8 +289,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -303,23 +322,23 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -331,8 +350,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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= diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.go new file mode 100644 index 0000000..7e53681 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.go @@ -0,0 +1,164 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: c1/connector/v2/annotation_entitlement.proto + +package v2 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EntitlementImmutable struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceId string `protobuf:"bytes,1,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"` + Metadata *structpb.Struct `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *EntitlementImmutable) Reset() { + *x = EntitlementImmutable{} + if protoimpl.UnsafeEnabled { + mi := &file_c1_connector_v2_annotation_entitlement_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EntitlementImmutable) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EntitlementImmutable) ProtoMessage() {} + +func (x *EntitlementImmutable) ProtoReflect() protoreflect.Message { + mi := &file_c1_connector_v2_annotation_entitlement_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EntitlementImmutable.ProtoReflect.Descriptor instead. +func (*EntitlementImmutable) Descriptor() ([]byte, []int) { + return file_c1_connector_v2_annotation_entitlement_proto_rawDescGZIP(), []int{0} +} + +func (x *EntitlementImmutable) GetSourceId() string { + if x != nil { + return x.SourceId + } + return "" +} + +func (x *EntitlementImmutable) GetMetadata() *structpb.Struct { + if x != nil { + return x.Metadata + } + return nil +} + +var File_c1_connector_v2_annotation_entitlement_proto protoreflect.FileDescriptor + +var file_c1_connector_v2_annotation_entitlement_proto_rawDesc = []byte{ + 0x0a, 0x2c, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, + 0x32, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, + 0x63, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x68, 0x0a, + 0x14, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6d, 0x6d, 0x75, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, + 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x2f, + 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_c1_connector_v2_annotation_entitlement_proto_rawDescOnce sync.Once + file_c1_connector_v2_annotation_entitlement_proto_rawDescData = file_c1_connector_v2_annotation_entitlement_proto_rawDesc +) + +func file_c1_connector_v2_annotation_entitlement_proto_rawDescGZIP() []byte { + file_c1_connector_v2_annotation_entitlement_proto_rawDescOnce.Do(func() { + file_c1_connector_v2_annotation_entitlement_proto_rawDescData = protoimpl.X.CompressGZIP(file_c1_connector_v2_annotation_entitlement_proto_rawDescData) + }) + return file_c1_connector_v2_annotation_entitlement_proto_rawDescData +} + +var file_c1_connector_v2_annotation_entitlement_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_c1_connector_v2_annotation_entitlement_proto_goTypes = []interface{}{ + (*EntitlementImmutable)(nil), // 0: c1.connector.v2.EntitlementImmutable + (*structpb.Struct)(nil), // 1: google.protobuf.Struct +} +var file_c1_connector_v2_annotation_entitlement_proto_depIdxs = []int32{ + 1, // 0: c1.connector.v2.EntitlementImmutable.metadata:type_name -> google.protobuf.Struct + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_c1_connector_v2_annotation_entitlement_proto_init() } +func file_c1_connector_v2_annotation_entitlement_proto_init() { + if File_c1_connector_v2_annotation_entitlement_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_c1_connector_v2_annotation_entitlement_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EntitlementImmutable); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_c1_connector_v2_annotation_entitlement_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_c1_connector_v2_annotation_entitlement_proto_goTypes, + DependencyIndexes: file_c1_connector_v2_annotation_entitlement_proto_depIdxs, + MessageInfos: file_c1_connector_v2_annotation_entitlement_proto_msgTypes, + }.Build() + File_c1_connector_v2_annotation_entitlement_proto = out.File + file_c1_connector_v2_annotation_entitlement_proto_rawDesc = nil + file_c1_connector_v2_annotation_entitlement_proto_goTypes = nil + file_c1_connector_v2_annotation_entitlement_proto_depIdxs = nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.validate.go new file mode 100644 index 0000000..189bf53 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_entitlement.pb.validate.go @@ -0,0 +1,169 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: c1/connector/v2/annotation_entitlement.proto + +package v2 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) + +// Validate checks the field values on EntitlementImmutable with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *EntitlementImmutable) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on EntitlementImmutable with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// EntitlementImmutableMultiError, or nil if none found. +func (m *EntitlementImmutable) ValidateAll() error { + return m.validate(true) +} + +func (m *EntitlementImmutable) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for SourceId + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, EntitlementImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, EntitlementImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return EntitlementImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return EntitlementImmutableMultiError(errors) + } + + return nil +} + +// EntitlementImmutableMultiError is an error wrapping multiple validation +// errors returned by EntitlementImmutable.ValidateAll() if the designated +// constraints aren't met. +type EntitlementImmutableMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m EntitlementImmutableMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m EntitlementImmutableMultiError) AllErrors() []error { return m } + +// EntitlementImmutableValidationError is the validation error returned by +// EntitlementImmutable.Validate if the designated constraints aren't met. +type EntitlementImmutableValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e EntitlementImmutableValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e EntitlementImmutableValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e EntitlementImmutableValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e EntitlementImmutableValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e EntitlementImmutableValidationError) ErrorName() string { + return "EntitlementImmutableValidationError" +} + +// Error satisfies the builtin error interface +func (e EntitlementImmutableValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sEntitlementImmutable.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = EntitlementImmutableValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = EntitlementImmutableValidationError{} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.go new file mode 100644 index 0000000..445884a --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.go @@ -0,0 +1,148 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: c1/connector/v2/annotation_external_ticket.proto + +package v2 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ExternalTicketSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *ExternalTicketSettings) Reset() { + *x = ExternalTicketSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_c1_connector_v2_annotation_external_ticket_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExternalTicketSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExternalTicketSettings) ProtoMessage() {} + +func (x *ExternalTicketSettings) ProtoReflect() protoreflect.Message { + mi := &file_c1_connector_v2_annotation_external_ticket_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExternalTicketSettings.ProtoReflect.Descriptor instead. +func (*ExternalTicketSettings) Descriptor() ([]byte, []int) { + return file_c1_connector_v2_annotation_external_ticket_proto_rawDescGZIP(), []int{0} +} + +func (x *ExternalTicketSettings) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +var File_c1_connector_v2_annotation_external_ticket_proto protoreflect.FileDescriptor + +var file_c1_connector_v2_annotation_external_ticket_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, + 0x32, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x32, 0x22, 0x32, 0x0a, 0x16, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, + 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x2f, + 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_c1_connector_v2_annotation_external_ticket_proto_rawDescOnce sync.Once + file_c1_connector_v2_annotation_external_ticket_proto_rawDescData = file_c1_connector_v2_annotation_external_ticket_proto_rawDesc +) + +func file_c1_connector_v2_annotation_external_ticket_proto_rawDescGZIP() []byte { + file_c1_connector_v2_annotation_external_ticket_proto_rawDescOnce.Do(func() { + file_c1_connector_v2_annotation_external_ticket_proto_rawDescData = protoimpl.X.CompressGZIP(file_c1_connector_v2_annotation_external_ticket_proto_rawDescData) + }) + return file_c1_connector_v2_annotation_external_ticket_proto_rawDescData +} + +var file_c1_connector_v2_annotation_external_ticket_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_c1_connector_v2_annotation_external_ticket_proto_goTypes = []interface{}{ + (*ExternalTicketSettings)(nil), // 0: c1.connector.v2.ExternalTicketSettings +} +var file_c1_connector_v2_annotation_external_ticket_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_c1_connector_v2_annotation_external_ticket_proto_init() } +func file_c1_connector_v2_annotation_external_ticket_proto_init() { + if File_c1_connector_v2_annotation_external_ticket_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_c1_connector_v2_annotation_external_ticket_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExternalTicketSettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_c1_connector_v2_annotation_external_ticket_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_c1_connector_v2_annotation_external_ticket_proto_goTypes, + DependencyIndexes: file_c1_connector_v2_annotation_external_ticket_proto_depIdxs, + MessageInfos: file_c1_connector_v2_annotation_external_ticket_proto_msgTypes, + }.Build() + File_c1_connector_v2_annotation_external_ticket_proto = out.File + file_c1_connector_v2_annotation_external_ticket_proto_rawDesc = nil + file_c1_connector_v2_annotation_external_ticket_proto_goTypes = nil + file_c1_connector_v2_annotation_external_ticket_proto_depIdxs = nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.validate.go new file mode 100644 index 0000000..abe6a36 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_external_ticket.pb.validate.go @@ -0,0 +1,140 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: c1/connector/v2/annotation_external_ticket.proto + +package v2 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) + +// Validate checks the field values on ExternalTicketSettings with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *ExternalTicketSettings) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on ExternalTicketSettings with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// ExternalTicketSettingsMultiError, or nil if none found. +func (m *ExternalTicketSettings) ValidateAll() error { + return m.validate(true) +} + +func (m *ExternalTicketSettings) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Enabled + + if len(errors) > 0 { + return ExternalTicketSettingsMultiError(errors) + } + + return nil +} + +// ExternalTicketSettingsMultiError is an error wrapping multiple validation +// errors returned by ExternalTicketSettings.ValidateAll() if the designated +// constraints aren't met. +type ExternalTicketSettingsMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m ExternalTicketSettingsMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m ExternalTicketSettingsMultiError) AllErrors() []error { return m } + +// ExternalTicketSettingsValidationError is the validation error returned by +// ExternalTicketSettings.Validate if the designated constraints aren't met. +type ExternalTicketSettingsValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ExternalTicketSettingsValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ExternalTicketSettingsValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ExternalTicketSettingsValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ExternalTicketSettingsValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ExternalTicketSettingsValidationError) ErrorName() string { + return "ExternalTicketSettingsValidationError" +} + +// Error satisfies the builtin error interface +func (e ExternalTicketSettingsValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sExternalTicketSettings.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ExternalTicketSettingsValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ExternalTicketSettingsValidationError{} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.go index 7957869..725f84a 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.go @@ -131,6 +131,61 @@ func (x *GrantExpandable) GetResourceTypeIds() []string { return nil } +type GrantImmutable struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceId string `protobuf:"bytes,1,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"` + Metadata *structpb.Struct `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *GrantImmutable) Reset() { + *x = GrantImmutable{} + if protoimpl.UnsafeEnabled { + mi := &file_c1_connector_v2_annotation_grant_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GrantImmutable) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GrantImmutable) ProtoMessage() {} + +func (x *GrantImmutable) ProtoReflect() protoreflect.Message { + mi := &file_c1_connector_v2_annotation_grant_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GrantImmutable.ProtoReflect.Descriptor instead. +func (*GrantImmutable) Descriptor() ([]byte, []int) { + return file_c1_connector_v2_annotation_grant_proto_rawDescGZIP(), []int{2} +} + +func (x *GrantImmutable) GetSourceId() string { + if x != nil { + return x.SourceId + } + return "" +} + +func (x *GrantImmutable) GetMetadata() *structpb.Struct { + if x != nil { + return x.Metadata + } + return nil +} + var File_c1_connector_v2_annotation_grant_proto protoreflect.FileDescriptor var file_c1_connector_v2_annotation_grant_proto_rawDesc = []byte{ @@ -152,10 +207,17 @@ var file_c1_connector_v2_annotation_grant_proto_rawDesc = []byte{ 0x6c, 0x6c, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x73, - 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, - 0x6f, 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x2f, 0x62, 0x61, 0x74, 0x6f, - 0x6e, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x62, 0x0a, 0x0e, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x49, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x2f, + 0x62, 0x61, 0x74, 0x6f, 0x6e, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -170,19 +232,21 @@ func file_c1_connector_v2_annotation_grant_proto_rawDescGZIP() []byte { return file_c1_connector_v2_annotation_grant_proto_rawDescData } -var file_c1_connector_v2_annotation_grant_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_c1_connector_v2_annotation_grant_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_c1_connector_v2_annotation_grant_proto_goTypes = []interface{}{ (*GrantMetadata)(nil), // 0: c1.connector.v2.GrantMetadata (*GrantExpandable)(nil), // 1: c1.connector.v2.GrantExpandable - (*structpb.Struct)(nil), // 2: google.protobuf.Struct + (*GrantImmutable)(nil), // 2: c1.connector.v2.GrantImmutable + (*structpb.Struct)(nil), // 3: google.protobuf.Struct } var file_c1_connector_v2_annotation_grant_proto_depIdxs = []int32{ - 2, // 0: c1.connector.v2.GrantMetadata.metadata:type_name -> google.protobuf.Struct - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 3, // 0: c1.connector.v2.GrantMetadata.metadata:type_name -> google.protobuf.Struct + 3, // 1: c1.connector.v2.GrantImmutable.metadata:type_name -> google.protobuf.Struct + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_c1_connector_v2_annotation_grant_proto_init() } @@ -215,6 +279,18 @@ func file_c1_connector_v2_annotation_grant_proto_init() { return nil } } + file_c1_connector_v2_annotation_grant_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GrantImmutable); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -222,7 +298,7 @@ func file_c1_connector_v2_annotation_grant_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_c1_connector_v2_annotation_grant_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.validate.go index 05d3ff2..98bfe0e 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.validate.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_grant.pb.validate.go @@ -265,3 +265,134 @@ var _ interface { Cause() error ErrorName() string } = GrantExpandableValidationError{} + +// Validate checks the field values on GrantImmutable with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *GrantImmutable) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GrantImmutable with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in GrantImmutableMultiError, +// or nil if none found. +func (m *GrantImmutable) ValidateAll() error { + return m.validate(true) +} + +func (m *GrantImmutable) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for SourceId + + if all { + switch v := interface{}(m.GetMetadata()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, GrantImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, GrantImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return GrantImmutableValidationError{ + field: "Metadata", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return GrantImmutableMultiError(errors) + } + + return nil +} + +// GrantImmutableMultiError is an error wrapping multiple validation errors +// returned by GrantImmutable.ValidateAll() if the designated constraints +// aren't met. +type GrantImmutableMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GrantImmutableMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GrantImmutableMultiError) AllErrors() []error { return m } + +// GrantImmutableValidationError is the validation error returned by +// GrantImmutable.Validate if the designated constraints aren't met. +type GrantImmutableValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GrantImmutableValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GrantImmutableValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GrantImmutableValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GrantImmutableValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GrantImmutableValidationError) ErrorName() string { return "GrantImmutableValidationError" } + +// Error satisfies the builtin error interface +func (e GrantImmutableValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGrantImmutable.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GrantImmutableValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GrantImmutableValidationError{} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.go index 8731ec7..0415504 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.go @@ -1063,6 +1063,7 @@ type Ticket struct { CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` CompletedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` + RequestedFor *Resource `protobuf:"bytes,15,opt,name=requested_for,json=requestedFor,proto3" json:"requested_for,omitempty"` } func (x *Ticket) Reset() { @@ -1188,6 +1189,13 @@ func (x *Ticket) GetCompletedAt() *timestamppb.Timestamp { return nil } +func (x *Ticket) GetRequestedFor() *Resource { + if x != nil { + return x.RequestedFor + } + return nil +} + type TicketType struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1254,6 +1262,7 @@ type TicketRequest struct { Type *TicketType `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` Labels []string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty"` CustomFields map[string]*TicketCustomField `protobuf:"bytes,6,rep,name=custom_fields,json=customFields,proto3" json:"custom_fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + RequestedFor *Resource `protobuf:"bytes,7,opt,name=requested_for,json=requestedFor,proto3" json:"requested_for,omitempty"` } func (x *TicketRequest) Reset() { @@ -1330,6 +1339,13 @@ func (x *TicketRequest) GetCustomFields() map[string]*TicketCustomField { return nil } +func (x *TicketRequest) GetRequestedFor() *Resource { + if x != nil { + return x.RequestedFor + } + return nil +} + type TicketsServiceCreateTicketRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1753,7 +1769,7 @@ var file_c1_connector_v2_ticket_proto_rawDesc = []byte{ 0x12, 0x36, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc9, 0x05, 0x0a, 0x06, 0x54, 0x69, 0x63, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x89, 0x06, 0x0a, 0x06, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, @@ -1791,7 +1807,11 @@ var file_c1_connector_v2_ticket_proto_rawDesc = []byte{ 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x1a, + 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x3e, 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x1a, 0x63, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, @@ -1802,7 +1822,7 @@ var file_c1_connector_v2_ticket_proto_rawDesc = []byte{ 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x90, 0x03, 0x0a, 0x0d, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd0, 0x03, 0x0a, 0x0d, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, @@ -1821,7 +1841,11 @@ var file_c1_connector_v2_ticket_proto_rawDesc = []byte{ 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x1a, 0x63, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x31, 0x2e, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x46, 0x6f, 0x72, 0x1a, 0x63, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x31, 0x2e, 0x63, @@ -1979,33 +2003,35 @@ var file_c1_connector_v2_ticket_proto_depIdxs = []int32{ 26, // 27: c1.connector.v2.Ticket.created_at:type_name -> google.protobuf.Timestamp 26, // 28: c1.connector.v2.Ticket.updated_at:type_name -> google.protobuf.Timestamp 26, // 29: c1.connector.v2.Ticket.completed_at:type_name -> google.protobuf.Timestamp - 11, // 30: c1.connector.v2.TicketRequest.status:type_name -> c1.connector.v2.TicketStatus - 17, // 31: c1.connector.v2.TicketRequest.type:type_name -> c1.connector.v2.TicketType - 25, // 32: c1.connector.v2.TicketRequest.custom_fields:type_name -> c1.connector.v2.TicketRequest.CustomFieldsEntry - 18, // 33: c1.connector.v2.TicketsServiceCreateTicketRequest.request:type_name -> c1.connector.v2.TicketRequest - 0, // 34: c1.connector.v2.TicketsServiceCreateTicketRequest.schema:type_name -> c1.connector.v2.TicketSchema - 27, // 35: c1.connector.v2.TicketsServiceCreateTicketRequest.annotations:type_name -> google.protobuf.Any - 16, // 36: c1.connector.v2.TicketsServiceCreateTicketResponse.ticket:type_name -> c1.connector.v2.Ticket - 27, // 37: c1.connector.v2.TicketsServiceCreateTicketResponse.annotations:type_name -> google.protobuf.Any - 27, // 38: c1.connector.v2.TicketsServiceGetTicketRequest.annotations:type_name -> google.protobuf.Any - 16, // 39: c1.connector.v2.TicketsServiceGetTicketResponse.ticket:type_name -> c1.connector.v2.Ticket - 27, // 40: c1.connector.v2.TicketsServiceGetTicketResponse.annotations:type_name -> google.protobuf.Any - 1, // 41: c1.connector.v2.TicketSchema.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField - 1, // 42: c1.connector.v2.Ticket.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField - 1, // 43: c1.connector.v2.TicketRequest.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField - 19, // 44: c1.connector.v2.TicketsService.CreateTicket:input_type -> c1.connector.v2.TicketsServiceCreateTicketRequest - 21, // 45: c1.connector.v2.TicketsService.GetTicket:input_type -> c1.connector.v2.TicketsServiceGetTicketRequest - 14, // 46: c1.connector.v2.TicketsService.ListTicketSchemas:input_type -> c1.connector.v2.TicketsServiceListTicketSchemasRequest - 12, // 47: c1.connector.v2.TicketsService.GetTicketSchema:input_type -> c1.connector.v2.TicketsServiceGetTicketSchemaRequest - 20, // 48: c1.connector.v2.TicketsService.CreateTicket:output_type -> c1.connector.v2.TicketsServiceCreateTicketResponse - 22, // 49: c1.connector.v2.TicketsService.GetTicket:output_type -> c1.connector.v2.TicketsServiceGetTicketResponse - 15, // 50: c1.connector.v2.TicketsService.ListTicketSchemas:output_type -> c1.connector.v2.TicketsServiceListTicketSchemasResponse - 13, // 51: c1.connector.v2.TicketsService.GetTicketSchema:output_type -> c1.connector.v2.TicketsServiceGetTicketSchemaResponse - 48, // [48:52] is the sub-list for method output_type - 44, // [44:48] is the sub-list for method input_type - 44, // [44:44] is the sub-list for extension type_name - 44, // [44:44] is the sub-list for extension extendee - 0, // [0:44] is the sub-list for field type_name + 28, // 30: c1.connector.v2.Ticket.requested_for:type_name -> c1.connector.v2.Resource + 11, // 31: c1.connector.v2.TicketRequest.status:type_name -> c1.connector.v2.TicketStatus + 17, // 32: c1.connector.v2.TicketRequest.type:type_name -> c1.connector.v2.TicketType + 25, // 33: c1.connector.v2.TicketRequest.custom_fields:type_name -> c1.connector.v2.TicketRequest.CustomFieldsEntry + 28, // 34: c1.connector.v2.TicketRequest.requested_for:type_name -> c1.connector.v2.Resource + 18, // 35: c1.connector.v2.TicketsServiceCreateTicketRequest.request:type_name -> c1.connector.v2.TicketRequest + 0, // 36: c1.connector.v2.TicketsServiceCreateTicketRequest.schema:type_name -> c1.connector.v2.TicketSchema + 27, // 37: c1.connector.v2.TicketsServiceCreateTicketRequest.annotations:type_name -> google.protobuf.Any + 16, // 38: c1.connector.v2.TicketsServiceCreateTicketResponse.ticket:type_name -> c1.connector.v2.Ticket + 27, // 39: c1.connector.v2.TicketsServiceCreateTicketResponse.annotations:type_name -> google.protobuf.Any + 27, // 40: c1.connector.v2.TicketsServiceGetTicketRequest.annotations:type_name -> google.protobuf.Any + 16, // 41: c1.connector.v2.TicketsServiceGetTicketResponse.ticket:type_name -> c1.connector.v2.Ticket + 27, // 42: c1.connector.v2.TicketsServiceGetTicketResponse.annotations:type_name -> google.protobuf.Any + 1, // 43: c1.connector.v2.TicketSchema.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField + 1, // 44: c1.connector.v2.Ticket.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField + 1, // 45: c1.connector.v2.TicketRequest.CustomFieldsEntry.value:type_name -> c1.connector.v2.TicketCustomField + 19, // 46: c1.connector.v2.TicketsService.CreateTicket:input_type -> c1.connector.v2.TicketsServiceCreateTicketRequest + 21, // 47: c1.connector.v2.TicketsService.GetTicket:input_type -> c1.connector.v2.TicketsServiceGetTicketRequest + 14, // 48: c1.connector.v2.TicketsService.ListTicketSchemas:input_type -> c1.connector.v2.TicketsServiceListTicketSchemasRequest + 12, // 49: c1.connector.v2.TicketsService.GetTicketSchema:input_type -> c1.connector.v2.TicketsServiceGetTicketSchemaRequest + 20, // 50: c1.connector.v2.TicketsService.CreateTicket:output_type -> c1.connector.v2.TicketsServiceCreateTicketResponse + 22, // 51: c1.connector.v2.TicketsService.GetTicket:output_type -> c1.connector.v2.TicketsServiceGetTicketResponse + 15, // 52: c1.connector.v2.TicketsService.ListTicketSchemas:output_type -> c1.connector.v2.TicketsServiceListTicketSchemasResponse + 13, // 53: c1.connector.v2.TicketsService.GetTicketSchema:output_type -> c1.connector.v2.TicketsServiceGetTicketSchemaResponse + 50, // [50:54] is the sub-list for method output_type + 46, // [46:50] is the sub-list for method input_type + 46, // [46:46] is the sub-list for extension type_name + 46, // [46:46] is the sub-list for extension extendee + 0, // [0:46] is the sub-list for field type_name } func init() { file_c1_connector_v2_ticket_proto_init() } diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.validate.go index 3d9a46d..4aeef01 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.validate.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/ticket.pb.validate.go @@ -2853,6 +2853,35 @@ func (m *Ticket) validate(all bool) error { } } + if all { + switch v := interface{}(m.GetRequestedFor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, TicketValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, TicketValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetRequestedFor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return TicketValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + } + } + } + if len(errors) > 0 { return TicketMultiError(errors) } @@ -3163,6 +3192,35 @@ func (m *TicketRequest) validate(all bool) error { } } + if all { + switch v := interface{}(m.GetRequestedFor()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, TicketRequestValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, TicketRequestValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetRequestedFor()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return TicketRequestValidationError{ + field: "RequestedFor", + reason: "embedded message failed validation", + cause: err, + } + } + } + if len(errors) > 0 { return TicketRequestMultiError(errors) } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/annotations/annotations.go b/vendor/github.com/conductorone/baton-sdk/pkg/annotations/annotations.go index 84db79b..19eda94 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/annotations/annotations.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/annotations/annotations.go @@ -65,6 +65,10 @@ func (a *Annotations) Update(msg proto.Message) { *a = newAnnotations } +func (a *Annotations) Merge(newAnnotations ...*anypb.Any) { + *a = append(*a, newAnnotations...) +} + // Contains checks if the message is in the annotations slice. func (a *Annotations) Contains(msg proto.Message) bool { if msg == nil { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/cli.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/cli.go deleted file mode 100644 index 2b1918d..0000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/cli/cli.go +++ /dev/null @@ -1,495 +0,0 @@ -package cli - -import ( - "bufio" - "context" - "encoding/base64" - "fmt" - "os" - - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "github.com/spf13/cobra" - "go.uber.org/zap" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/conductorone/baton-sdk/internal/connector" - v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" - v1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1" - "github.com/conductorone/baton-sdk/pkg/connectorrunner" - "github.com/conductorone/baton-sdk/pkg/logging" - "github.com/conductorone/baton-sdk/pkg/types" -) - -const ( - envPrefix = "baton" - defaultLogLevel = "info" - defaultLogFormat = logging.LogFormatJSON -) - -// NewCmd returns a new cobra command that will populate the provided config object, validate it, and run the provided run function. -func NewCmd[T any, PtrT *T]( - ctx context.Context, - name string, - cfg PtrT, - validateF func(ctx context.Context, cfg PtrT) error, - getConnector func(ctx context.Context, cfg PtrT) (types.ConnectorServer, error), - opts ...connectorrunner.Option, -) (*cobra.Command, error) { - err := setupService(name) - if err != nil { - return nil, err - } - - cmd := &cobra.Command{ - Use: name, - Short: name, - SilenceErrors: true, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) error { - v, err := loadConfig(cmd, cfg) - if err != nil { - return err - } - - runCtx, err := initLogger( - ctx, - name, - logging.WithLogFormat(v.GetString("log-format")), - logging.WithLogLevel(v.GetString("log-level")), - ) - if err != nil { - return err - } - - err = validateF(ctx, cfg) - if err != nil { - return err - } - - l := ctxzap.Extract(runCtx) - - if isService() { - runCtx, err = runService(runCtx, name) - if err != nil { - l.Error("error running service", zap.Error(err)) - return err - } - } - - c, err := getConnector(runCtx, cfg) - if err != nil { - return err - } - - daemonMode := v.GetString("client-id") != "" || isService() - if daemonMode { - if v.GetString("client-id") == "" { - return fmt.Errorf("client-id is required in service mode") - } - if v.GetString("client-secret") == "" { - return fmt.Errorf("client-secret is required in service mode") - } - opts = append(opts, connectorrunner.WithClientCredentials(v.GetString("client-id"), v.GetString("client-secret"))) - } else { - switch { - case v.GetString("grant-entitlement") != "": - opts = append(opts, - connectorrunner.WithProvisioningEnabled(), - connectorrunner.WithOnDemandGrant( - v.GetString("file"), - v.GetString("grant-entitlement"), - v.GetString("grant-principal"), - v.GetString("grant-principal-type"), - )) - case v.GetString("revoke-grant") != "": - opts = append(opts, - connectorrunner.WithProvisioningEnabled(), - connectorrunner.WithOnDemandRevoke( - v.GetString("file"), - v.GetString("revoke-grant"), - )) - case v.GetBool("event-feed"): - opts = append(opts, connectorrunner.WithOnDemandEventStream()) - case v.GetString("create-account-login") != "": - opts = append(opts, - connectorrunner.WithProvisioningEnabled(), - connectorrunner.WithOnDemandCreateAccount( - v.GetString("file"), - v.GetString("create-account-login"), - v.GetString("create-account-email"), - )) - case v.GetString("delete-resource") != "": - opts = append(opts, - connectorrunner.WithProvisioningEnabled(), - connectorrunner.WithOnDemandDeleteResource( - v.GetString("file"), - v.GetString("delete-resource"), - v.GetString("delete-resource-type"), - )) - case v.GetString("rotate-credentials") != "": - opts = append(opts, - connectorrunner.WithProvisioningEnabled(), - connectorrunner.WithOnDemandRotateCredentials( - v.GetString("file"), - v.GetString("rotate-credentials"), - v.GetString("rotate-credentials-type"), - )) - case v.GetBool("create-ticket"): - opts = append(opts, - connectorrunner.WithTicketingEnabled(), - connectorrunner.WithCreateTicket(v.GetString("ticket-template-path"))) - case v.GetBool("list-ticket-schemas"): - opts = append(opts, - connectorrunner.WithTicketingEnabled(), - connectorrunner.WithListTicketSchemas()) - case v.GetBool("get-ticket"): - opts = append(opts, - connectorrunner.WithTicketingEnabled(), - connectorrunner.WithGetTicket(v.GetString("ticket-id"))) - default: - opts = append(opts, connectorrunner.WithOnDemandSync(v.GetString("file"))) - } - } - - if v.GetString("c1z-temp-dir") != "" { - c1zTmpDir := v.GetString("c1z-temp-dir") - if _, err := os.Stat(c1zTmpDir); os.IsNotExist(err) { - return fmt.Errorf("the specified c1z temp dir does not exist: %s", c1zTmpDir) - } - opts = append(opts, connectorrunner.WithTempDir(v.GetString("c1z-temp-dir"))) - } - - r, err := connectorrunner.NewConnectorRunner(runCtx, c, opts...) - if err != nil { - l.Error("error creating connector runner", zap.Error(err)) - return err - } - defer r.Close(runCtx) - - err = r.Run(runCtx) - if err != nil { - l.Error("error running connector", zap.Error(err)) - return err - } - - return nil - }, - } - - grpcServerCmd := &cobra.Command{ - Use: "_connector-service", - Short: "Start the connector service", - Hidden: true, - RunE: func(cmd *cobra.Command, args []string) error { - v, err := loadConfig(cmd, cfg) - if err != nil { - return err - } - - runCtx, err := initLogger( - ctx, - name, - logging.WithLogFormat(v.GetString("log-format")), - logging.WithLogLevel(v.GetString("log-level")), - ) - if err != nil { - return err - } - - err = validateF(runCtx, cfg) - if err != nil { - return err - } - - c, err := getConnector(runCtx, cfg) - if err != nil { - return err - } - - var copts []connector.Option - - if v.GetBool("provisioning") { - copts = append(copts, connector.WithProvisioningEnabled()) - } - - if v.GetBool("ticketing") { - copts = append(copts, connector.WithTicketingEnabled()) - } - - switch { - case v.GetString("grant-entitlement") != "": - copts = append(copts, connector.WithProvisioningEnabled()) - case v.GetString("revoke-grant") != "": - copts = append(copts, connector.WithProvisioningEnabled()) - case v.GetString("create-account-login") != "" || v.GetString("create-account-email") != "": - copts = append(copts, connector.WithProvisioningEnabled()) - case v.GetString("delete-resource") != "" || v.GetString("delete-resource-type") != "": - copts = append(copts, connector.WithProvisioningEnabled()) - case v.GetString("rotate-credentials") != "" || v.GetString("rotate-credentials-type") != "": - copts = append(copts, connector.WithProvisioningEnabled()) - case v.GetBool("create-ticket"): - copts = append(copts, connector.WithTicketingEnabled()) - case v.GetBool("list-ticket-schemas"): - copts = append(copts, connector.WithTicketingEnabled()) - case v.GetBool("get-ticket"): - copts = append(copts, connector.WithTicketingEnabled()) - } - - cw, err := connector.NewWrapper(runCtx, c, copts...) - if err != nil { - return err - } - - var cfgStr string - scn := bufio.NewScanner(os.Stdin) - for scn.Scan() { - cfgStr = scn.Text() - break - } - cfgBytes, err := base64.StdEncoding.DecodeString(cfgStr) - if err != nil { - return err - } - - go func() { - in := make([]byte, 1) - _, err := os.Stdin.Read(in) - if err != nil { - os.Exit(0) - } - }() - - if len(cfgBytes) == 0 { - return fmt.Errorf("unexpected empty input") - } - - serverCfg := &v1.ServerConfig{} - err = proto.Unmarshal(cfgBytes, serverCfg) - if err != nil { - return err - } - - err = serverCfg.ValidateAll() - if err != nil { - return err - } - - return cw.Run(runCtx, serverCfg) - }, - } - - capabilitiesCmd := &cobra.Command{ - Use: "capabilities", - Short: "Get connector capabilities", - RunE: func(cmd *cobra.Command, args []string) error { - v, err := loadConfig(cmd, cfg) - if err != nil { - return err - } - - runCtx, err := initLogger( - ctx, - name, - logging.WithLogFormat(v.GetString("log-format")), - logging.WithLogLevel(v.GetString("log-level")), - ) - if err != nil { - return err - } - - c, err := getConnector(runCtx, cfg) - if err != nil { - return err - } - - md, err := c.GetMetadata(runCtx, &v2.ConnectorServiceGetMetadataRequest{}) - if err != nil { - return err - } - - if md.Metadata.Capabilities == nil { - return fmt.Errorf("connector does not support capabilities") - } - - protoMarshaller := protojson.MarshalOptions{ - Multiline: true, - Indent: " ", - } - - a := &anypb.Any{} - err = anypb.MarshalFrom(a, md.Metadata.Capabilities, proto.MarshalOptions{Deterministic: true}) - if err != nil { - return err - } - - outBytes, err := protoMarshaller.Marshal(a) - if err != nil { - return err - } - - _, err = fmt.Fprint(os.Stdout, string(outBytes)) - if err != nil { - return err - } - - return nil - }, - } - - cmd.AddCommand(grpcServerCmd) - cmd.AddCommand(capabilitiesCmd) - - // Flags for file management - cmd.PersistentFlags().String("c1z-temp-dir", "", "The directory to store temporary files in. It "+ - "must exist, and write access is required. Defaults to the OS temporary directory. ($BATON_C1Z_TEMP_DIR)") - if err := cmd.PersistentFlags().MarkHidden("c1z-temp-dir"); err != nil { - return nil, err - } - - // Flags for logging configuration - cmd.PersistentFlags().String("log-level", defaultLogLevel, "The log level: debug, info, warn, error ($BATON_LOG_LEVEL)") - cmd.PersistentFlags().String("log-format", defaultLogFormat, "The output format for logs: json, console ($BATON_LOG_FORMAT)") - - // Flags for direct syncing and provisioning - cmd.PersistentFlags().StringP("file", "f", "sync.c1z", "The path to the c1z file to sync with ($BATON_FILE)") - - // TODO (ggreer): simplify command line flags. make one action and reuse entitlement, resource, etc. - // baton-connector --provision-action=grant --entitlement=entitlement_id --resource=resource_id --resource-type=resource_type - // baton-connector --provision-action=revoke --grant=grant_id - // baton-connector --provision-action=delete --resource-id=resource_id --resource-type=resource_type - // baton-connector --provision-action=create-account --login=login --email=email - // baton-connector --provision-action=rotate-credentials --resource-id=resource_id --resource-type=resource_type - - cmd.PersistentFlags().String("grant-entitlement", "", "The id of the entitlement to grant to the supplied principal ($BATON_GRANT_ENTITLEMENT)") - cmd.PersistentFlags().String("grant-principal", "", "The id of the resource to grant the entitlement to ($BATON_GRANT_PRINCIPAL)") - cmd.PersistentFlags().String("grant-principal-type", "", "The resource type of the principal to grant the entitlement to ($BATON_GRANT_PRINCIPAL_TYPE)") - cmd.PersistentFlags().String("revoke-grant", "", "The grant to revoke ($BATON_REVOKE_GRANT)") - cmd.PersistentFlags().Bool("event-feed", false, "Read feed events to stdout ($BATON_EVENT_FEED)") - cmd.MarkFlagsRequiredTogether("grant-entitlement", "grant-principal", "grant-principal-type") - - cmd.PersistentFlags().String("create-account-login", "", "The login of the account to create ($BATON_CREATE_ACCOUNT_LOGIN)") - cmd.PersistentFlags().String("create-account-email", "", "The email of the account to create ($BATON_CREATE_ACCOUNT_EMAIL)") - - cmd.PersistentFlags().String("delete-resource", "", "The id of the resource to delete ($BATON_DELETE_RESOURCE)") - cmd.PersistentFlags().String("delete-resource-type", "", "The type of the resource to delete ($BATON_DELETE_RESOURCE_TYPE)") - - cmd.PersistentFlags().String("rotate-credentials", "", "The id of the resource to rotate credentials on ($BATON_ROTATE_CREDENTIALS)") - cmd.PersistentFlags().String("rotate-credentials-type", "", "The type of the resource to rotate credentials on ($BATON_ROTATE_CREDENTIALS_TYPE)") - - cmd.PersistentFlags().Bool("ticketing", false, "This must be set to enable ticketing support ($BATON_TICKETING)") - cmd.PersistentFlags().Bool("create-ticket", false, "Create ticket ($BATON_CREATE_TICKET)") - cmd.PersistentFlags().String("ticket-template-path", "", "A JSON file describing the ticket to create ($BATON_TICKET_TEMPLATE_PATH)") - - cmd.PersistentFlags().Bool("list-ticket-schemas", false, "List ticket schemas ($BATON_LIST_SCHEMAS)") - - cmd.PersistentFlags().Bool("get-ticket", false, "Get ticket ($BATON_GET_TICKET)") - cmd.PersistentFlags().String("ticket-id", "", "The ID of the ticket to get ($BATON_TICKET_ID)") - - cmd.MarkFlagsMutuallyExclusive( - "grant-entitlement", - "revoke-grant", - "create-account-login", - "delete-resource", - "rotate-credentials", - "event-feed", - "create-ticket", - "get-ticket", - "list-ticket-schemas", - ) - cmd.MarkFlagsMutuallyExclusive( - "grant-entitlement", - "revoke-grant", - "create-account-email", - "delete-resource-type", - "rotate-credentials-type", - "event-feed", - "create-ticket", - "get-ticket", - "list-ticket-schemas", - ) - err = cmd.PersistentFlags().MarkHidden("grant-entitlement") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("grant-principal") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("grant-principal-type") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("revoke-grant") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("event-feed") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("create-ticket") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("ticket-template-path") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("list-ticket-schemas") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("get-ticket") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("ticket-id") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("create-account-login") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("create-account-email") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("delete-resource") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("delete-resource-type") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("rotate-credentials") - if err != nil { - return nil, err - } - err = cmd.PersistentFlags().MarkHidden("rotate-credentials-type") - if err != nil { - return nil, err - } - - // Flags for daemon mode - cmd.PersistentFlags().String("client-id", "", "The client ID used to authenticate with ConductorOne ($BATON_CLIENT_ID)") - cmd.PersistentFlags().String("client-secret", "", "The client secret used to authenticate with ConductorOne ($BATON_CLIENT_SECRET)") - cmd.PersistentFlags().BoolP("provisioning", "p", false, "This must be set in order for provisioning actions to be enabled. ($BATON_PROVISIONING)") - cmd.MarkFlagsRequiredTogether("client-id", "client-secret") - cmd.MarkFlagsMutuallyExclusive("file", "client-id") - cmd.MarkFlagsRequiredTogether("create-ticket", "ticket-template-path") - cmd.MarkFlagsRequiredTogether("get-ticket", "ticket-id") - // Add a hook for additional commands to be added to the root command. - // We use this for OS specific commands. - cmd.AddCommand(additionalCommands(name, cfg)...) - - err = configToCmdFlags(cmd, cfg) - if err != nil { - return nil, err - } - - return cmd, nil -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go new file mode 100644 index 0000000..34edb0c --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go @@ -0,0 +1,323 @@ +package cli + +import ( + "bufio" + "context" + "encoding/base64" + "fmt" + "os" + + "github.com/conductorone/baton-sdk/internal/connector" + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + v1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1" + "github.com/conductorone/baton-sdk/pkg/connectorrunner" + "github.com/conductorone/baton-sdk/pkg/field" + "github.com/conductorone/baton-sdk/pkg/logging" + "github.com/conductorone/baton-sdk/pkg/types" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "go.uber.org/zap" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +type GetConnectorFunc func(context.Context, *viper.Viper) (types.ConnectorServer, error) + +func MakeMainCommand( + ctx context.Context, + name string, + v *viper.Viper, + confschema field.Configuration, + getconnector GetConnectorFunc, + opts ...connectorrunner.Option, +) func(*cobra.Command, []string) error { + return func(*cobra.Command, []string) error { + // validate required fields and relationship constraints + if err := field.Validate(confschema, v); err != nil { + return err + } + + runCtx, err := initLogger( + ctx, + name, + logging.WithLogFormat(v.GetString("log-format")), + logging.WithLogLevel(v.GetString("log-level")), + ) + if err != nil { + return err + } + + l := ctxzap.Extract(runCtx) + + if isService() { + runCtx, err = runService(runCtx, name) + if err != nil { + l.Error("error running service", zap.Error(err)) + return err + } + } + + c, err := getconnector(runCtx, v) + if err != nil { + return err + } + + daemonMode := v.GetString("client-id") != "" || isService() + if daemonMode { + if v.GetString("client-id") == "" { + return fmt.Errorf("client-id is required in service mode") + } + if v.GetString("client-secret") == "" { + return fmt.Errorf("client-secret is required in service mode") + } + opts = append( + opts, + connectorrunner.WithClientCredentials( + v.GetString("client-id"), + v.GetString("client-secret"), + ), + ) + } else { + switch { + case v.GetString("grant-entitlement") != "": + opts = append(opts, + connectorrunner.WithProvisioningEnabled(), + connectorrunner.WithOnDemandGrant( + v.GetString("file"), + v.GetString("grant-entitlement"), + v.GetString("grant-principal"), + v.GetString("grant-principal-type"), + )) + case v.GetString("revoke-grant") != "": + opts = append(opts, + connectorrunner.WithProvisioningEnabled(), + connectorrunner.WithOnDemandRevoke( + v.GetString("file"), + v.GetString("revoke-grant"), + )) + case v.GetBool("event-feed"): + opts = append(opts, connectorrunner.WithOnDemandEventStream()) + case v.GetString("create-account-login") != "": + opts = append(opts, + connectorrunner.WithProvisioningEnabled(), + connectorrunner.WithOnDemandCreateAccount( + v.GetString("file"), + v.GetString("create-account-login"), + v.GetString("create-account-email"), + )) + case v.GetString("delete-resource") != "": + opts = append(opts, + connectorrunner.WithProvisioningEnabled(), + connectorrunner.WithOnDemandDeleteResource( + v.GetString("file"), + v.GetString("delete-resource"), + v.GetString("delete-resource-type"), + )) + case v.GetString("rotate-credentials") != "": + opts = append(opts, + connectorrunner.WithProvisioningEnabled(), + connectorrunner.WithOnDemandRotateCredentials( + v.GetString("file"), + v.GetString("rotate-credentials"), + v.GetString("rotate-credentials-type"), + )) + case v.GetBool("create-ticket"): + opts = append(opts, + connectorrunner.WithTicketingEnabled(), + connectorrunner.WithCreateTicket(v.GetString("ticket-template-path"))) + case v.GetBool("list-ticket-schemas"): + opts = append(opts, + connectorrunner.WithTicketingEnabled(), + connectorrunner.WithListTicketSchemas()) + case v.GetBool("get-ticket"): + opts = append(opts, + connectorrunner.WithTicketingEnabled(), + connectorrunner.WithGetTicket(v.GetString("ticket-id"))) + default: + opts = append(opts, connectorrunner.WithOnDemandSync(v.GetString("file"))) + } + } + + if v.GetString("c1z-temp-dir") != "" { + c1zTmpDir := v.GetString("c1z-temp-dir") + if _, err := os.Stat(c1zTmpDir); os.IsNotExist(err) { + return fmt.Errorf("the specified c1z temp dir does not exist: %s", c1zTmpDir) + } + opts = append(opts, connectorrunner.WithTempDir(v.GetString("c1z-temp-dir"))) + } + + r, err := connectorrunner.NewConnectorRunner(runCtx, c, opts...) + if err != nil { + l.Error("error creating connector runner", zap.Error(err)) + return err + } + defer r.Close(runCtx) + + err = r.Run(runCtx) + if err != nil { + l.Error("error running connector", zap.Error(err)) + return err + } + + return nil + } +} + +func MakeGRPCServerCommand( + ctx context.Context, + name string, + v *viper.Viper, + confschema field.Configuration, + getconnector GetConnectorFunc, +) func(*cobra.Command, []string) error { + return func(*cobra.Command, []string) error { + // validate required fields and relationship constraints + if err := field.Validate(confschema, v); err != nil { + return err + } + + runCtx, err := initLogger( + ctx, + name, + logging.WithLogFormat(v.GetString("log-format")), + logging.WithLogLevel(v.GetString("log-level")), + ) + if err != nil { + return err + } + + c, err := getconnector(runCtx, v) + if err != nil { + return err + } + + var copts []connector.Option + + if v.GetBool("provisioning") { + copts = append(copts, connector.WithProvisioningEnabled()) + } + + if v.GetBool("ticketing") { + copts = append(copts, connector.WithTicketingEnabled()) + } + + switch { + case v.GetString("grant-entitlement") != "": + copts = append(copts, connector.WithProvisioningEnabled()) + case v.GetString("revoke-grant") != "": + copts = append(copts, connector.WithProvisioningEnabled()) + case v.GetString("create-account-login") != "" || v.GetString("create-account-email") != "": + copts = append(copts, connector.WithProvisioningEnabled()) + case v.GetString("delete-resource") != "" || v.GetString("delete-resource-type") != "": + copts = append(copts, connector.WithProvisioningEnabled()) + case v.GetString("rotate-credentials") != "" || v.GetString("rotate-credentials-type") != "": + copts = append(copts, connector.WithProvisioningEnabled()) + case v.GetBool("create-ticket"): + copts = append(copts, connector.WithTicketingEnabled()) + case v.GetBool("list-ticket-schemas"): + copts = append(copts, connector.WithTicketingEnabled()) + case v.GetBool("get-ticket"): + copts = append(copts, connector.WithTicketingEnabled()) + } + + cw, err := connector.NewWrapper(runCtx, c, copts...) + if err != nil { + return err + } + + var cfgStr string + scn := bufio.NewScanner(os.Stdin) + for scn.Scan() { + cfgStr = scn.Text() + break + } + cfgBytes, err := base64.StdEncoding.DecodeString(cfgStr) + if err != nil { + return err + } + + // NOTE (shackra): I don't understand this goroutine + go func() { + in := make([]byte, 1) + _, err := os.Stdin.Read(in) + if err != nil { + os.Exit(0) + } + }() + + if len(cfgBytes) == 0 { + return fmt.Errorf("unexpected empty input") + } + + serverCfg := &v1.ServerConfig{} + err = proto.Unmarshal(cfgBytes, serverCfg) + if err != nil { + return err + } + + err = serverCfg.ValidateAll() + if err != nil { + return err + } + + return cw.Run(runCtx, serverCfg) + } +} + +func MakeCapabilitiesCommand( + ctx context.Context, + name string, + v *viper.Viper, + getconnector GetConnectorFunc, +) func(*cobra.Command, []string) error { + return func(*cobra.Command, []string) error { + runCtx, err := initLogger( + ctx, + name, + logging.WithLogFormat(v.GetString("log-format")), + logging.WithLogLevel(v.GetString("log-level")), + ) + if err != nil { + return err + } + + c, err := getconnector(runCtx, v) + if err != nil { + return err + } + + md, err := c.GetMetadata(runCtx, &v2.ConnectorServiceGetMetadataRequest{}) + if err != nil { + return err + } + + if md.Metadata.Capabilities == nil { + return fmt.Errorf("connector does not support capabilities") + } + + protoMarshaller := protojson.MarshalOptions{ + Multiline: true, + Indent: " ", + } + + a := &anypb.Any{} + err = anypb.MarshalFrom(a, md.Metadata.Capabilities, proto.MarshalOptions{Deterministic: true}) + if err != nil { + return err + } + + outBytes, err := protoMarshaller.Marshal(a) + if err != nil { + return err + } + + _, err = fmt.Fprint(os.Stdout, string(outBytes)) + if err != nil { + return err + } + + return nil + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/config.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/config.go deleted file mode 100644 index a271a3e..0000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/cli/config.go +++ /dev/null @@ -1,146 +0,0 @@ -package cli - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -type BaseConfig struct { - LogLevel string `mapstructure:"log-level"` - LogFormat string `mapstructure:"log-format"` - C1zPath string `mapstructure:"file"` - ClientID string `mapstructure:"client-id"` - ClientSecret string `mapstructure:"client-secret"` - GrantEntitlementID string `mapstructure:"grant-entitlement"` - GrantPrincipalID string `mapstructure:"grant-principal"` - GrantPrincipalType string `mapstructure:"grant-principal-type"` - RevokeGrantID string `mapstructure:"revoke-grant"` - C1zTempDir string `mapstructure:"c1z-temp-dir"` -} - -func getConfigPath(customPath string) (string, string, error) { - if customPath != "" { - cfgDir, cfgFile := filepath.Split(filepath.Clean(customPath)) - if cfgDir == "" { - cfgDir = "." - } - - ext := filepath.Ext(cfgFile) - if ext == "" || (ext != ".yaml" && ext != ".yml") { - return "", "", errors.New("expected config file to have .yaml or .yml extension") - } - - return strings.TrimSuffix(cfgDir, string(filepath.Separator)), strings.TrimSuffix(cfgFile, ext), nil - } - - return ".", ".baton", nil -} - -// loadConfig sets viper up to parse the config into the provided configuration object. -func loadConfig[T any, PtrT *T](cmd *cobra.Command, cfg PtrT) (*viper.Viper, error) { - v := viper.New() - v.SetConfigType("yaml") - - cfgPath, cfgName, err := getConfigPath(os.Getenv("BATON_CONFIG_PATH")) - if err != nil { - return nil, err - } - - v.SetConfigName(cfgName) - v.AddConfigPath(cfgPath) - - if err := v.ReadInConfig(); err != nil { - if ok := !errors.Is(err, viper.ConfigFileNotFoundError{}); !ok { - return nil, err - } - } - - v.SetEnvPrefix(envPrefix) - v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - v.AutomaticEnv() - if err := v.BindPFlags(cmd.PersistentFlags()); err != nil { - return nil, err - } - if err := v.BindPFlags(cmd.Flags()); err != nil { - return nil, err - } - - if err := v.Unmarshal(cfg); err != nil { - return nil, err - } - - return v, nil -} - -func configToCmdFlags[T any, PtrT *T](cmd *cobra.Command, cfg PtrT) error { - baseConfigFields := reflect.VisibleFields(reflect.TypeOf(BaseConfig{})) - baseConfigFieldsMap := make(map[string]bool) - for _, field := range baseConfigFields { - baseConfigFieldsMap[field.Name] = true - } - - fields := reflect.VisibleFields(reflect.TypeOf(*cfg)) - for _, field := range fields { - // ignore BaseConfig fields - if _, ok := baseConfigFieldsMap[field.Name]; ok { - continue - } - if field.Name == "BaseConfig" { - continue - } - - cfgField := field.Tag.Get("mapstructure") - if cfgField == "" { - return fmt.Errorf("mapstructure tag is required on config field %s", field.Name) - } - description := field.Tag.Get("description") - if description == "" { - // Skip fields without descriptions for backwards compatibility - continue - } - defaultValueStr := field.Tag.Get("defaultValue") - - envVarName := strings.ReplaceAll(strings.ToUpper(cfgField), "-", "_") - description = fmt.Sprintf("%s ($BATON_%s)", description, envVarName) - switch field.Type.Kind() { - case reflect.String: - cmd.PersistentFlags().String(cfgField, defaultValueStr, description) - case reflect.Bool: - defaultValue, err := strconv.ParseBool(defaultValueStr) - if defaultValueStr != "" && err != nil { - return fmt.Errorf("invalid default value for config field %s: %w", field.Name, err) - } - cmd.PersistentFlags().Bool(cfgField, defaultValue, description) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - defaultValue, err := strconv.ParseInt(defaultValueStr, 10, 64) - if defaultValueStr != "" && err != nil { - return fmt.Errorf("invalid default value for config field %s: %w", field.Name, err) - } - cmd.PersistentFlags().Int64(cfgField, defaultValue, description) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - defaultValue, err := strconv.ParseUint(defaultValueStr, 10, 64) - if defaultValueStr != "" && err != nil { - return fmt.Errorf("invalid default value for config field %s: %w", field.Name, err) - } - cmd.PersistentFlags().Uint64(cfgField, defaultValue, description) - case reflect.Float32, reflect.Float64: - defaultValue, err := strconv.ParseFloat(defaultValueStr, 64) - if defaultValueStr != "" && err != nil { - return fmt.Errorf("invalid default value for config field %s: %w", field.Name, err) - } - cmd.PersistentFlags().Float64(cfgField, defaultValue, description) - default: - return fmt.Errorf("unsupported type %s for config field %s", field.Type.Kind(), field.Name) - } - } - - return nil -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_unix.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_unix.go index 19d9533..d9e8ad8 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_unix.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_unix.go @@ -5,6 +5,7 @@ package cli import ( "context" + "github.com/conductorone/baton-sdk/pkg/field" "github.com/conductorone/baton-sdk/pkg/logging" "github.com/spf13/cobra" ) @@ -13,18 +14,14 @@ func isService() bool { return false } -func setupService(name string) error { - return nil -} - -func additionalCommands[T any, PtrT *T](connectorName string, cfg PtrT) []*cobra.Command { - return nil -} - -func runService(ctx context.Context, name string) (context.Context, error) { +func runService(ctx context.Context, _ string) (context.Context, error) { return ctx, nil } -func initLogger(ctx context.Context, name string, opts ...logging.Option) (context.Context, error) { +func initLogger(ctx context.Context, _ string, opts ...logging.Option) (context.Context, error) { return logging.Init(ctx, opts...) } + +func AdditionalCommands(_ string, _ []field.SchemaField) []*cobra.Command { + return nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_windows.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_windows.go index ed2d4a9..5ba7498 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_windows.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/cli/service_windows.go @@ -11,9 +11,9 @@ import ( "path/filepath" "reflect" "strconv" - "strings" "time" + "github.com/conductorone/baton-sdk/pkg/field" "github.com/conductorone/baton-sdk/pkg/logging" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "github.com/spf13/cobra" @@ -272,48 +272,50 @@ func getWindowsService(ctx context.Context, name string) (*mgr.Service, func(), }, nil } -func interactiveSetup[T any, PtrT *T](ctx context.Context, outputFilePath string, cfg PtrT) error { +func interactiveSetup(ctx context.Context, outputFilePath string, fields []field.SchemaField) error { l := ctxzap.Extract(ctx) - var ret []reflect.StructField - fields := reflect.VisibleFields(reflect.TypeOf(*cfg)) - for _, field := range fields { - if _, ok := skipServiceSetupFields[field.Name]; !ok { - ret = append(ret, field) + config := make(map[string]interface{}) + for _, vfield := range fields { + if vfield.GetName() == "" { + return fmt.Errorf("field has no name") } - } - config := make(map[string]interface{}) - for _, field := range ret { - var input string - cfgField := field.Tag.Get("mapstructure") - if cfgField == "" { - return fmt.Errorf("mapstructure tag is required on config field %s", field.Name) + // ignore any fields from the default set + if field.IsFieldAmongDefaultList(vfield) { + continue } - fmt.Printf("Enter %s: ", field.Name) + var input string + fmt.Printf("Enter %s: ", vfield.GetName()) scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { input = scanner.Text() break } - switch reflect.ValueOf(cfg).Elem().FieldByName(field.Name).Type() { - case stringReflectType: - config[cfgField] = input - - case boolReflectType: + switch vfield.GetType() { + case reflect.Bool: b, err := strconv.ParseBool(input) if err != nil { return err } - config[cfgField] = b + config[vfield.GetName()] = b - case stringSliceReflectType: - config[cfgField] = strings.Split(input, ",") + case reflect.String: + config[vfield.GetName()] = input + case reflect.Int: + i, err := strconv.Atoi(input) + if err != nil { + return err + } + + config[vfield.GetName()] = i + + // TODO (shackra): add support for []string in SDK default: - l.Error("Unsupported type for interactive config.", zap.String("type", field.Type.String())) + l.Error("Unsupported type for interactive config.", zap.String("type", vfield.GetType().String())) return errors.New("unsupported type for interactive config") } } @@ -349,52 +351,40 @@ func interactiveSetup[T any, PtrT *T](ctx context.Context, outputFilePath string return nil } -func installCmd[T any, PtrT *T](name string, cfg PtrT) *cobra.Command { +func installCmd(name string, fields []field.SchemaField) *cobra.Command { cmd := &cobra.Command{ Use: "setup", Short: fmt.Sprintf("Setup and configure the %s service", name), RunE: func(cmd *cobra.Command, args []string) error { - ctx, err := initLogger( - context.Background(), - name, - logging.WithLogFormat(logging.LogFormatConsole), - logging.WithLogLevel("info"), - ) - + ctx, err := initLogger(context.Background(), name, logging.WithLogFormat(logging.LogFormatConsole), logging.WithLogLevel("info")) l := ctxzap.Extract(ctx) - svcMgr, err := mgr.Connect() if err != nil { l.Error("Failed to connect to service manager.", zap.Error(err)) return err } defer svcMgr.Disconnect() - s, err := svcMgr.OpenService(name) if err == nil { s.Close() return fmt.Errorf("%s is already installed as a service. Please run '%s remove' to remove it first.", name, os.Args[0]) } - - err = interactiveSetup(ctx, filepath.Join(getConfigDir(name), defaultConfigFile), cfg) + err = interactiveSetup(ctx, filepath.Join(getConfigDir(name), defaultConfigFile), fields) if err != nil { l.Error("Failed to setup service.", zap.Error(err)) return err } - exePath, err := getExePath() if err != nil { l.Error("Failed to get executable path.", zap.Error(err)) return err } - s, err = svcMgr.CreateService(name, exePath, mgr.Config{DisplayName: name}) if err != nil { l.Error("Failed to create service.", zap.Error(err), zap.String("service_name", name)) return err } defer s.Close() - err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) if err != nil { l.Error("Failed to install event log source.", zap.Error(err)) @@ -405,7 +395,6 @@ func installCmd[T any, PtrT *T](name string, cfg PtrT) *cobra.Command { } return err } - l.Info("Successfully installed service.", zap.String("service_name", name)) return nil }, @@ -521,12 +510,12 @@ func runService(ctx context.Context, name string) (context.Context, error) { return ctx, nil } -func additionalCommands[T any, PtrT *T](connectorName string, cfg PtrT) []*cobra.Command { +func AdditionalCommands(connectorName string, fields []field.SchemaField) []*cobra.Command { return []*cobra.Command{ startCmd(connectorName), stopCmd(connectorName), statusCmd(connectorName), - installCmd(connectorName, cfg), + installCmd(connectorName, fields), uninstallCmd(connectorName), } } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/connectorbuilder/connectorbuilder.go b/vendor/github.com/conductorone/baton-sdk/pkg/connectorbuilder/connectorbuilder.go index 4f8531d..48616d4 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/connectorbuilder/connectorbuilder.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/connectorbuilder/connectorbuilder.go @@ -2,8 +2,10 @@ package connectorbuilder import ( "context" + "errors" "fmt" "sort" + "time" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" @@ -15,8 +17,10 @@ import ( v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" "github.com/conductorone/baton-sdk/pkg/annotations" "github.com/conductorone/baton-sdk/pkg/crypto" + "github.com/conductorone/baton-sdk/pkg/metrics" "github.com/conductorone/baton-sdk/pkg/pagination" "github.com/conductorone/baton-sdk/pkg/types" + "github.com/conductorone/baton-sdk/pkg/types/tasks" ) type ResourceSyncer interface { @@ -83,10 +87,16 @@ type builderImpl struct { eventFeed EventProvider cb ConnectorBuilder ticketManager TicketManager + ticketingEnabled bool + m *metrics.M + nowFunc func() time.Time } func (b *builderImpl) ListTicketSchemas(ctx context.Context, request *v2.TicketsServiceListTicketSchemasRequest) (*v2.TicketsServiceListTicketSchemasResponse, error) { + start := b.nowFunc() + tt := tasks.ListTicketSchemasType if b.ticketManager == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: ticket manager not implemented") } @@ -95,12 +105,15 @@ func (b *builderImpl) ListTicketSchemas(ctx context.Context, request *v2.Tickets Token: request.PageToken, }) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing ticket schemas failed: %w", err) } if request.PageToken != "" && request.PageToken == nextPageToken { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing ticket schemas failed: next page token is the same as the current page token. this is most likely a connector bug") } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.TicketsServiceListTicketSchemasResponse{ List: out, NextPageToken: nextPageToken, @@ -109,12 +122,16 @@ func (b *builderImpl) ListTicketSchemas(ctx context.Context, request *v2.Tickets } func (b *builderImpl) CreateTicket(ctx context.Context, request *v2.TicketsServiceCreateTicketRequest) (*v2.TicketsServiceCreateTicketResponse, error) { + start := b.nowFunc() + tt := tasks.CreateTicketType if b.ticketManager == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: ticket manager not implemented") } reqBody := request.GetRequest() if reqBody == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: request body is nil") } cTicket := &v2.Ticket{ @@ -124,13 +141,16 @@ func (b *builderImpl) CreateTicket(ctx context.Context, request *v2.TicketsServi Type: reqBody.GetType(), Labels: reqBody.GetLabels(), CustomFields: reqBody.GetCustomFields(), + RequestedFor: reqBody.GetRequestedFor(), } ticket, annos, err := b.ticketManager.CreateTicket(ctx, cTicket, request.GetSchema()) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: creating ticket failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.TicketsServiceCreateTicketResponse{ Ticket: ticket, Annotations: annos, @@ -138,15 +158,20 @@ func (b *builderImpl) CreateTicket(ctx context.Context, request *v2.TicketsServi } func (b *builderImpl) GetTicket(ctx context.Context, request *v2.TicketsServiceGetTicketRequest) (*v2.TicketsServiceGetTicketResponse, error) { + start := b.nowFunc() + tt := tasks.GetTicketType if b.ticketManager == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: ticket manager not implemented") } ticket, annos, err := b.ticketManager.GetTicket(ctx, request.GetId()) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: getting ticket failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.TicketsServiceGetTicketResponse{ Ticket: ticket, Annotations: annos, @@ -154,15 +179,20 @@ func (b *builderImpl) GetTicket(ctx context.Context, request *v2.TicketsServiceG } func (b *builderImpl) GetTicketSchema(ctx context.Context, request *v2.TicketsServiceGetTicketSchemaRequest) (*v2.TicketsServiceGetTicketSchemaResponse, error) { + start := b.nowFunc() + tt := tasks.GetTicketSchemaType if b.ticketManager == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: ticket manager not implemented") } ticketSchema, annos, err := b.ticketManager.GetTicketSchema(ctx, request.GetId()) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: getting ticket metadata failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.TicketsServiceGetTicketSchemaResponse{ Schema: ticketSchema, Annotations: annos, @@ -170,7 +200,7 @@ func (b *builderImpl) GetTicketSchema(ctx context.Context, request *v2.TicketsSe } // NewConnector creates a new ConnectorServer for a new resource. -func NewConnector(ctx context.Context, in interface{}) (types.ConnectorServer, error) { +func NewConnector(ctx context.Context, in interface{}, opts ...Opt) (types.ConnectorServer, error) { switch c := in.(type) { case ConnectorBuilder: ret := &builderImpl{ @@ -182,6 +212,16 @@ func NewConnector(ctx context.Context, in interface{}) (types.ConnectorServer, e credentialManagers: make(map[string]CredentialManager), cb: c, ticketManager: nil, + nowFunc: time.Now, + } + + err := ret.options(opts...) + if err != nil { + return nil, err + } + + if ret.m == nil { + ret.m = metrics.New(metrics.NewNoOpHandler(ctx)) } if b, ok := c.(EventProvider); ok { @@ -250,6 +290,35 @@ func NewConnector(ctx context.Context, in interface{}) (types.ConnectorServer, e } } +type Opt func(b *builderImpl) error + +func WithTicketingEnabled() Opt { + return func(b *builderImpl) error { + if _, ok := b.cb.(TicketManager); ok { + b.ticketingEnabled = true + return nil + } + return errors.New("external ticketing not supported") + } +} + +func WithMetricsHandler(h metrics.Handler) Opt { + return func(b *builderImpl) error { + b.m = metrics.New(h) + return nil + } +} + +func (b *builderImpl) options(opts ...Opt) error { + for _, opt := range opts { + if err := opt(b); err != nil { + return err + } + } + + return nil +} + func validateProvisionerVersion(ctx context.Context, p ResourceSyncer) error { _, ok := p.(ResourceProvisioner) _, okV2 := p.(ResourceProvisionerV2) @@ -265,19 +334,25 @@ func (b *builderImpl) ListResourceTypes( ctx context.Context, request *v2.ResourceTypesServiceListResourceTypesRequest, ) (*v2.ResourceTypesServiceListResourceTypesResponse, error) { + start := b.nowFunc() + tt := tasks.ListResourceTypesType var out []*v2.ResourceType for _, rb := range b.resourceBuilders { out = append(out, rb.ResourceType(ctx)) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.ResourceTypesServiceListResourceTypesResponse{List: out}, nil } // ListResources returns all available resources for a given resource type ID. func (b *builderImpl) ListResources(ctx context.Context, request *v2.ResourcesServiceListResourcesRequest) (*v2.ResourcesServiceListResourcesResponse, error) { + start := b.nowFunc() + tt := tasks.ListResourcesType rb, ok := b.resourceBuilders[request.ResourceTypeId] if !ok { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: list resources with unknown resource type %s", request.ResourceTypeId) } @@ -286,12 +361,15 @@ func (b *builderImpl) ListResources(ctx context.Context, request *v2.ResourcesSe Token: request.PageToken, }) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing resources failed: %w", err) } if request.PageToken != "" && request.PageToken == nextPageToken { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing resources failed: next page token is the same as the current page token. this is most likely a connector bug") } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.ResourcesServiceListResourcesResponse{ List: out, NextPageToken: nextPageToken, @@ -301,8 +379,11 @@ func (b *builderImpl) ListResources(ctx context.Context, request *v2.ResourcesSe // ListEntitlements returns all the entitlements for a given resource. func (b *builderImpl) ListEntitlements(ctx context.Context, request *v2.EntitlementsServiceListEntitlementsRequest) (*v2.EntitlementsServiceListEntitlementsResponse, error) { + start := b.nowFunc() + tt := tasks.ListEntitlementsType rb, ok := b.resourceBuilders[request.Resource.Id.ResourceType] if !ok { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: list entitlements with unknown resource type %s", request.Resource.Id.ResourceType) } @@ -311,12 +392,15 @@ func (b *builderImpl) ListEntitlements(ctx context.Context, request *v2.Entitlem Token: request.PageToken, }) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing entitlements failed: %w", err) } if request.PageToken != "" && request.PageToken == nextPageToken { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing entitlements failed: next page token is the same as the current page token. this is most likely a connector bug") } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.EntitlementsServiceListEntitlementsResponse{ List: out, NextPageToken: nextPageToken, @@ -326,9 +410,13 @@ func (b *builderImpl) ListEntitlements(ctx context.Context, request *v2.Entitlem // ListGrants lists all the grants for a given resource. func (b *builderImpl) ListGrants(ctx context.Context, request *v2.GrantsServiceListGrantsRequest) (*v2.GrantsServiceListGrantsResponse, error) { - rb, ok := b.resourceBuilders[request.Resource.Id.ResourceType] + start := b.nowFunc() + tt := tasks.ListGrantsType + rid := request.Resource.Id + rb, ok := b.resourceBuilders[rid.ResourceType] if !ok { - return nil, fmt.Errorf("error: list entitlements with unknown resource type %s", request.Resource.Id.ResourceType) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) + return nil, fmt.Errorf("error: list entitlements with unknown resource type %s", rid.ResourceType) } out, nextPageToken, annos, err := rb.Grants(ctx, request.Resource, &pagination.Token{ @@ -336,12 +424,17 @@ func (b *builderImpl) ListGrants(ctx context.Context, request *v2.GrantsServiceL Token: request.PageToken, }) if err != nil { - return nil, fmt.Errorf("error: listing grants failed: %w", err) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) + return nil, fmt.Errorf("error: listing grants for resource %s/%s failed: %w", rid.ResourceType, rid.Resource, err) } if request.PageToken != "" && request.PageToken == nextPageToken { - return nil, fmt.Errorf("error: listing grants failed: next page token is the same as the current page token. this is most likely a connector bug") + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) + return nil, fmt.Errorf("error: listing grants for resource %s/%s failed: next page token is the same as the current page token. this is most likely a connector bug", + rid.ResourceType, + rid.Resource) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.GrantsServiceListGrantsResponse{ List: out, NextPageToken: nextPageToken, @@ -351,13 +444,23 @@ func (b *builderImpl) ListGrants(ctx context.Context, request *v2.GrantsServiceL // GetMetadata gets all metadata for a connector. func (b *builderImpl) GetMetadata(ctx context.Context, request *v2.ConnectorServiceGetMetadataRequest) (*v2.ConnectorServiceGetMetadataResponse, error) { + start := b.nowFunc() + tt := tasks.GetMetadataType md, err := b.cb.Metadata(ctx) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, err } md.Capabilities = getCapabilities(ctx, b) + annos := annotations.Annotations(md.Annotations) + if b.ticketManager != nil { + annos.Append(&v2.ExternalTicketSettings{Enabled: b.ticketingEnabled}) + } + md.Annotations = annos + + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.ConnectorServiceGetMetadataResponse{Metadata: md}, nil } @@ -415,6 +518,8 @@ func (b *builderImpl) Validate(ctx context.Context, request *v2.ConnectorService } func (b *builderImpl) Grant(ctx context.Context, request *v2.GrantManagerServiceGrantRequest) (*v2.GrantManagerServiceGrantResponse, error) { + start := b.nowFunc() + tt := tasks.GrantType l := ctxzap.Extract(ctx) rt := request.Entitlement.Resource.Id.ResourceType @@ -423,9 +528,11 @@ func (b *builderImpl) Grant(ctx context.Context, request *v2.GrantManagerService annos, err := provisioner.Grant(ctx, request.Principal, request.Entitlement) if err != nil { l.Error("error: grant failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: grant failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.GrantManagerServiceGrantResponse{Annotations: annos}, nil } @@ -434,17 +541,23 @@ func (b *builderImpl) Grant(ctx context.Context, request *v2.GrantManagerService grants, annos, err := provisionerV2.Grant(ctx, request.Principal, request.Entitlement) if err != nil { l.Error("error: grant failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: grant failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.GrantManagerServiceGrantResponse{Annotations: annos, Grants: grants}, nil } l.Error("error: resource type does not have provisioner configured", zap.String("resource_type", rt)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: resource type does not have provisioner configured") } func (b *builderImpl) Revoke(ctx context.Context, request *v2.GrantManagerServiceRevokeRequest) (*v2.GrantManagerServiceRevokeResponse, error) { + start := b.nowFunc() + tt := tasks.RevokeType + l := ctxzap.Extract(ctx) rt := request.Grant.Entitlement.Resource.Id.ResourceType @@ -453,6 +566,7 @@ func (b *builderImpl) Revoke(ctx context.Context, request *v2.GrantManagerServic annos, err := provisioner.Revoke(ctx, request.Grant) if err != nil { l.Error("error: revoke failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: revoke failed: %w", err) } return &v2.GrantManagerServiceRevokeResponse{Annotations: annos}, nil @@ -463,12 +577,16 @@ func (b *builderImpl) Revoke(ctx context.Context, request *v2.GrantManagerServic annos, err := provisionerV2.Revoke(ctx, request.Grant) if err != nil { l.Error("error: revoke failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: revoke failed: %w", err) } + + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.GrantManagerServiceRevokeResponse{Annotations: annos}, nil } l.Error("error: resource type does not have provisioner configured", zap.String("resource_type", rt)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, "resource type does not have provisioner configured") } @@ -479,7 +597,10 @@ func (b *builderImpl) GetAsset(request *v2.AssetServiceGetAssetRequest, server v } func (b *builderImpl) ListEvents(ctx context.Context, request *v2.ListEventsRequest) (*v2.ListEventsResponse, error) { + start := b.nowFunc() + tt := tasks.ListEventsType if b.eventFeed == nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: event feed not implemented") } events, streamState, annotations, err := b.eventFeed.ListEvents(ctx, request.StartAt, &pagination.StreamToken{ @@ -487,8 +608,10 @@ func (b *builderImpl) ListEvents(ctx context.Context, request *v2.ListEventsRequ Cursor: request.Cursor, }) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: listing events failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.ListEventsResponse{ Events: events, Cursor: streamState.Cursor, @@ -498,6 +621,8 @@ func (b *builderImpl) ListEvents(ctx context.Context, request *v2.ListEventsRequ } func (b *builderImpl) CreateResource(ctx context.Context, request *v2.CreateResourceRequest) (*v2.CreateResourceResponse, error) { + start := b.nowFunc() + tt := tasks.CreateResourceType l := ctxzap.Extract(ctx) rt := request.GetResource().GetId().GetResourceType() manager, ok := b.resourceManagers[rt] @@ -505,15 +630,21 @@ func (b *builderImpl) CreateResource(ctx context.Context, request *v2.CreateReso resource, annos, err := manager.Create(ctx, request.Resource) if err != nil { l.Error("error: create resource failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: create resource failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.CreateResourceResponse{Created: resource, Annotations: annos}, nil } l.Error("error: resource type does not have resource manager configured", zap.String("resource_type", rt)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, "resource type does not have resource manager configured") } func (b *builderImpl) DeleteResource(ctx context.Context, request *v2.DeleteResourceRequest) (*v2.DeleteResourceResponse, error) { + start := b.nowFunc() + tt := tasks.DeleteResourceType + l := ctxzap.Extract(ctx) rt := request.GetResourceId().GetResourceType() manager, ok := b.resourceManagers[rt] @@ -521,32 +652,40 @@ func (b *builderImpl) DeleteResource(ctx context.Context, request *v2.DeleteReso annos, err := manager.Delete(ctx, request.GetResourceId()) if err != nil { l.Error("error: delete resource failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: delete resource failed: %w", err) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.DeleteResourceResponse{Annotations: annos}, nil } l.Error("error: resource type does not have resource manager configured", zap.String("resource_type", rt)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, "resource type does not have resource manager configured") } func (b *builderImpl) RotateCredential(ctx context.Context, request *v2.RotateCredentialRequest) (*v2.RotateCredentialResponse, error) { + start := b.nowFunc() + tt := tasks.RotateCredentialsType l := ctxzap.Extract(ctx) rt := request.GetResourceId().GetResourceType() manager, ok := b.credentialManagers[rt] if !ok { l.Error("error: resource type does not have credential manager configured", zap.String("resource_type", rt)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, "resource type does not have credential manager configured") } plaintexts, annos, err := manager.Rotate(ctx, request.GetResourceId(), request.GetCredentialOptions()) if err != nil { l.Error("error: rotate credentials on resource failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: rotate credentials on resource failed: %w", err) } pkem, err := crypto.NewEncryptionManager(request.GetCredentialOptions(), request.GetEncryptionConfigs()) if err != nil { l.Error("error: creating encryption manager failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: creating encryption manager failed: %w", err) } @@ -554,11 +693,13 @@ func (b *builderImpl) RotateCredential(ctx context.Context, request *v2.RotateCr for _, plaintextCredential := range plaintexts { encryptedData, err := pkem.Encrypt(ctx, plaintextCredential) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, err } encryptedDatas = append(encryptedDatas, encryptedData...) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return &v2.RotateCredentialResponse{ Annotations: annos, ResourceId: request.GetResourceId(), @@ -567,20 +708,25 @@ func (b *builderImpl) RotateCredential(ctx context.Context, request *v2.RotateCr } func (b *builderImpl) CreateAccount(ctx context.Context, request *v2.CreateAccountRequest) (*v2.CreateAccountResponse, error) { + start := b.nowFunc() + tt := tasks.CreateAccountType l := ctxzap.Extract(ctx) if b.accountManager == nil { l.Error("error: connector does not have account manager configured") + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, "connector does not have credential manager configured") } result, plaintexts, annos, err := b.accountManager.CreateAccount(ctx, request.GetAccountInfo(), request.GetCredentialOptions()) if err != nil { l.Error("error: create account failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: create account failed: %w", err) } pkem, err := crypto.NewEncryptionManager(request.GetCredentialOptions(), request.GetEncryptionConfigs()) if err != nil { l.Error("error: creating encryption manager failed", zap.Error(err)) + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, fmt.Errorf("error: creating encryption manager failed: %w", err) } @@ -588,6 +734,7 @@ func (b *builderImpl) CreateAccount(ctx context.Context, request *v2.CreateAccou for _, plaintextCredential := range plaintexts { encryptedData, err := pkem.Encrypt(ctx, plaintextCredential) if err != nil { + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, err } encryptedDatas = append(encryptedDatas, encryptedData...) @@ -604,8 +751,10 @@ func (b *builderImpl) CreateAccount(ctx context.Context, request *v2.CreateAccou case *v2.CreateAccountResponse_ActionRequiredResult: rv.Result = &v2.CreateAccountResponse_ActionRequired{ActionRequired: r} default: + b.m.RecordTaskFailure(ctx, tt, b.nowFunc().Sub(start)) return nil, status.Error(codes.Unimplemented, fmt.Sprintf("unknown result type: %T", result)) } + b.m.RecordTaskSuccess(ctx, tt, b.nowFunc().Sub(start)) return rv, nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go b/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go index 596278a..54d3f52 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go @@ -36,10 +36,11 @@ type Writer interface { CurrentSyncStep(ctx context.Context) (string, error) CheckpointSync(ctx context.Context, syncToken string) error EndSync(ctx context.Context) error - PutResourceType(ctx context.Context, resourceType *v2.ResourceType) error - PutResource(ctx context.Context, resource *v2.Resource) error - PutEntitlement(ctx context.Context, entitlement *v2.Entitlement) error - PutGrant(ctx context.Context, grant *v2.Grant) error PutAsset(ctx context.Context, assetRef *v2.AssetRef, contentType string, data []byte) error Cleanup(ctx context.Context) error + + PutGrants(ctx context.Context, grants ...*v2.Grant) error + PutResourceTypes(ctx context.Context, resourceTypes ...*v2.ResourceType) error + PutResources(ctx context.Context, resources ...*v2.Resource) error + PutEntitlements(ctx context.Context, entitlements ...*v2.Entitlement) error } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/assets.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/assets.go index d3f723f..2347a5b 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/assets.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/assets.go @@ -9,7 +9,6 @@ import ( "github.com/doug-martin/goqu/v9" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" ) @@ -64,8 +63,6 @@ func (c *C1File) PutAsset(ctx context.Context, assetRef *v2.AssetRef, contentTyp contentType = "unknown" } - l.Debug("syncing asset", zap.String("content_type", contentType), zap.Int("asset_size", len(data))) - err := c.validateSyncDb(ctx) if err != nil { return err @@ -101,8 +98,6 @@ func (c *C1File) PutAsset(ctx context.Context, assetRef *v2.AssetRef, contentTyp // GetAsset fetches the specified asset from the database, and returns the content type and an io.Reader for the caller to // read the asset from. func (c *C1File) GetAsset(ctx context.Context, request *v2.AssetServiceGetAssetRequest) (string, io.Reader, error) { - ctxzap.Extract(ctx).Debug("fetching asset", zap.String("id", request.Asset.Id)) - err := c.validateDb(ctx) if err != nil { return "", nil, err diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go index b33d1c3..04a3529 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go @@ -8,9 +8,17 @@ import ( "path/filepath" "github.com/doug-martin/goqu/v9" + // NOTE: required to register the dialect for goqu. + // + // If you remove this import, goqu.Dialect("sqlite3") will + // return a copy of the default dialect, which is not what we want, + // and allocates a ton of memory. + _ "github.com/doug-martin/goqu/v9/dialect/sqlite3" + _ "github.com/glebarez/go-sqlite" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/connectorstore" ) type pragma struct { @@ -30,6 +38,8 @@ type C1File struct { pragmas []pragma } +var _ connectorstore.Writer = (*C1File)(nil) + type C1FOption func(*C1File) func WithC1FTmpDir(tempDir string) C1FOption { @@ -50,6 +60,7 @@ func NewC1File(ctx context.Context, dbFilePath string, opts ...C1FOption) (*C1Fi if err != nil { return nil, err } + db := goqu.New("sqlite3", rawDB) c1File := &C1File{ diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/entitlements.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/entitlements.go index 69e4eb6..7191d21 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/entitlements.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/entitlements.go @@ -5,8 +5,6 @@ import ( "fmt" "github.com/doug-martin/goqu/v9" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" "google.golang.org/protobuf/proto" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -51,10 +49,9 @@ func (r *entitlementsTable) Schema() (string, []interface{}) { } func (c *C1File) ListEntitlements(ctx context.Context, request *v2.EntitlementsServiceListEntitlementsRequest) (*v2.EntitlementsServiceListEntitlementsResponse, error) { - ctxzap.Extract(ctx).Debug("listing entitlements") objs, nextPageToken, err := c.listConnectorObjects(ctx, entitlements.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing entitlements: %w", err) } ret := make([]*v2.Entitlement, 0, len(objs)) @@ -74,13 +71,11 @@ func (c *C1File) ListEntitlements(ctx context.Context, request *v2.EntitlementsS } func (c *C1File) GetEntitlement(ctx context.Context, request *reader_v2.EntitlementsReaderServiceGetEntitlementRequest) (*reader_v2.EntitlementsReaderServiceGetEntitlementResponse, error) { - ctxzap.Extract(ctx).Debug("fetching entitlement", zap.String("entitlement_id", request.EntitlementId)) - ret := &v2.Entitlement{} err := c.getConnectorObject(ctx, entitlements.Name(), request.EntitlementId, ret) if err != nil { - return nil, err + return nil, fmt.Errorf("error fetching entitlement '%s': %w", request.EntitlementId, err) } return &reader_v2.EntitlementsReaderServiceGetEntitlementResponse{ @@ -88,27 +83,26 @@ func (c *C1File) GetEntitlement(ctx context.Context, request *reader_v2.Entitlem }, nil } -func (c *C1File) PutEntitlement(ctx context.Context, entitlement *v2.Entitlement) error { - ctxzap.Extract(ctx).Debug("syncing entitlement", zap.String("entitlement_id", entitlement.Id)) - - if entitlement.Resource == nil && entitlement.Resource.Id == nil { - return fmt.Errorf("entitlements must have a non-nil resource") - } +func (c *C1File) PutEntitlements(ctx context.Context, entitlementObjs ...*v2.Entitlement) error { + err := c.db.WithTx(func(tx *goqu.TxDatabase) error { + err := bulkPutConnectorObjectTx(ctx, c, tx, entitlements.Name(), + func(entitlement *v2.Entitlement) (goqu.Record, error) { + return goqu.Record{ + "resource_id": entitlement.Resource.Id.Resource, + "resource_type_id": entitlement.Resource.Id.ResourceType, + }, nil + }, + entitlementObjs..., + ) + if err != nil { + return err + } - query, args, err := c.putConnectorObjectQuery(ctx, entitlements.Name(), entitlement, goqu.Record{ - "resource_id": entitlement.Resource.Id.Resource, - "resource_type_id": entitlement.Resource.Id.ResourceType, + return nil }) if err != nil { return err } - - _, err = c.db.ExecContext(ctx, query, args...) - if err != nil { - return err - } - c.dbUpdated = true - return nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/grants.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/grants.go index 1f82d58..0a3cf5a 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/grants.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/grants.go @@ -5,8 +5,6 @@ import ( "fmt" "github.com/doug-martin/goqu/v9" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" "google.golang.org/protobuf/proto" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -60,11 +58,9 @@ func (r *grantsTable) Schema() (string, []interface{}) { } func (c *C1File) ListGrants(ctx context.Context, request *v2.GrantsServiceListGrantsRequest) (*v2.GrantsServiceListGrantsResponse, error) { - ctxzap.Extract(ctx).Debug("listing grants") - objs, nextPageToken, err := c.listConnectorObjects(ctx, grants.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing grants: %w", err) } ret := make([]*v2.Grant, 0, len(objs)) @@ -84,13 +80,11 @@ func (c *C1File) ListGrants(ctx context.Context, request *v2.GrantsServiceListGr } func (c *C1File) GetGrant(ctx context.Context, request *reader_v2.GrantsReaderServiceGetGrantRequest) (*reader_v2.GrantsReaderServiceGetGrantResponse, error) { - ctxzap.Extract(ctx).Debug("fetching grant", zap.String("grant_id", request.GrantId)) - ret := &v2.Grant{} err := c.getConnectorObject(ctx, grants.Name(), request.GrantId, ret) if err != nil { - return nil, err + return nil, fmt.Errorf("error fetching grant '%s': %w", request.GetGrantId(), err) } return &reader_v2.GrantsReaderServiceGetGrantResponse{ @@ -102,11 +96,9 @@ func (c *C1File) ListGrantsForEntitlement( ctx context.Context, request *reader_v2.GrantsReaderServiceListGrantsForEntitlementRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementResponse, error) { - ctxzap.Extract(ctx).Debug("listing grants for entitlement") - objs, nextPageToken, err := c.listConnectorObjects(ctx, grants.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing grants for entitlement '%s': %w", request.GetEntitlement().GetId(), err) } ret := make([]*v2.Grant, 0, len(objs)) @@ -129,11 +121,9 @@ func (c *C1File) ListGrantsForPrincipal( ctx context.Context, request *reader_v2.GrantsReaderServiceListGrantsForEntitlementRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementResponse, error) { - ctxzap.Extract(ctx).Debug("listing grants for entitlement") - objs, nextPageToken, err := c.listConnectorObjects(ctx, grants.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing grants for principal '%s': %w", request.GetPrincipalId(), err) } ret := make([]*v2.Grant, 0, len(objs)) @@ -156,11 +146,9 @@ func (c *C1File) ListGrantsForResourceType( ctx context.Context, request *reader_v2.GrantsReaderServiceListGrantsForResourceTypeRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForResourceTypeResponse, error) { - ctxzap.Extract(ctx).Debug("listing grants for resource type") - objs, nextPageToken, err := c.listConnectorObjects(ctx, grants.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing grants for resource type '%s': %w", request.GetResourceTypeId(), err) } ret := make([]*v2.Grant, 0, len(objs)) @@ -179,26 +167,28 @@ func (c *C1File) ListGrantsForResourceType( }, nil } -func (c *C1File) PutGrant(ctx context.Context, grant *v2.Grant) error { - ctxzap.Extract(ctx).Debug("syncing grant", zap.String("grant_id", grant.Id)) - - query, args, err := c.putConnectorObjectQuery(ctx, grants.Name(), grant, goqu.Record{ - "resource_type_id": grant.Entitlement.Resource.Id.ResourceType, - "resource_id": grant.Entitlement.Resource.Id.Resource, - "entitlement_id": grant.Entitlement.Id, - "principal_resource_type_id": grant.Principal.Id.ResourceType, - "principal_resource_id": grant.Principal.Id.Resource, +func (c *C1File) PutGrants(ctx context.Context, bulkGrants ...*v2.Grant) error { + err := c.db.WithTx(func(tx *goqu.TxDatabase) error { + err := bulkPutConnectorObjectTx(ctx, c, tx, grants.Name(), + func(grant *v2.Grant) (goqu.Record, error) { + return goqu.Record{ + "resource_type_id": grant.Entitlement.Resource.Id.ResourceType, + "resource_id": grant.Entitlement.Resource.Id.Resource, + "entitlement_id": grant.Entitlement.Id, + "principal_resource_type_id": grant.Principal.Id.ResourceType, + "principal_resource_id": grant.Principal.Id.Resource, + }, nil + }, + bulkGrants..., + ) + if err != nil { + return err + } + return nil }) if err != nil { return err } - - _, err = c.db.ExecContext(ctx, query, args...) - if err != nil { - return err - } - c.dbUpdated = true - return nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resouce_types.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resouce_types.go index 14048e3..a0bb4d7 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resouce_types.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resouce_types.go @@ -4,12 +4,11 @@ import ( "context" "fmt" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" "google.golang.org/protobuf/proto" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" reader_v2 "github.com/conductorone/baton-sdk/pb/c1/reader/v2" + "github.com/doug-martin/goqu/v9" ) const resourceTypesTableVersion = "1" @@ -45,11 +44,9 @@ func (r *resourceTypesTable) Schema() (string, []interface{}) { } func (c *C1File) ListResourceTypes(ctx context.Context, request *v2.ResourceTypesServiceListResourceTypesRequest) (*v2.ResourceTypesServiceListResourceTypesResponse, error) { - ctxzap.Extract(ctx).Debug("listing resource types") - objs, nextPageToken, err := c.listConnectorObjects(ctx, resourceTypes.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing resource types: %w", err) } ret := make([]*v2.ResourceType, 0, len(objs)) @@ -69,13 +66,11 @@ func (c *C1File) ListResourceTypes(ctx context.Context, request *v2.ResourceType } func (c *C1File) GetResourceType(ctx context.Context, request *reader_v2.ResourceTypesReaderServiceGetResourceTypeRequest) (*reader_v2.ResourceTypesReaderServiceGetResourceTypeResponse, error) { - ctxzap.Extract(ctx).Debug("fetching resource type", zap.String("resource_type_id", request.ResourceTypeId)) - ret := &v2.ResourceType{} err := c.getConnectorObject(ctx, resourceTypes.Name(), request.ResourceTypeId, ret) if err != nil { - return nil, err + return nil, fmt.Errorf("error fetching resource type '%s': %w", request.ResourceTypeId, err) } return &reader_v2.ResourceTypesReaderServiceGetResourceTypeResponse{ @@ -83,20 +78,22 @@ func (c *C1File) GetResourceType(ctx context.Context, request *reader_v2.Resourc }, nil } -func (c *C1File) PutResourceType(ctx context.Context, resourceType *v2.ResourceType) error { - ctxzap.Extract(ctx).Debug("syncing resource type", zap.String("resource_type_id", resourceType.Id)) - - query, args, err := c.putConnectorObjectQuery(ctx, resourceTypes.Name(), resourceType, nil) - if err != nil { - return err - } - - _, err = c.db.ExecContext(ctx, query, args...) +func (c *C1File) PutResourceTypes(ctx context.Context, resourceTypesObjs ...*v2.ResourceType) error { + err := c.db.WithTx(func(tx *goqu.TxDatabase) error { + err := bulkPutConnectorObjectTx(ctx, c, tx, resourceTypes.Name(), + func(resource *v2.ResourceType) (goqu.Record, error) { + return nil, nil + }, + resourceTypesObjs..., + ) + if err != nil { + return err + } + return nil + }) if err != nil { return err } - c.dbUpdated = true - return nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resources.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resources.go index a463316..dc23569 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resources.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/resources.go @@ -4,13 +4,12 @@ import ( "context" "fmt" - c1zpb "github.com/conductorone/baton-sdk/pb/c1/c1z/v1" - "github.com/conductorone/baton-sdk/pkg/annotations" "github.com/doug-martin/goqu/v9" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" "google.golang.org/protobuf/proto" + c1zpb "github.com/conductorone/baton-sdk/pb/c1/c1z/v1" + "github.com/conductorone/baton-sdk/pkg/annotations" + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" reader_v2 "github.com/conductorone/baton-sdk/pb/c1/reader/v2" ) @@ -57,11 +56,9 @@ func (r *resourcesTable) Schema() (string, []interface{}) { } func (c *C1File) ListResources(ctx context.Context, request *v2.ResourcesServiceListResourcesRequest) (*v2.ResourcesServiceListResourcesResponse, error) { - ctxzap.Extract(ctx).Debug("listing resources") - objs, nextPageToken, err := c.listConnectorObjects(ctx, resources.Name(), request) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing resources: %w", err) } ret := make([]*v2.Resource, 0, len(objs)) @@ -81,12 +78,6 @@ func (c *C1File) ListResources(ctx context.Context, request *v2.ResourcesService } func (c *C1File) GetResource(ctx context.Context, request *reader_v2.ResourcesReaderServiceGetResourceRequest) (*reader_v2.ResourcesReaderServiceGetResourceResponse, error) { - ctxzap.Extract(ctx).Debug( - "fetching resource", - zap.String("resource_id", request.ResourceId.Resource), - zap.String("resource_type_id", request.ResourceId.ResourceType), - ) - ret := &v2.Resource{} annos := annotations.Annotations(request.GetAnnotations()) syncDetails := &c1zpb.SyncDetails{} @@ -98,7 +89,7 @@ func (c *C1File) GetResource(ctx context.Context, request *reader_v2.ResourcesRe err := c.getResourceObject(ctx, request.ResourceId, ret, syncID) if err != nil { - return nil, err + return nil, fmt.Errorf("error fetching resource '%s': %w", request.ResourceId, err) } return &reader_v2.ResourcesReaderServiceGetResourceResponse{ @@ -106,34 +97,31 @@ func (c *C1File) GetResource(ctx context.Context, request *reader_v2.ResourcesRe }, nil } -func (c *C1File) PutResource(ctx context.Context, resource *v2.Resource) error { - ctxzap.Extract(ctx).Debug( - "syncing resource", - zap.String("resource_id", resource.Id.Resource), - zap.String("resource_type_id", resource.Id.ResourceType), - ) - - updateRecord := goqu.Record{ - "resource_type_id": resource.Id.ResourceType, - "external_id": fmt.Sprintf("%s:%s", resource.Id.ResourceType, resource.Id.Resource), - } - - if resource.ParentResourceId != nil { - updateRecord["parent_resource_type_id"] = resource.ParentResourceId.ResourceType - updateRecord["parent_resource_id"] = resource.ParentResourceId.Resource - } - - query, args, err := c.putConnectorObjectQuery(ctx, resources.Name(), resource, updateRecord) - if err != nil { - return err - } - - _, err = c.db.ExecContext(ctx, query, args...) +func (c *C1File) PutResources(ctx context.Context, resourceObjs ...*v2.Resource) error { + err := c.db.WithTx(func(tx *goqu.TxDatabase) error { + err := bulkPutConnectorObjectTx(ctx, c, tx, resources.Name(), + func(resource *v2.Resource) (goqu.Record, error) { + fields := goqu.Record{ + "resource_type_id": resource.Id.ResourceType, + "external_id": fmt.Sprintf("%s:%s", resource.Id.ResourceType, resource.Id.Resource), + } + + if resource.ParentResourceId != nil { + fields["parent_resource_type_id"] = resource.ParentResourceId.ResourceType + fields["parent_resource_id"] = resource.ParentResourceId.Resource + } + return fields, nil + }, + resourceObjs..., + ) + if err != nil { + return err + } + return nil + }) if err != nil { return err } - c.dbUpdated = true - return nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go index 63f8356..3bd3fb9 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go @@ -70,8 +70,8 @@ type protoHasID interface { GetId() string } -// listConnectorObjects uses a connecter list request to fetch the corresponding data from the local db. -// It returns the raw bytes that need to be unmarshaled into the correct proto message. +// listConnectorObjects uses a connector list request to fetch the corresponding data from the local db. +// It returns the raw bytes that need to be unmarshalled into the correct proto message. func (c *C1File) listConnectorObjects(ctx context.Context, tableName string, req proto.Message) ([][]byte, string, error) { err := c.validateDb(ctx) if err != nil { @@ -231,37 +231,56 @@ func (c *C1File) listConnectorObjects(ctx context.Context, tableName string, req return ret, nextPageToken, nil } -func (c *C1File) putConnectorObjectQuery(ctx context.Context, tableName string, m proto.Message, fields goqu.Record) (string, []interface{}, error) { - err := c.validateSyncDb(ctx) - if err != nil { - return "", nil, err - } +var protoMarshaler = proto.MarshalOptions{Deterministic: true} - messageBlob, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) +func bulkPutConnectorObjectTx[T proto.Message](ctx context.Context, c *C1File, + tx *goqu.TxDatabase, + tableName string, + extractFields func(m T) (goqu.Record, error), + msgs ...T) error { + err := c.validateSyncDb(ctx) if err != nil { - return "", nil, err + return err } - if fields == nil { - fields = goqu.Record{} - } + baseQ := tx.Insert(tableName).Prepared(true) + baseQ = baseQ.OnConflict(goqu.DoUpdate("external_id, sync_id", goqu.C("data").Set(goqu.I("EXCLUDED.data")))) - if _, idSet := fields["external_id"]; !idSet { - idGetter, ok := m.(protoHasID) - if !ok { - return "", nil, fmt.Errorf("unable to get ID for object") + for _, m := range msgs { + messageBlob, err := protoMarshaler.Marshal(m) + if err != nil { + return err } - fields["external_id"] = idGetter.GetId() - } - fields["data"] = messageBlob - fields["sync_id"] = c.currentSyncID - fields["discovered_at"] = time.Now().Format("2006-01-02 15:04:05.999999999") - q := c.db.Insert(tableName).Prepared(true) - q = q.Rows(fields) - q = q.OnConflict(goqu.DoUpdate("external_id, sync_id", goqu.C("data").Set(goqu.I("EXCLUDED.data")))) + fields, err := extractFields(m) + if err != nil { + return err + } + if fields == nil { + fields = goqu.Record{} + } - return q.ToSQL() + if _, idSet := fields["external_id"]; !idSet { + idGetter, ok := any(m).(protoHasID) + if !ok { + return fmt.Errorf("unable to get ID for object") + } + fields["external_id"] = idGetter.GetId() + } + fields["data"] = messageBlob + fields["sync_id"] = c.currentSyncID + fields["discovered_at"] = time.Now().Format("2006-01-02 15:04:05.999999999") + q := baseQ.Rows(fields) + query, args, err := q.ToSQL() + if err != nil { + return err + } + _, err = tx.Exec(query, args...) + if err != nil { + return err + } + } + return nil } func (c *C1File) getResourceObject(ctx context.Context, resourceID *v2.ResourceId, m *v2.Resource, syncID string) error { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go new file mode 100644 index 0000000..13b69f8 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go @@ -0,0 +1,33 @@ +package field + +var defaultRelationship = []SchemaFieldRelationship{ + FieldsRequiredTogether(grantEntitlementField, grantPrincipalField), + FieldsRequiredTogether(clientIDField, clientSecretField), + FieldsRequiredTogether(createTicketField, ticketTemplatePathField), + FieldsRequiredTogether(getTicketField, ticketIDField), + FieldsMutuallyExclusive(fileField, clientIDField), + FieldsMutuallyExclusive( + grantEntitlementField, + revokeGrantField, + createAccountLoginField, + deleteResourceField, + rotateCredentialsField, + eventFeedField, + createTicketField, + getTicketField, + listTicketSchemasField, + ), + FieldsMutuallyExclusive( + grantEntitlementField, + revokeGrantField, + createAccountEmailField, + deleteResourceTypeField, + rotateCredentialsTypeField, + eventFeedField, + listTicketSchemasField, + ), +} + +func EnsureDefaultRelationships(original []SchemaFieldRelationship) []SchemaFieldRelationship { + return append(defaultRelationship, original...) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go new file mode 100644 index 0000000..1de26af --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go @@ -0,0 +1,92 @@ +package field + +import "github.com/conductorone/baton-sdk/pkg/logging" + +var ( + createTicketField = BoolField("create-ticket", WithHidden(true), WithDescription("Create ticket")) + getTicketField = BoolField("get-ticket", WithHidden(true), WithDescription("Get ticket")) + listTicketSchemasField = BoolField("list-ticket-schemas", WithHidden(true), WithDescription("List ticket schemas")) + provisioningField = BoolField("provisioning", WithShortHand("p"), WithDescription("This must be set in order for provisioning actions to be enabled")) + ticketingField = BoolField("ticketing", WithDescription("This must be set to enable ticketing support")) + c1zTmpDirField = StringField("c1z-temp-dir", WithHidden(true), WithDescription("The directory to store temporary files in. It must exist, "+ + "and write access is required. Defaults to the OS temporary directory.")) + clientIDField = StringField("client-id", WithDescription("The client ID used to authenticate with ConductorOne")) + clientSecretField = StringField("client-secret", WithDescription("The client secret used to authenticate with ConductorOne")) + createAccountEmailField = StringField("create-account-email", WithHidden(true), WithDescription("The email of the account to create")) + createAccountLoginField = StringField("create-account-login", WithHidden(true), WithDescription("The login of the account to create")) + deleteResourceField = StringField("delete-resource", WithHidden(true), WithDescription("The id of the resource to delete")) + deleteResourceTypeField = StringField("delete-resource-type", WithHidden(true), WithDescription("The type of the resource to delete")) + eventFeedField = StringField("event-feed", WithHidden(true), WithDescription("Read feed events to stdout")) + fileField = StringField("file", WithShortHand("f"), WithDefaultValue("sync.c1z"), WithDescription("The path to the c1z file to sync with")) + grantEntitlementField = StringField("grant-entitlement", WithHidden(true), WithDescription("The id of the entitlement to grant to the supplied principal")) + grantPrincipalField = StringField("grant-principal", WithHidden(true), WithDescription("The id of the resource to grant the entitlement to")) + grantPrincipalTypeField = StringField("grant-principal-type", WithHidden(true), WithDescription("The resource type of the principal to grant the entitlement to")) + logFormatField = StringField("log-format", WithDefaultValue(logging.LogFormatJSON), WithDescription("The output format for logs: json, console")) + revokeGrantField = StringField("revoke-grant", WithHidden(true), WithDescription("The grant to revoke")) + rotateCredentialsField = StringField("rotate-credentials", WithHidden(true), WithDescription("The id of the resource to rotate credentials on")) + rotateCredentialsTypeField = StringField("rotate-credentials-type", WithHidden(true), WithDescription("The type of the resource to rotate credentials on")) + ticketIDField = StringField("ticket-id", WithHidden(true), WithDescription("The ID of the ticket to get")) + ticketTemplatePathField = StringField("ticket-template-path", WithHidden(true), WithDescription("A JSON file describing the ticket to create")) + logLevelField = StringField("log-level", WithDefaultValue("info"), WithDescription("The log level: debug, info, warn, error")) +) + +// DefaultFields list the default fields expected in every single connector. +var DefaultFields = []SchemaField{ + createTicketField, + getTicketField, + listTicketSchemasField, + provisioningField, + ticketingField, + c1zTmpDirField, + clientIDField, + clientSecretField, + createAccountEmailField, + createAccountLoginField, + deleteResourceField, + deleteResourceTypeField, + eventFeedField, + fileField, + grantEntitlementField, + grantPrincipalField, + grantPrincipalTypeField, + logFormatField, + revokeGrantField, + rotateCredentialsField, + rotateCredentialsTypeField, + ticketIDField, + ticketTemplatePathField, + logLevelField, +} + +func IsFieldAmongDefaultList(f SchemaField) bool { + for _, v := range DefaultFields { + if v.FieldName == f.FieldName { + return true + } + } + + return false +} + +func EnsureDefaultFieldsExists(originalFields []SchemaField) []SchemaField { + var notfound []SchemaField + + // compare the default list of fields + // with the incoming original list of fields + for _, d := range DefaultFields { + found := false + for _, o := range originalFields { + if d.FieldName == o.FieldName { + found = true + } + } + + if !found { + notfound = append(notfound, d) + } + } + + notfound = append(notfound, originalFields...) + + return notfound +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/field_options.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/field_options.go new file mode 100644 index 0000000..7177ee0 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/field_options.go @@ -0,0 +1,42 @@ +package field + +type fieldOption func(SchemaField) SchemaField + +func WithRequired(required bool) fieldOption { + return func(o SchemaField) SchemaField { + o.Required = required + return o + } +} + +func WithDescription(description string) fieldOption { + return func(o SchemaField) SchemaField { + o.Description = description + + return o + } +} + +func WithDefaultValue(value any) fieldOption { + return func(o SchemaField) SchemaField { + o.DefaultValue = value + + return o + } +} + +func WithHidden(hidden bool) fieldOption { + return func(o SchemaField) SchemaField { + o.Hidden = hidden + + return o + } +} + +func WithShortHand(sh string) fieldOption { + return func(o SchemaField) SchemaField { + o.CLIShortHand = sh + + return o + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/fields.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/fields.go new file mode 100644 index 0000000..7b08deb --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/fields.go @@ -0,0 +1,149 @@ +package field + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +var ( + WrongValueTypeErr = errors.New("unable to cast any to concrete type") +) + +type SchemaField struct { + FieldName string + FieldType reflect.Kind + CLIShortHand string + Required bool + Hidden bool + Description string + DefaultValue any +} + +// Bool returns the default value as a boolean. +func (s SchemaField) Bool() (bool, error) { + value, ok := s.DefaultValue.(bool) + if !ok { + return false, WrongValueTypeErr + } + + return value, nil +} + +// Int returns the default value as a integer. +func (s SchemaField) Int() (int, error) { + value, ok := s.DefaultValue.(int) + if !ok { + return 0, WrongValueTypeErr + } + + return value, nil +} + +// String returns the default value as a string. +func (s SchemaField) String() (string, error) { + value, ok := s.DefaultValue.(string) + if !ok { + return "", WrongValueTypeErr + } + + return value, nil +} + +// StringArray retuns the default value as a string array. +func (s SchemaField) StringArray() ([]string, error) { + value, ok := s.DefaultValue.([]string) + if !ok { + return nil, WrongValueTypeErr + } + + return value, nil +} + +func (s SchemaField) GetDescription() string { + var line string + if s.Description == "" { + line = fmt.Sprintf("($BATON_%s)", toUpperCase(s.FieldName)) + } else { + line = fmt.Sprintf("%s ($BATON_%s)", s.Description, toUpperCase(s.FieldName)) + } + + if s.Required { + line = fmt.Sprintf("required: %s", line) + } + + return line +} + +func (s SchemaField) GetName() string { + return s.FieldName +} + +func (s SchemaField) GetType() reflect.Kind { + return s.FieldType +} + +func BoolField(name string, optional ...fieldOption) SchemaField { + field := SchemaField{ + FieldName: name, + FieldType: reflect.Bool, + DefaultValue: false, + } + + for _, o := range optional { + field = o(field) + } + + if field.Required { + panic(fmt.Sprintf("requiring %s of type %s does not make sense", field.FieldName, field.FieldType)) + } + + return field +} + +func StringField(name string, optional ...fieldOption) SchemaField { + field := SchemaField{ + FieldName: name, + FieldType: reflect.String, + DefaultValue: "", + } + + for _, o := range optional { + field = o(field) + } + + return field +} + +func IntField(name string, optional ...fieldOption) SchemaField { + field := SchemaField{ + FieldName: name, + FieldType: reflect.Int, + DefaultValue: 0, + } + + for _, o := range optional { + field = o(field) + } + + return field +} + +func StringArrayField(name string, optional ...fieldOption) SchemaField { + field := SchemaField{ + FieldName: name, + FieldType: reflect.Slice, + DefaultValue: []string{}, + } + + for _, o := range optional { + field = o(field) + } + + return field +} + +func toUpperCase(i string) string { + return strings.ReplaceAll(strings.ToUpper(i), "-", "_") +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/relationships.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/relationships.go new file mode 100644 index 0000000..3ba0db7 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/relationships.go @@ -0,0 +1,35 @@ +package field + +type Relationship int + +const ( + RequiredTogether Relationship = iota + 1 + MutuallyExclusive + AtLeastOne +) + +type SchemaFieldRelationship struct { + Kind Relationship + Fields []SchemaField +} + +func FieldsRequiredTogether(fields ...SchemaField) SchemaFieldRelationship { + return SchemaFieldRelationship{ + Kind: RequiredTogether, + Fields: fields, + } +} + +func FieldsMutuallyExclusive(fields ...SchemaField) SchemaFieldRelationship { + return SchemaFieldRelationship{ + Kind: MutuallyExclusive, + Fields: fields, + } +} + +func FieldsAtLeastOneUsed(fields ...SchemaField) SchemaFieldRelationship { + return SchemaFieldRelationship{ + Kind: AtLeastOne, + Fields: fields, + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/struct.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/struct.go new file mode 100644 index 0000000..12ecf64 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/struct.go @@ -0,0 +1,13 @@ +package field + +type Configuration struct { + Fields []SchemaField + Constraints []SchemaFieldRelationship +} + +func NewConfiguration(fields []SchemaField, constraints ...SchemaFieldRelationship) Configuration { + return Configuration{ + Fields: fields, + Constraints: constraints, + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/validation.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/validation.go new file mode 100644 index 0000000..244969f --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/validation.go @@ -0,0 +1,124 @@ +package field + +import ( + "fmt" + "reflect" + "strings" + + "github.com/spf13/viper" +) + +type ErrConfigurationMissingFields struct { + errors []error +} + +func (e *ErrConfigurationMissingFields) Error() string { + var messages []string + + for _, err := range e.errors { + messages = append(messages, err.Error()) + } + + return fmt.Sprintf("errors found:\n%s", strings.Join(messages, "\n")) +} + +func (e *ErrConfigurationMissingFields) Push(err error) { + e.errors = append(e.errors, err) +} + +// Validate perform validation of field requirement and constraints +// relationships after the configuration is read. +// We don't check the following: +// - if required fields are mutually exclusive +// - repeated fields (by name) are defined +// - if sets of fields are mutually exclusive and required +// together at the same time +func Validate(c Configuration, v *viper.Viper) error { + present := make(map[string]int) + missingFieldsError := &ErrConfigurationMissingFields{} + + // check if required fields are present + for _, f := range c.Fields { + isNonZero := false + switch f.FieldType { + case reflect.Bool: + isNonZero = v.GetBool(f.FieldName) + case reflect.Int: + isNonZero = v.GetInt(f.FieldName) != 0 + case reflect.String: + isNonZero = v.GetString(f.FieldName) != "" + case reflect.Slice: + isNonZero = len(v.GetStringSlice(f.FieldName)) == 0 + default: + return fmt.Errorf("field %s has unsupported type %s", f.FieldName, f.FieldType) + } + + if isNonZero { + present[f.FieldName] = 1 + } + + if f.Required && !isNonZero { + missingFieldsError.Push(fmt.Errorf("field %s of type %s is marked as required but it has a zero-value", f.FieldName, f.FieldType)) + } + } + + if len(missingFieldsError.errors) > 0 { + return missingFieldsError + } + + // check constraints + return validateConstraints(present, c.Constraints) +} + +func validateConstraints(fieldsPresent map[string]int, relationships []SchemaFieldRelationship) error { + for _, relationship := range relationships { + var present int + for _, f := range relationship.Fields { + present += fieldsPresent[f.FieldName] + } + if present > 1 && relationship.Kind == MutuallyExclusive { + return makeMutuallyExclusiveError(fieldsPresent, relationship) + } + if present > 0 && present < len(relationship.Fields) && relationship.Kind == RequiredTogether { + return makeNeededTogetherError(fieldsPresent, relationship) + } + if present == 0 && relationship.Kind == AtLeastOne { + return makeAtLeastOneError(fieldsPresent, relationship) + } + } + + return nil +} + +func makeMutuallyExclusiveError(fields map[string]int, relation SchemaFieldRelationship) error { + var found []string + for _, f := range relation.Fields { + if fields[f.FieldName] == 1 { + found = append(found, f.FieldName) + } + } + + return fmt.Errorf("fields marked as mutually exclusive were set: %s", strings.Join(found, ", ")) +} + +func makeNeededTogetherError(fields map[string]int, relation SchemaFieldRelationship) error { + var found []string + for _, f := range relation.Fields { + if fields[f.FieldName] == 0 { + found = append(found, f.FieldName) + } + } + + return fmt.Errorf("fields marked as needed together are missing: %s", strings.Join(found, ", ")) +} + +func makeAtLeastOneError(fields map[string]int, relation SchemaFieldRelationship) error { + var found []string + for _, f := range relation.Fields { + if fields[f.FieldName] == 0 { + found = append(found, f.FieldName) + } + } + + return fmt.Errorf("at least one field was expected, any of: %s", strings.Join(found, ", ")) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/helpers/helpers.go b/vendor/github.com/conductorone/baton-sdk/pkg/helpers/helpers.go index b08f689..b4da5ed 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/helpers/helpers.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/helpers/helpers.go @@ -25,6 +25,57 @@ func SplitFullName(name string) (string, string) { return firstName, lastName } +var limitHeaders = []string{ + "X-Ratelimit-Limit", + "Ratelimit-Limit", + "X-RateLimit-Requests-Limit", // Linear uses a non-standard header +} + +var remainingHeaders = []string{ + "X-Ratelimit-Remaining", + "Ratelimit-Remaining", + "X-RateLimit-Requests-Remaining", // Linear uses a non-standard header +} + +var resetAtHeaders = []string{ + "X-Ratelimit-Reset", + "Ratelimit-Reset", + "X-RateLimit-Requests-Reset", // Linear uses a non-standard header + "Retry-After", // Often returned with 429 +} + +const thirtyYears = 60 * 60 * 24 * 365 * (2000 - 1970) + +// Many APIs don't follow standards and return incorrect datetimes. This function tries to handle those cases. +func parseTime(timeStr string) (time.Time, error) { + var t time.Time + res, err := strconv.ParseInt(timeStr, 10, 64) + if err != nil { + t, err = time.Parse(time.RFC850, timeStr) + if err != nil { + // Datetimes should be RFC850 but some APIs return RFC3339 + t, err = time.Parse(time.RFC3339, timeStr) + } + return t, err + } + + // Times are supposed to be in seconds, but some APIs return milliseconds + if res > thirtyYears*1000 { + res /= 1000 + } + + // Times are supposed to be offsets, but some return absolute seconds since 1970. + if res > thirtyYears { + // If more than 30 years, it's probably an absolute timestamp + t = time.Unix(res, 0) + } else { + // Otherwise, it's a relative timestamp + t = time.Now().Add(time.Second * time.Duration(res)) + } + + return t, nil +} + func ExtractRateLimitData(statusCode int, header *http.Header) (*v2.RateLimitDescription, error) { if header == nil { return nil, nil @@ -34,43 +85,53 @@ func ExtractRateLimitData(statusCode int, header *http.Header) (*v2.RateLimitDes var limit int64 var err error - limitStr := header.Get("X-Ratelimit-Limit") - if limitStr != "" { - limit, err = strconv.ParseInt(limitStr, 10, 64) - if err != nil { - return nil, err + for _, limitHeader := range limitHeaders { + limitStr := header.Get(limitHeader) + if limitStr != "" { + limit, err = strconv.ParseInt(limitStr, 10, 64) + if err != nil { + return nil, err + } + break } } var remaining int64 - remainingStr := header.Get("X-Ratelimit-Remaining") - if remainingStr != "" { - remaining, err = strconv.ParseInt(remainingStr, 10, 64) - if err != nil { - return nil, err - } - if remaining > 0 { - rlstatus = v2.RateLimitDescription_STATUS_OK + for _, remainingHeader := range remainingHeaders { + remainingStr := header.Get(remainingHeader) + if remainingStr != "" { + remaining, err = strconv.ParseInt(remainingStr, 10, 64) + if err != nil { + return nil, err + } + break } } + if remaining > 0 { + rlstatus = v2.RateLimitDescription_STATUS_OK + } var resetAt time.Time - reset := header.Get("X-Ratelimit-Reset") - if reset != "" { - res, err := strconv.ParseInt(reset, 10, 64) - if err != nil { - return nil, err + for _, resetAtHeader := range resetAtHeaders { + resetAtStr := header.Get(resetAtHeader) + if resetAtStr != "" { + resetAt, err = parseTime(resetAtStr) + if err != nil { + return nil, err + } + break } + } - resetAt = time.Now().Add(time.Second * time.Duration(res)) + if statusCode == http.StatusTooManyRequests { + rlstatus = v2.RateLimitDescription_STATUS_OVERLIMIT + remaining = 0 } // If we didn't get any rate limit headers and status code is 429, return some sane defaults - if limit == 0 && remaining == 0 && resetAt.IsZero() && statusCode == http.StatusTooManyRequests { + if remaining == 0 && resetAt.IsZero() && rlstatus == v2.RateLimitDescription_STATUS_OVERLIMIT { limit = 1 - remaining = 0 resetAt = time.Now().Add(time.Second * 60) - rlstatus = v2.RateLimitDescription_STATUS_OVERLIMIT } return &v2.RateLimitDescription{ @@ -103,7 +164,7 @@ func IsXMLContentType(contentType string) bool { normalizedContentType := strings.TrimSpace(strings.ToLower(contentType)) for _, xmlContentType := range xmlContentTypes { - if normalizedContentType == xmlContentType { + if strings.HasPrefix(normalizedContentType, xmlContentType) { return true } } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/logging/logging.go b/vendor/github.com/conductorone/baton-sdk/pkg/logging/logging.go index c31c6ed..50c6fe3 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/logging/logging.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/logging/logging.go @@ -30,6 +30,7 @@ func WithLogFormat(format string) Option { c.Encoding = LogFormatJSON case LogFormatConsole: c.Encoding = LogFormatConsole + c.EncoderConfig = zap.NewDevelopmentEncoderConfig() default: c.Encoding = LogFormatJSON } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/metrics/instrumentor.go b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/instrumentor.go new file mode 100644 index 0000000..52541d4 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/instrumentor.go @@ -0,0 +1,39 @@ +package metrics + +import ( + "context" + "time" + + "github.com/conductorone/baton-sdk/pkg/types/tasks" +) + +const ( + taskSuccessCounterName = "baton_sdk.task_success" + taskFailureCounterName = "baton_sdk.task_failure" + taskDurationHistoName = "baton_sdk.task_latency" + taskSuccessCounterDesc = "number of successful tasks by task type" + taskFailureCounterDesc = "number of failed tasks by task type" + taskDurationHistoDesc = "duration of all tasks by task type and status" +) + +type M struct { + underlying Handler +} + +func (m *M) RecordTaskSuccess(ctx context.Context, task tasks.TaskType, dur time.Duration) { + c := m.underlying.Int64Counter(taskSuccessCounterName, taskSuccessCounterDesc, Dimensionless) + h := m.underlying.Int64Histogram(taskDurationHistoName, taskDurationHistoDesc, Milliseconds) + c.Add(ctx, 1, map[string]string{"task_type": task.String()}) + h.Record(ctx, dur.Milliseconds(), map[string]string{"task_type": task.String(), "task_status": "success"}) +} + +func (m *M) RecordTaskFailure(ctx context.Context, task tasks.TaskType, dur time.Duration) { + c := m.underlying.Int64Counter(taskFailureCounterName, taskFailureCounterDesc, Dimensionless) + h := m.underlying.Int64Histogram(taskDurationHistoName, taskDurationHistoDesc, Milliseconds) + c.Add(ctx, 1, map[string]string{"task_type": task.String()}) + h.Record(ctx, dur.Milliseconds(), map[string]string{"task_type": task.String(), "task_status": "failure"}) +} + +func New(handler Handler) *M { + return &M{underlying: handler} +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/metrics/metrics.go b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/metrics.go new file mode 100644 index 0000000..7af8bcd --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/metrics.go @@ -0,0 +1,32 @@ +package metrics + +import ( + "context" +) + +type Handler interface { + Int64Counter(name string, description string, unit Unit) Int64Counter + Int64Gauge(name string, description string, unit Unit) Int64Gauge + Int64Histogram(name string, description string, unit Unit) Int64Histogram + WithTags(tags map[string]string) Handler +} + +type Int64Counter interface { + Add(ctx context.Context, value int64, tags map[string]string) +} + +type Int64Histogram interface { + Record(ctx context.Context, value int64, tags map[string]string) +} + +type Int64Gauge interface { + Observe(ctx context.Context, value int64, tags map[string]string) +} + +type Unit string + +const ( + Dimensionless Unit = "1" + Bytes Unit = "By" + Milliseconds Unit = "ms" +) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/metrics/noop.go b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/noop.go new file mode 100644 index 0000000..eec13f8 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/noop.go @@ -0,0 +1,39 @@ +package metrics + +import "context" + +type noopRecorder struct{} + +func (*noopRecorder) Record(_ context.Context, _ int64, _ map[string]string) {} + +func (*noopRecorder) Add(_ context.Context, _ int64, _ map[string]string) {} + +func (*noopRecorder) Observe(_ context.Context, _ int64, _ map[string]string) {} + +var _ Int64Counter = (*noopRecorder)(nil) +var _ Int64Histogram = (*noopRecorder)(nil) +var _ Int64Gauge = (*noopRecorder)(nil) + +type noopHandler struct{} + +func (*noopHandler) Int64Counter(_ string, _ string, _ Unit) Int64Counter { + return &noopRecorder{} +} + +func (*noopHandler) Int64Gauge(_ string, _ string, _ Unit) Int64Gauge { + return &noopRecorder{} +} + +func (*noopHandler) Int64Histogram(_ string, _ string, _ Unit) Int64Histogram { + return &noopRecorder{} +} + +func (*noopHandler) WithTags(_ map[string]string) Handler { + return &noopHandler{} +} + +var _ Handler = (*noopHandler)(nil) + +func NewNoOpHandler(_ context.Context) Handler { + return &noopHandler{} +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/metrics/otel.go b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/otel.go new file mode 100644 index 0000000..9a43d79 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/metrics/otel.go @@ -0,0 +1,183 @@ +package metrics + +import ( + "context" + "strings" + "sync" + + "go.opentelemetry.io/otel/attribute" + otelmetric "go.opentelemetry.io/otel/metric" +) + +var ( + _ Handler = (*otelHandler)(nil) + _ Int64Counter = (*otelInt64Counter)(nil) + _ Int64Gauge = (*otelInt64Gauge)(nil) + _ Int64Histogram = (*otelInt64Histogram)(nil) +) + +type otelHandler struct { + name string + meter otelmetric.Meter + provider otelmetric.MeterProvider + defaultAttrs *[]attribute.KeyValue + + int64CountersMtx sync.Mutex + int64Counters map[string]*otelInt64Counter + int64HistosMtx sync.Mutex + int64Histos map[string]*otelInt64Histogram + int64GaugesMtx sync.Mutex + int64Gauges map[string]*otelInt64Gauge +} + +type baseAttrs struct { + defaultAttrs *[]attribute.KeyValue +} + +func (a *baseAttrs) getAttributes(tags map[string]string) []attribute.KeyValue { + attrs := makeAttrs(tags) + if a.defaultAttrs != nil { + attrs = append(attrs, *a.defaultAttrs...) + } + + return attrs +} + +func (a *baseAttrs) setDefaultAttrs(attrs *[]attribute.KeyValue) { + a.defaultAttrs = attrs +} + +type otelInt64Counter struct { + *baseAttrs + counter otelmetric.Int64Counter +} + +func (c *otelInt64Counter) Add(ctx context.Context, value int64, tags map[string]string) { + attrs := c.getAttributes(tags) + + c.counter.Add(ctx, value, otelmetric.WithAttributes(attrs...)) +} + +type otelInt64Histogram struct { + *baseAttrs + histo otelmetric.Int64Histogram +} + +func (h *otelInt64Histogram) Record(ctx context.Context, value int64, tags map[string]string) { + attrs := h.getAttributes(tags) + + h.histo.Record(ctx, value, otelmetric.WithAttributes(attrs...)) +} + +type otelInt64Gauge struct { + *baseAttrs + value int64 + attrs []attribute.KeyValue + gauge otelmetric.Int64ObservableGauge +} + +func (g *otelInt64Gauge) Observe(_ context.Context, value int64, tags map[string]string) { + g.attrs = g.getAttributes(tags) + g.value = value +} + +func (h *otelHandler) Int64Histogram(name string, description string, unit Unit) Int64Histogram { + h.int64HistosMtx.Lock() + defer h.int64HistosMtx.Unlock() + + name = strings.ToLower(name) + + c, ok := h.int64Histos[name] + if !ok { + histo, err := h.meter.Int64Histogram(name, otelmetric.WithDescription(description), otelmetric.WithUnit(string(unit))) + if err != nil { + panic(err) + } + c = &otelInt64Histogram{histo: histo, baseAttrs: &baseAttrs{}} + h.int64Histos[name] = c + } + + c.setDefaultAttrs(h.defaultAttrs) + + return c +} + +func (h *otelHandler) Int64Counter(name string, description string, unit Unit) Int64Counter { + h.int64CountersMtx.Lock() + defer h.int64CountersMtx.Unlock() + + name = strings.ToLower(name) + + c, ok := h.int64Counters[name] + if !ok { + counter, err := h.meter.Int64Counter(name, otelmetric.WithDescription(description), otelmetric.WithUnit(string(unit))) + if err != nil { + panic(err) + } + c = &otelInt64Counter{counter: counter, baseAttrs: &baseAttrs{}} + h.int64Counters[name] = c + } + + c.setDefaultAttrs(h.defaultAttrs) + + return c +} + +func (h *otelHandler) Int64Gauge(name string, description string, unit Unit) Int64Gauge { + h.int64GaugesMtx.Lock() + defer h.int64GaugesMtx.Unlock() + + name = strings.ToLower(name) + + c, ok := h.int64Gauges[name] + if !ok { + gauge, err := h.meter.Int64ObservableGauge(name, otelmetric.WithDescription(description), otelmetric.WithUnit(string(unit))) + if err != nil { + panic(err) + } + + c = &otelInt64Gauge{gauge: gauge, baseAttrs: &baseAttrs{}} + + _, err = h.meter.RegisterCallback(func(ctx context.Context, observer otelmetric.Observer) error { + observer.ObserveInt64(c.gauge, c.value, otelmetric.WithAttributes(c.attrs...)) + return nil + }, c.gauge) + if err != nil { + panic(err) + } + + h.int64Gauges[name] = c + } + + c.setDefaultAttrs(h.defaultAttrs) + + return c +} + +func (h *otelHandler) WithTags(tags map[string]string) Handler { + attrs := makeAttrs(tags) + + h.defaultAttrs = &attrs + + return h +} + +func makeAttrs(tags map[string]string) []attribute.KeyValue { + attrs := make([]attribute.KeyValue, 0, len(tags)) + for k, v := range tags { + attrs = append(attrs, attribute.String(k, v)) + } + + return attrs +} + +func NewOtelHandler(_ context.Context, provider otelmetric.MeterProvider, name string) Handler { + return &otelHandler{ + name: name, + meter: provider.Meter(name), + provider: provider, + int64Counters: make(map[string]*otelInt64Counter), + int64Histos: make(map[string]*otelInt64Histogram), + int64Gauges: make(map[string]*otelInt64Gauge), + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand.go deleted file mode 100644 index 6d339c4..0000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand.go +++ /dev/null @@ -1,401 +0,0 @@ -package sync - -import ( - "context" - "errors" - "fmt" - "reflect" - - v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.uber.org/zap" -) - -var ( - ErrNoEntitlement = errors.New("no entitlement found") -) - -type EntitlementGraphAction struct { - SourceEntitlementID string `json:"source_entitlement_id"` - DescendantEntitlementID string `json:"descendant_entitlement_id"` - Shallow bool `json:"shallow"` - ResourceTypeIDs []string `json:"resource_types_ids"` - PageToken string `json:"page_token"` -} - -// Edges between entitlements are grants. -type grantInfo struct { - Expanded bool `json:"expanded"` - Shallow bool `json:"shallow"` - ResourceTypeIDs []string `json:"resource_type_ids"` -} - -type Node struct { - Id int `json:"id"` - EntitlementIDs []string `json:"entitlementIds"` // List of entitlements. -} - -type EntitlementGraph struct { - NodeCount int `json:"node_count"` - Nodes map[int]Node `json:"nodes"` - Edges map[int]map[int]*grantInfo `json:"edges"` // Adjacency list. Source node -> destination node - Loaded bool `json:"loaded"` - Depth int `json:"depth"` - Actions []EntitlementGraphAction `json:"actions"` -} - -func NewEntitlementGraph(ctx context.Context) *EntitlementGraph { - return &EntitlementGraph{ - NodeCount: 1, // Start at 1 in case we don't initialize something and try to get node 0 - Nodes: make(map[int]Node), - // TODO: probably want this for efficiency - // EntitlementsToNodes: make(map[string]int), - Edges: make(map[int]map[int]*grantInfo), - } -} - -func (g *EntitlementGraph) Str() string { - str := "\n" - for id, node := range g.Nodes { - str += fmt.Sprintf("node %v entitlement IDs: %v\n", id, node.EntitlementIDs) - } - str += "edges:\n" - for src, dsts := range g.Edges { - for dst, gi := range dsts { - str += fmt.Sprintf("%v -> %v grantInfo %v\n", src, dst, gi) - } - } - return str -} - -func (g *EntitlementGraph) Validate() error { - for srcNodeId, dstNodeIDs := range g.Edges { - node, ok := g.Nodes[srcNodeId] - if !ok { - return ErrNoEntitlement - } - if len(node.EntitlementIDs) == 0 { - return fmt.Errorf("empty node") - } - for dstNodeId, grantInfo := range dstNodeIDs { - node, ok := g.Nodes[dstNodeId] - if !ok { - return ErrNoEntitlement - } - if len(node.EntitlementIDs) == 0 { - return fmt.Errorf("empty node") - } - if grantInfo == nil { - return fmt.Errorf("nil edge info. dst node %v", dstNodeId) - } - } - } - // check for entitlement ids that are in multiple nodes - seenEntitlements := make(map[string]int) - for nodeID, node := range g.Nodes { - for _, entID := range node.EntitlementIDs { - if _, ok := seenEntitlements[entID]; ok { - return fmt.Errorf("entitlement %v is in multiple nodes: %v %v", entID, nodeID, seenEntitlements[entID]) - } - seenEntitlements[entID] = nodeID - } - } - return nil -} - -func (g *EntitlementGraph) isNodeExpanded(nodeID int) bool { - dstNodeIDs := g.Edges[nodeID] - for _, edgeInfo := range dstNodeIDs { - if !edgeInfo.Expanded { - return false - } - } - return true -} - -// IsExpanded returns true if all entitlements in the graph have been expanded. -func (g *EntitlementGraph) IsExpanded() bool { - for srcNodeID := range g.Edges { - if !g.isNodeExpanded(srcNodeID) { - return false - } - } - return true -} - -// IsEntitlementExpanded returns true if all the outgoing edges for the given entitlement have been expanded. -func (g *EntitlementGraph) IsEntitlementExpanded(entitlementID string) bool { - node := g.GetNode(entitlementID) - if node == nil { - // TODO: log error? return error? - return false - } - if !g.isNodeExpanded(node.Id) { - return false - } - return true -} - -// HasUnexpandedAncestors returns true if the given entitlement has ancestors that have not been expanded yet. -func (g *EntitlementGraph) HasUnexpandedAncestors(entitlementID string) bool { - node := g.GetNode(entitlementID) - if node == nil { - return false - } - - for _, ancestorId := range g.getAncestors(entitlementID) { - if !g.isNodeExpanded(ancestorId) { - return true - } - } - return false -} - -func (g *EntitlementGraph) getAncestors(entitlementID string) []int { - node := g.GetNode(entitlementID) - if node == nil { - panic("entitlement not found") - // return nil - } - - ancestors := make([]int, 0) - for src, dst := range g.Edges { - if _, ok := dst[node.Id]; ok { - ancestors = append(ancestors, src) - } - } - return ancestors -} - -// Find the direct ancestors of the given entitlement. The 'all' flag returns all ancestors regardless of 'done' state. -func (g *EntitlementGraph) GetCycles() ([][]int, bool) { - rv := make([][]int, 0) - for nodeID := range g.Nodes { - edges, ok := g.Edges[nodeID] - if !ok || len(edges) == 0 { - continue - } - cycle, isCycle := g.getCycle([]int{nodeID}) - if isCycle && !isInCycle(cycle, rv) { - rv = append(rv, cycle) - } - } - - return rv, len(rv) > 0 -} - -func isInCycle(newCycle []int, cycles [][]int) bool { - for _, cycle := range cycles { - if len(cycle) > 0 && reflect.DeepEqual(cycle, newCycle) { - return true - } - } - return false -} - -func shift(arr []int, n int) []int { - for i := 0; i < n; i++ { - arr = append(arr[1:], arr[0]) - } - return arr -} - -func (g *EntitlementGraph) getCycle(visits []int) ([]int, bool) { - if len(visits) == 0 { - return nil, false - } - nodeId := visits[len(visits)-1] - for descendantId := range g.Edges[nodeId] { - tempVisits := make([]int, len(visits)) - copy(tempVisits, visits) - if descendantId == visits[0] { - // shift array so that the smallest element is first - smallestIndex := 0 - for i := range tempVisits { - if tempVisits[i] < tempVisits[smallestIndex] { - smallestIndex = i - } - } - tempVisits = shift(tempVisits, smallestIndex) - return tempVisits, true - } - for _, visit := range visits { - if visit == descendantId { - return nil, false - } - } - - tempVisits = append(tempVisits, descendantId) - return g.getCycle(tempVisits) - } - return nil, false -} - -func (g *EntitlementGraph) GetDescendantEntitlements(entitlementID string) map[string]*grantInfo { - node := g.GetNode(entitlementID) - if node == nil { - return nil - } - entsToGrants := make(map[string]*grantInfo) - for dstNodeId, edgeInfo := range g.Edges[node.Id] { - dstNode := g.Nodes[dstNodeId] - for _, entId := range dstNode.EntitlementIDs { - entsToGrants[entId] = edgeInfo - } - } - return entsToGrants -} - -func (g *EntitlementGraph) HasEntitlement(entitlementID string) bool { - return g.GetNode(entitlementID) != nil -} - -func (g *EntitlementGraph) AddEntitlement(entitlement *v2.Entitlement) { - node := g.GetNode(entitlement.Id) - if node != nil { - return - } - - g.Nodes[g.NodeCount] = Node{ - Id: g.NodeCount, - EntitlementIDs: []string{entitlement.Id}, - } - g.NodeCount++ -} - -func (g *EntitlementGraph) GetEntitlements() []string { - var entitlements []string - for _, node := range g.Nodes { - entitlements = append(entitlements, node.EntitlementIDs...) - } - return entitlements -} - -func (g *EntitlementGraph) MarkEdgeExpanded(sourceEntitlementID string, descendantEntitlementID string) { - srcNode := g.GetNode(sourceEntitlementID) - if srcNode == nil { - // TODO: panic? - return - } - dstNode := g.GetNode(descendantEntitlementID) - if dstNode == nil { - // TODO: panic? - return - } - _, ok := g.Edges[srcNode.Id][dstNode.Id] - if !ok { - return - } - - g.Edges[srcNode.Id][dstNode.Id].Expanded = true -} - -func (g *EntitlementGraph) GetNode(entitlementId string) *Node { - // TODO: add an EntitlementToNode map for efficiency - for _, node := range g.Nodes { - for _, entId := range node.EntitlementIDs { - if entId == entitlementId { - return &node - } - } - } - return nil -} - -func (g *EntitlementGraph) AddEdge(ctx context.Context, srcEntitlementID string, dstEntitlementID string, shallow bool, resourceTypeIDs []string) error { - srcNode := g.GetNode(srcEntitlementID) - if srcNode == nil { - return ErrNoEntitlement - } - dstNode := g.GetNode(dstEntitlementID) - if dstNode == nil { - return ErrNoEntitlement - } - - _, ok := g.Edges[srcNode.Id] - if !ok { - g.Edges[srcNode.Id] = make(map[int]*grantInfo) - } - - _, ok = g.Edges[srcNode.Id][dstNode.Id] - if !ok { - g.Edges[srcNode.Id][dstNode.Id] = &grantInfo{ - Expanded: false, - Shallow: shallow, - ResourceTypeIDs: resourceTypeIDs, - } - } else { - // TODO: just do nothing? it's probably a mistake if we're adding the same edge twice - ctxzap.Extract(ctx).Warn( - "duplicate edge from datasource", - zap.String("src_entitlement_id", srcEntitlementID), - zap.String("dst_entitlement_id", dstEntitlementID), - zap.Bool("shallow", shallow), - zap.Strings("resource_type_ids", resourceTypeIDs), - ) - return nil - } - return nil -} - -func (g *EntitlementGraph) removeNode(nodeID int) { - delete(g.Nodes, nodeID) - delete(g.Edges, nodeID) - for id := range g.Edges { - delete(g.Edges[id], nodeID) - } -} - -func (g *EntitlementGraph) mergeNodes(node1ID int, node2ID int) { - node1 := g.Nodes[node1ID] - node2 := g.Nodes[node2ID] - - // Put node's entitlements on first node - node1.EntitlementIDs = append(node1.EntitlementIDs, node2.EntitlementIDs...) - - // TODO: Merge grant info? - - for dstNodeID := range g.Edges[node2ID] { - dstNode := g.Nodes[dstNodeID] - if dstNodeID == node1ID { - continue - } - - // Set outgoing edges on first node - for id := range g.Edges[dstNodeID] { - if id != node1.Id { - g.Edges[node1.Id][id] = g.Edges[dstNode.Id][id] - } - } - } - - // Set incoming edges on first node - for srcID, edges := range g.Edges { - for dstID, gi := range edges { - if dstID == node2ID && srcID != node1ID { - g.Edges[srcID][node1ID] = gi - delete(g.Edges[srcID], dstID) - } - } - } - - // Delete node - g.removeNode(node2ID) -} - -func (g *EntitlementGraph) FixCycles() error { - // If we can't fix the cycles in 10 tries, just give up - const maxTries = 10 - for i := 0; i < maxTries; i++ { - cycles, hasCycles := g.GetCycles() - if !hasCycles { - return nil - } - - // Merge all the nodes in a cycle. - for i := 1; i < len(cycles[0]); i++ { - g.mergeNodes(cycles[0][0], cycles[0][i]) - } - } - return fmt.Errorf("could not fix cycles after %v tries", maxTries) -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/cycle.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/cycle.go new file mode 100644 index 0000000..bea170c --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/cycle.go @@ -0,0 +1,213 @@ +package expand + +import ( + mapset "github.com/deckarep/golang-set/v2" +) + +// GetFirstCycle given an entitlements graph, return a cycle by node ID if it +// exists. Returns nil if no cycle exists. If there is a single +// node pointing to itself, that will count as a cycle. +func (g *EntitlementGraph) GetFirstCycle() []int { + visited := mapset.NewSet[int]() + for nodeID := range g.Nodes { + cycle, hasCycle := g.cycleDetectionHelper(nodeID, visited, []int{}) + if hasCycle { + return cycle + } + } + + return nil +} + +func (g *EntitlementGraph) cycleDetectionHelper( + nodeID int, + visited mapset.Set[int], + currentCycle []int, +) ([]int, bool) { + visited.Add(nodeID) + if destinations, ok := g.SourcesToDestinations[nodeID]; ok { + for destinationID := range destinations { + nextCycle := make([]int, len(currentCycle)) + copy(nextCycle, currentCycle) + nextCycle = append(nextCycle, nodeID) + + if !visited.Contains(destinationID) { + if cycle, hasCycle := g.cycleDetectionHelper(destinationID, visited, nextCycle); hasCycle { + return cycle, true + } + } else { + // Make sure to not include part of the start before the cycle. + outputCycle := make([]int, 0) + for i := len(nextCycle) - 1; i >= 0; i-- { + outputCycle = append(outputCycle, nextCycle[i]) + if nextCycle[i] == destinationID { + return outputCycle, true + } + } + } + } + } + return nil, false +} + +// removeNode obliterates a node and all incoming/outgoing edges. +func (g *EntitlementGraph) removeNode(nodeID int) { + // Delete from reverse mapping. + if node, ok := g.Nodes[nodeID]; ok { + for _, entitlementID := range node.EntitlementIDs { + entNodeId, ok := g.EntitlementsToNodes[entitlementID] + if ok && entNodeId == nodeID { + delete(g.EntitlementsToNodes, entitlementID) + } + } + } + + // Delete from nodes list. + delete(g.Nodes, nodeID) + + // Delete all outgoing edges. + if destinations, ok := g.SourcesToDestinations[nodeID]; ok { + for destinationID, edgeID := range destinations { + delete(g.DestinationsToSources[destinationID], nodeID) + delete(g.Edges, edgeID) + } + } + delete(g.SourcesToDestinations, nodeID) + + // Delete all incoming edges. + if sources, ok := g.DestinationsToSources[nodeID]; ok { + for sourceID, edgeID := range sources { + delete(g.SourcesToDestinations[sourceID], nodeID) + delete(g.Edges, edgeID) + } + } + delete(g.SourcesToDestinations, nodeID) +} + +// FixCycles if any cycles of nodes exist, merge all nodes in that cycle into a +// single node and then repeat. Iteration ends when there are no more cycles. +func (g *EntitlementGraph) FixCycles() error { + cycle := g.GetFirstCycle() + if cycle == nil { + return nil + } + + if err := g.fixCycle(cycle); err != nil { + return err + } + + // Recurse! + return g.FixCycles() +} + +// fixCycle takes a list of Node IDs that form a cycle and merges them into a +// single, new node. +func (g *EntitlementGraph) fixCycle(nodeIDs []int) error { + entitlementIDs := mapset.NewSet[string]() + outgoingEdgesToResourceTypeIDs := map[int]mapset.Set[string]{} + incomingEdgesToResourceTypeIDs := map[int]mapset.Set[string]{} + for _, nodeID := range nodeIDs { + if node, ok := g.Nodes[nodeID]; ok { + // Gather entitlements. + for _, entitlementID := range node.EntitlementIDs { + entitlementIDs.Add(entitlementID) + } + + // Gather all incoming edges. + if sources, ok := g.DestinationsToSources[nodeID]; ok { + for sourceNodeID, edgeID := range sources { + if edge, ok := g.Edges[edgeID]; ok { + resourceTypeIDs, ok := incomingEdgesToResourceTypeIDs[sourceNodeID] + if !ok { + resourceTypeIDs = mapset.NewSet[string]() + } + for _, resourceTypeID := range edge.ResourceTypeIDs { + resourceTypeIDs.Add(resourceTypeID) + } + incomingEdgesToResourceTypeIDs[sourceNodeID] = resourceTypeIDs + } + } + } + + // Gather all outgoing edges. + if destinations, ok := g.SourcesToDestinations[nodeID]; ok { + for destinationNodeID, edgeID := range destinations { + if edge, ok := g.Edges[edgeID]; ok { + resourceTypeIDs, ok := outgoingEdgesToResourceTypeIDs[destinationNodeID] + if !ok { + resourceTypeIDs = mapset.NewSet[string]() + } + for _, resourceTypeID := range edge.ResourceTypeIDs { + resourceTypeIDs.Add(resourceTypeID) + } + outgoingEdgesToResourceTypeIDs[destinationNodeID] = resourceTypeIDs + } + } + } + } + } + + // Create a new node with the entitlements. + g.NextNodeID++ + node := Node{ + Id: g.NextNodeID, + EntitlementIDs: entitlementIDs.ToSlice(), + } + g.Nodes[node.Id] = node + for entitlementID := range entitlementIDs.Iter() { + // Break the old connections and point to this node. + g.EntitlementsToNodes[entitlementID] = node.Id + } + + // Hook up edges + for destinationID, resourceTypeIDs := range outgoingEdgesToResourceTypeIDs { + g.NextEdgeID++ + edge := Edge{ + EdgeID: g.NextEdgeID, + SourceID: node.Id, + DestinationID: destinationID, + IsExpanded: false, + IsShallow: false, + ResourceTypeIDs: resourceTypeIDs.ToSlice(), + } + g.Edges[edge.EdgeID] = edge + if _, ok := g.SourcesToDestinations[node.Id]; !ok { + g.SourcesToDestinations[node.Id] = make(map[int]int) + } + g.SourcesToDestinations[node.Id][destinationID] = edge.EdgeID + if _, ok := g.DestinationsToSources[destinationID]; !ok { + g.DestinationsToSources[destinationID] = make(map[int]int) + } + g.DestinationsToSources[destinationID][node.Id] = edge.EdgeID + } + for sourceID, resourceTypeIDs := range incomingEdgesToResourceTypeIDs { + g.NextEdgeID++ + edge := Edge{ + EdgeID: g.NextEdgeID, + SourceID: sourceID, + DestinationID: node.Id, + IsExpanded: false, + IsShallow: false, + ResourceTypeIDs: resourceTypeIDs.ToSlice(), + } + g.Edges[edge.EdgeID] = edge + + if _, ok := g.SourcesToDestinations[sourceID]; !ok { + g.SourcesToDestinations[sourceID] = make(map[int]int) + } + g.SourcesToDestinations[sourceID][node.Id] = edge.EdgeID + + if _, ok := g.DestinationsToSources[node.Id]; !ok { + g.DestinationsToSources[node.Id] = make(map[int]int) + } + g.DestinationsToSources[node.Id][sourceID] = edge.EdgeID + } + + // Call delete to delete the node and every associated edge. This will + // conveniently delete all edges that were internal to the cycle. + for _, nodeID := range nodeIDs { + g.removeNode(nodeID) + } + + return nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go new file mode 100644 index 0000000..dbdce81 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go @@ -0,0 +1,283 @@ +package expand + +import ( + "context" + + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "go.uber.org/zap" +) + +type EntitlementGraphAction struct { + SourceEntitlementID string `json:"source_entitlement_id"` + DescendantEntitlementID string `json:"descendant_entitlement_id"` + Shallow bool `json:"shallow"` + ResourceTypeIDs []string `json:"resource_types_ids"` + PageToken string `json:"page_token"` +} + +type Edge struct { + EdgeID int `json:"edge_id"` + SourceID int `json:"source_id"` + DestinationID int `json:"destination_id"` + IsExpanded bool `json:"expanded"` + IsShallow bool `json:"shallow"` + ResourceTypeIDs []string `json:"resource_type_ids"` +} + +// Node represents a list of entitlements. It is the base element of the graph. +type Node struct { + Id int `json:"id"` + EntitlementIDs []string `json:"entitlementIds"` // List of entitlements. +} + +// EntitlementGraph - a directed graph representing the relationships between +// entitlements and grants. This data structure is naïve to any business logic. +// Note that the data of each Node is actually a list or IDs, not a single ID. +// This is because the graph can have cycles, and we address them by reducing +// _all_ nodes in a cycle into a single node. +type EntitlementGraph struct { + NextNodeID int `json:"next_node_id"` // Automatically incremented so that each node has a unique ID. + NextEdgeID int `json:"next_edge_id"` // Automatically incremented so that each edge has a unique ID. + Nodes map[int]Node `json:"nodes"` // The mapping of all node IDs to nodes. + EntitlementsToNodes map[string]int `json:"entitlements_to_nodes"` // Internal mapping of entitlements to nodes for quicker lookup. + SourcesToDestinations map[int]map[int]int `json:"sources_to_destinations"` // Internal mapping of outgoing edges by node ID. + DestinationsToSources map[int]map[int]int `json:"destinations_to_sources"` // Internal mapping of incoming edges by node ID. + Edges map[int]Edge `json:"edges"` // Adjacency list. Source node -> descendant node + Loaded bool `json:"loaded"` + Depth int `json:"depth"` + Actions []EntitlementGraphAction `json:"actions"` +} + +func NewEntitlementGraph(_ context.Context) *EntitlementGraph { + return &EntitlementGraph{ + DestinationsToSources: make(map[int]map[int]int), + Edges: make(map[int]Edge), + EntitlementsToNodes: make(map[string]int), + Nodes: make(map[int]Node), + SourcesToDestinations: make(map[int]map[int]int), + } +} + +// isNodeExpanded - is every outgoing edge from this node Expanded? +func (g *EntitlementGraph) isNodeExpanded(nodeID int) bool { + for _, edgeID := range g.SourcesToDestinations[nodeID] { + if edge, ok := g.Edges[edgeID]; ok { + if !edge.IsExpanded { + return false + } + } + } + return true +} + +// IsExpanded returns true if all entitlements in the graph have been expanded. +func (g *EntitlementGraph) IsExpanded() bool { + for _, edge := range g.Edges { + if !edge.IsExpanded { + return false + } + } + return true +} + +// IsEntitlementExpanded returns true if all the outgoing edges for the given entitlement have been expanded. +func (g *EntitlementGraph) IsEntitlementExpanded(entitlementID string) bool { + node := g.GetNode(entitlementID) + if node == nil { + // TODO: log error? return error? + return false + } + return g.isNodeExpanded(node.Id) +} + +// HasUnexpandedAncestors returns true if the given entitlement has ancestors that have not been expanded yet. +func (g *EntitlementGraph) HasUnexpandedAncestors(entitlementID string) bool { + node := g.GetNode(entitlementID) + if node == nil { + return false + } + + for _, ancestorId := range g.getParents(entitlementID) { + if !g.isNodeExpanded(ancestorId) { + return true + } + } + return false +} + +// getParents gets _all_ IDs of nodes that have this entitlement as a child. +func (g *EntitlementGraph) getParents(entitlementID string) []int { + node := g.GetNode(entitlementID) + if node == nil { + panic("entitlement not found") + } + + parents := make([]int, 0) + if destinations, ok := g.DestinationsToSources[node.Id]; ok { + for id := range destinations { + parents = append(parents, id) + } + } + return parents +} + +// GetDescendantEntitlements given an entitlementID, return a mapping of child +// entitlementIDs to edge data. +func (g *EntitlementGraph) GetDescendantEntitlements(entitlementID string) map[string]*Edge { + node := g.GetNode(entitlementID) + if node == nil { + return nil + } + entitlementsToEdges := make(map[string]*Edge) + if destinations, ok := g.SourcesToDestinations[node.Id]; ok { + for destinationID, edgeID := range destinations { + if destination, ok := g.Nodes[destinationID]; ok { + for _, entitlementID := range destination.EntitlementIDs { + if edge, ok := g.Edges[edgeID]; ok { + entitlementsToEdges[entitlementID] = &edge + } + } + } + } + } + return entitlementsToEdges +} + +func (g *EntitlementGraph) HasEntitlement(entitlementID string) bool { + return g.GetNode(entitlementID) != nil +} + +// AddEntitlement - add an entitlement's ID as an unconnected node in the graph. +func (g *EntitlementGraph) AddEntitlement(entitlement *v2.Entitlement) { + // If the entitlement is already in the graph, fail silently. + found := g.GetNode(entitlement.Id) + if found != nil { + return + } + + // Start at 1 in case we don't initialize something and try to get node 0. + g.NextNodeID++ + + // Create a new node. + node := Node{ + Id: g.NextNodeID, + EntitlementIDs: []string{entitlement.Id}, + } + + // Add the node to the data structures. + g.Nodes[node.Id] = node + g.EntitlementsToNodes[entitlement.Id] = node.Id +} + +// GetEntitlements returns a combined list of _all_ entitlements from all nodes. +func (g *EntitlementGraph) GetEntitlements() []string { + var entitlements []string + for _, node := range g.Nodes { + entitlements = append(entitlements, node.EntitlementIDs...) + } + return entitlements +} + +// MarkEdgeExpanded given source and destination entitlements, mark the edge +// between them as "expanded". +func (g *EntitlementGraph) MarkEdgeExpanded(sourceEntitlementID string, descendantEntitlementID string) { + srcNode := g.GetNode(sourceEntitlementID) + if srcNode == nil { + // TODO: panic? + return + } + dstNode := g.GetNode(descendantEntitlementID) + if dstNode == nil { + // TODO: panic? + return + } + + if destinations, ok := g.SourcesToDestinations[srcNode.Id]; ok { + if edgeID, ok := destinations[dstNode.Id]; ok { + if edge, ok := g.Edges[edgeID]; ok { + edge.IsExpanded = true + g.Edges[edgeID] = edge + } + } + } +} + +// GetNode - returns the node that contains the given `entitlementID`. +func (g *EntitlementGraph) GetNode(entitlementID string) *Node { + nodeID, ok := g.EntitlementsToNodes[entitlementID] + if !ok { + return nil + } + node, ok := g.Nodes[nodeID] + if !ok { + return nil + } + return &node +} + +// AddEdge - given two entitlements, add an edge with resourceTypeIDs. +func (g *EntitlementGraph) AddEdge( + ctx context.Context, + srcEntitlementID string, + dstEntitlementID string, + isShallow bool, + resourceTypeIDs []string, +) error { + srcNode := g.GetNode(srcEntitlementID) + if srcNode == nil { + return ErrNoEntitlement + } + dstNode := g.GetNode(dstEntitlementID) + if dstNode == nil { + return ErrNoEntitlement + } + + if destinations, ok := g.SourcesToDestinations[srcNode.Id]; ok { + if _, ok = destinations[dstNode.Id]; ok { + // TODO: just do nothing? it's probably a mistake if we're adding the same edge twice + ctxzap.Extract(ctx).Warn( + "duplicate edge from datasource", + zap.String("src_entitlement_id", srcEntitlementID), + zap.String("dst_entitlement_id", dstEntitlementID), + zap.Bool("shallow", isShallow), + zap.Strings("resource_type_ids", resourceTypeIDs), + ) + return nil + } + } else { + g.SourcesToDestinations[srcNode.Id] = make(map[int]int) + } + + if sources, ok := g.DestinationsToSources[dstNode.Id]; ok { + if _, ok = sources[srcNode.Id]; ok { + // TODO: just do nothing? it's probably a mistake if we're adding the same edge twice + ctxzap.Extract(ctx).Warn( + "duplicate edge from datasource", + zap.String("src_entitlement_id", srcEntitlementID), + zap.String("dst_entitlement_id", dstEntitlementID), + zap.Bool("shallow", isShallow), + zap.Strings("resource_type_ids", resourceTypeIDs), + ) + return nil + } + } else { + g.DestinationsToSources[dstNode.Id] = make(map[int]int) + } + + // Start at 1 in case we don't initialize something and try to get edge 0. + g.NextEdgeID++ + edge := Edge{ + EdgeID: g.NextEdgeID, + SourceID: srcNode.Id, + DestinationID: dstNode.Id, + IsExpanded: false, + IsShallow: isShallow, + ResourceTypeIDs: resourceTypeIDs, + } + + g.Edges[g.NextEdgeID] = edge + g.SourcesToDestinations[srcNode.Id][dstNode.Id] = edge.EdgeID + g.DestinationsToSources[dstNode.Id][srcNode.Id] = edge.EdgeID + return nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/validate.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/validate.go new file mode 100644 index 0000000..fdb94e8 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/validate.go @@ -0,0 +1,134 @@ +package expand + +import ( + "errors" + "fmt" + "slices" + "strings" +) + +var ( + ErrNoEntitlement = errors.New("no entitlement found") +) + +func (node *Node) Str() string { + return fmt.Sprintf( + "node %v entitlement IDs: %v", + node.Id, + node.EntitlementIDs, + ) +} + +func (edge *Edge) Str() string { + return fmt.Sprintf( + "%v -> %v { expanded: %v, shallow: %v, resources: %v }", + edge.SourceID, + edge.DestinationID, + edge.IsExpanded, + edge.IsShallow, + edge.ResourceTypeIDs, + ) +} + +// Str lists every `node` line by line followed by every `edge`. Useful for debugging. +func (g *EntitlementGraph) Str() string { + nodeHeader := "" + edgeHeader := "edges:" + nodesStrings := make([]string, 0, len(g.Nodes)) + edgeStrings := make([]string, 0, len(g.Edges)) + + for id, node := range g.Nodes { + nodesStrings = append( + nodesStrings, + node.Str(), + ) + if destinationsMap, destinationOK := g.SourcesToDestinations[id]; destinationOK { + for _, edgeID := range destinationsMap { + if edge, edgeOK := g.Edges[edgeID]; edgeOK { + edgeStrings = append( + edgeStrings, + edge.Str(), + ) + } + } + } + } + + return strings.Join( + []string{ + nodeHeader, + strings.Join(nodesStrings, "\n"), + edgeHeader, + strings.Join(edgeStrings, "\n"), + }, + "\n", + ) +} + +// validateEdges validates that for every edge, both nodes actually exists. +func (g *EntitlementGraph) validateEdges() error { + for edgeId, edge := range g.Edges { + if _, ok := g.Nodes[edge.SourceID]; !ok { + return ErrNoEntitlement + } + if _, ok := g.Nodes[edge.DestinationID]; !ok { + return ErrNoEntitlement + } + + if g.SourcesToDestinations[edge.SourceID][edge.DestinationID] != edgeId { + return fmt.Errorf("edge %v does not match source %v to destination %v", edgeId, edge.SourceID, edge.DestinationID) + } + + if g.DestinationsToSources[edge.DestinationID][edge.SourceID] != edgeId { + return fmt.Errorf("edge %v does not match destination %v to source %v", edgeId, edge.DestinationID, edge.SourceID) + } + } + return nil +} + +// validateNodes validates that each node has at least one `entitlementID` and +// that each `entitlementID` only appears once in the graph. +func (g *EntitlementGraph) validateNodes() error { + // check for entitlement ids that are in multiple nodes + seenEntitlements := make(map[string]int) + for nodeID, node := range g.Nodes { + if len(node.EntitlementIDs) == 0 { + return fmt.Errorf("empty node %v", nodeID) + } + for _, entID := range node.EntitlementIDs { + if _, ok := seenEntitlements[entID]; ok { + return fmt.Errorf("entitlement %v is in multiple nodes: %v %v", entID, nodeID, seenEntitlements[entID]) + } + seenEntitlements[entID] = nodeID + entNodeId, ok := g.EntitlementsToNodes[entID] + if !ok { + return fmt.Errorf("entitlement %v is not in EntitlementsToNodes. should be in node %v", entID, nodeID) + } + if entNodeId != nodeID { + return fmt.Errorf("entitlement %v is in node %v but should be in node %v", entID, entNodeId, nodeID) + } + } + } + + for entID, nodeID := range g.EntitlementsToNodes { + node, ok := g.Nodes[nodeID] + if !ok { + return fmt.Errorf("entitlement %v is in EntitlementsToNodes but not in Nodes", entID) + } + if !slices.Contains(node.EntitlementIDs, entID) { + return fmt.Errorf("entitlement %v is in EntitlementsToNodes but not in node %v", entID, nodeID) + } + } + return nil +} + +// Validate checks every node and edge and returns an error if the graph is not valid. +func (g *EntitlementGraph) Validate() error { + if err := g.validateEdges(); err != nil { + return err + } + if err := g.validateNodes(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go index 60674f1..df651a4 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go @@ -6,6 +6,8 @@ import ( "fmt" "sync" + "github.com/conductorone/baton-sdk/pkg/sync/expand" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" ) @@ -16,7 +18,7 @@ type State interface { NextPage(ctx context.Context, pageToken string) error ResourceTypeID(ctx context.Context) string ResourceID(ctx context.Context) string - EntitlementGraph(ctx context.Context) *EntitlementGraph + EntitlementGraph(ctx context.Context) *expand.EntitlementGraph ParentResourceID(ctx context.Context) string ParentResourceTypeID(ctx context.Context) string PageToken(ctx context.Context) string @@ -52,12 +54,12 @@ func (s ActionOp) String() string { } } -// MarshalJSON marshals the ActionOp insto a json string. +// MarshalJSON marshals the ActionOp into a json string. func (s *ActionOp) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } -// UnmarshalJSON unmarshal's the input byte slice and updates this action op. +// UnmarshalJSON unmarshals the input byte slice and updates this action op. func (s *ActionOp) UnmarshalJSON(data []byte) error { var v string err := json.Unmarshal(data, &v) @@ -118,21 +120,21 @@ type state struct { mtx sync.RWMutex actions []Action currentAction *Action - entitlementGraph *EntitlementGraph + entitlementGraph *expand.EntitlementGraph needsExpansion bool } // serializedToken is used to serialize the token to JSON. This separate object is used to avoid having exported fields // on the object used externally. We should interface this, probably. type serializedToken struct { - Actions []Action `json:"actions"` - CurrentAction *Action `json:"current_action"` - NeedsExpansion bool `json:"needs_expansion"` - EntitlementGraph *EntitlementGraph `json:"entitlement_graph"` + Actions []Action `json:"actions"` + CurrentAction *Action `json:"current_action"` + NeedsExpansion bool `json:"needs_expansion"` + EntitlementGraph *expand.EntitlementGraph `json:"entitlement_graph"` } // push adds a new action to the stack. If there is no current state, the action is directly set to current, else -// the current state is appened to the slice of actions, and the new action is set to current. +// the current state is appended to the slice of actions, and the new action is set to current. func (st *state) push(action Action) { st.mtx.Lock() defer st.mtx.Unlock() @@ -291,13 +293,13 @@ func (st *state) ResourceID(ctx context.Context) string { } // EntitlementGraph returns the entitlement graph for the current action. -func (st *state) EntitlementGraph(ctx context.Context) *EntitlementGraph { +func (st *state) EntitlementGraph(ctx context.Context) *expand.EntitlementGraph { c := st.Current() if c == nil { panic("no current state") } if st.entitlementGraph == nil { - st.entitlementGraph = NewEntitlementGraph(ctx) + st.entitlementGraph = expand.NewEntitlementGraph(ctx) } return st.entitlementGraph } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go index fa0958b..fc4febc 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go @@ -11,6 +11,8 @@ import ( "strconv" "time" + "github.com/conductorone/baton-sdk/pkg/sync/expand" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" "google.golang.org/grpc/codes" @@ -80,18 +82,27 @@ func (s *syncer) handleProgress(ctx context.Context, a *Action, c int) { } } +var attempts = 0 + func shouldWaitAndRetry(ctx context.Context, err error) bool { + if err == nil { + attempts = 0 + return true + } if status.Code(err) != codes.Unavailable { return false } + attempts++ l := ctxzap.Extract(ctx) - l.Error("retrying operation", zap.Error(err)) + + var wait time.Duration = time.Duration(attempts) * time.Second + + l.Error("retrying operation", zap.Error(err), zap.Duration("wait", wait)) for { select { - // TODO: this should back off based on error counts - case <-time.After(1 * time.Second): + case <-time.After(wait): return true case <-ctx.Done(): return false @@ -101,8 +112,8 @@ func shouldWaitAndRetry(ctx context.Context, err error) bool { // Sync starts the syncing process. The sync process is driven by the action stack that is part of the state object. // For each page of data that is required to be fetched from the connector, a new action is pushed on to the stack. Once -// an action is completed, it is popped off of the queue. Before procesing each action, we checkpoint the state object -// into the datasouce. This allows for graceful resumes if a sync is interrupted. +// an action is completed, it is popped off of the queue. Before processing each action, we checkpoint the state object +// into the datasource. This allows for graceful resumes if a sync is interrupted. func (s *syncer) Sync(ctx context.Context) error { l := ctxzap.Extract(ctx) @@ -189,28 +200,28 @@ func (s *syncer) Sync(ctx context.Context) error { case SyncResourceTypesOp: err = s.SyncResourceTypes(ctx) - if err != nil && !shouldWaitAndRetry(ctx, err) { + if !shouldWaitAndRetry(ctx, err) { return err } continue case SyncResourcesOp: err = s.SyncResources(ctx) - if err != nil && !shouldWaitAndRetry(ctx, err) { + if !shouldWaitAndRetry(ctx, err) { return err } continue case SyncEntitlementsOp: err = s.SyncEntitlements(ctx) - if err != nil && !shouldWaitAndRetry(ctx, err) { + if !shouldWaitAndRetry(ctx, err) { return err } continue case SyncGrantsOp: err = s.SyncGrants(ctx) - if err != nil && !shouldWaitAndRetry(ctx, err) { + if !shouldWaitAndRetry(ctx, err) { return err } continue @@ -230,7 +241,7 @@ func (s *syncer) Sync(ctx context.Context) error { } err = s.SyncGrantExpansion(ctx) - if err != nil && !shouldWaitAndRetry(ctx, err) { + if !shouldWaitAndRetry(ctx, err) { return err } continue @@ -273,11 +284,9 @@ func (s *syncer) SyncResourceTypes(ctx context.Context) error { return err } - for _, rt := range resp.List { - err = s.store.PutResourceType(ctx, rt) - if err != nil { - return err - } + err = s.store.PutResourceTypes(ctx, resp.List...) + if err != nil { + return err } s.handleProgress(ctx, s.state.Current(), len(resp.List)) @@ -382,6 +391,7 @@ func (s *syncer) syncResources(ctx context.Context) error { } } + bulkPutResoruces := []*v2.Resource{} for _, r := range resp.List { // Check if we've already synced this resource, skip it if we have _, err = s.store.GetResource(ctx, &reader_v2.ResourcesReaderServiceGetResourceRequest{ @@ -403,12 +413,16 @@ func (s *syncer) syncResources(ctx context.Context) error { // Set the resource creation source r.CreationSource = v2.Resource_CREATION_SOURCE_CONNECTOR_LIST_RESOURCES - err = s.store.PutResource(ctx, r) + bulkPutResoruces = append(bulkPutResoruces, r) + + err = s.getSubResources(ctx, r) if err != nil { return err } + } - err = s.getSubResources(ctx, r) + if len(bulkPutResoruces) > 0 { + err = s.store.PutResources(ctx, bulkPutResoruces...) if err != nil { return err } @@ -480,7 +494,7 @@ func (s *syncer) shouldSkipEntitlementsAndGrants(ctx context.Context, r *v2.Reso } // SyncEntitlements fetches the entitlements from the connector. It first lists each resource from the datastore, -// and pushes an action to fetch the entitelments for each resource. +// and pushes an action to fetch the entitlements for each resource. func (s *syncer) SyncEntitlements(ctx context.Context) error { if s.state.ResourceTypeID(ctx) == "" && s.state.ResourceID(ctx) == "" { pageToken := s.state.PageToken(ctx) @@ -548,11 +562,9 @@ func (s *syncer) syncEntitlementsForResource(ctx context.Context, resourceID *v2 if err != nil { return err } - for _, e := range resp.List { - err = s.store.PutEntitlement(ctx, e) - if err != nil { - return err - } + err = s.store.PutEntitlements(ctx, resp.List...) + if err != nil { + return err } s.handleProgress(ctx, s.state.Current(), len(resp.List)) @@ -810,9 +822,13 @@ func (s *syncer) SyncGrantExpansion(ctx context.Context) error { } if entitlementGraph.Loaded { - cycles, hasCycles := entitlementGraph.GetCycles() - if hasCycles { - l.Warn("cycles detected in entitlement graph", zap.Any("cycles", cycles)) + cycle := entitlementGraph.GetFirstCycle() + if cycle != nil { + l.Warn( + "cycle detected in entitlement graph", + zap.Any("cycle", cycle), + zap.Any("initial graph", entitlementGraph), + ) if dontFixCycles { return fmt.Errorf("cycles detected in entitlement graph") } @@ -883,7 +899,7 @@ func (s *syncer) SyncGrants(ctx context.Context) error { return nil } -type lastestSyncFetcher interface { +type latestSyncFetcher interface { LatestFinishedSync(ctx context.Context) (string, error) } @@ -893,7 +909,7 @@ func (s *syncer) fetchResourceForPreviousSync(ctx context.Context, resourceID *v var previousSyncID string var err error - if psf, ok := s.store.(lastestSyncFetcher); ok { + if psf, ok := s.store.(latestSyncFetcher); ok { previousSyncID, err = psf.LatestFinishedSync(ctx) if err != nil { return "", nil, err @@ -1050,11 +1066,10 @@ func (s *syncer) syncGrantsForResource(ctx context.Context, resourceID *v2.Resou if grantAnnos.Contains(&v2.GrantExpandable{}) { s.state.SetNeedsExpansion() } - - err = s.store.PutGrant(ctx, grant) - if err != nil { - return err - } + } + err = s.store.PutGrants(ctx, grants...) + if err != nil { + return err } s.handleProgress(ctx, s.state.Current(), len(grants)) @@ -1080,7 +1095,7 @@ func (s *syncer) syncGrantsForResource(ctx context.Context, resourceID *v2.Resou if updatedETag != nil { resourceAnnos.Update(updatedETag) resource.Annotations = resourceAnnos - err = s.store.PutResource(ctx, resource) + err = s.store.PutResources(ctx, resource) if err != nil { return err } @@ -1246,7 +1261,7 @@ func (s *syncer) runGrantExpandActions(ctx context.Context) (bool, error) { zap.Any("sources", sources), ) - err = s.store.PutGrant(ctx, descendantGrant) + err = s.store.PutGrants(ctx, descendantGrant) if err != nil { l.Error("runGrantExpandActions: error updating descendant grant", zap.Error(err)) return false, fmt.Errorf("runGrantExpandActions: error updating descendant grant: %w", err) @@ -1300,13 +1315,17 @@ func (s *syncer) expandGrantsForEntitlements(ctx context.Context) error { } if graph.Depth > maxDepth { - l.Error("expandGrantsForEntitlements: exceeded max depth", zap.Any("graph", graph), zap.Int("max_depth", maxDepth)) + l.Error( + "expandGrantsForEntitlements: exceeded max depth", + zap.Any("graph", graph), + zap.Int("max_depth", maxDepth), + ) s.state.FinishAction(ctx) return fmt.Errorf("exceeded max depth") } - // TOOD(morgabra) Yield here after some amount of work? - // traverse edges or call some sort of getentitlements + // TODO(morgabra) Yield here after some amount of work? + // traverse edges or call some sort of getEntitlements for _, sourceEntitlementID := range graph.GetEntitlements() { // We've already expanded this entitlement, so skip it. if graph.IsEntitlementExpanded(sourceEntitlementID) { @@ -1320,14 +1339,14 @@ func (s *syncer) expandGrantsForEntitlements(ctx context.Context) error { } for descendantEntitlementID, grantInfo := range graph.GetDescendantEntitlements(sourceEntitlementID) { - if grantInfo.Expanded { + if grantInfo.IsExpanded { continue } - graph.Actions = append(graph.Actions, EntitlementGraphAction{ + graph.Actions = append(graph.Actions, expand.EntitlementGraphAction{ SourceEntitlementID: sourceEntitlementID, DescendantEntitlementID: descendantEntitlementID, PageToken: "", - Shallow: grantInfo.Shallow, + Shallow: grantInfo.IsShallow, ResourceTypeIDs: grantInfo.ResourceTypeIDs, }) } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/create_ticket.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/create_ticket.go index 27e4596..b4af952 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/create_ticket.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/create_ticket.go @@ -29,8 +29,8 @@ func (c *createTicketTaskHandler) HandleTask(ctx context.Context) error { t := c.task.GetCreateTicketTask() if t == nil || t.GetTicketRequest() == nil { - l.Error("create ticket task was nil or missing ticket request", zap.Any("create_resource_task", t)) - return c.helpers.FinishTask(ctx, nil, nil, errors.Join(errors.New("malformed create ticket task"), ErrTaskNonRetryable)) + l.Error("create ticket task was nil or missing ticket request", zap.Any("create_ticket_task", t)) + return c.helpers.FinishTask(ctx, nil, t.GetAnnotations(), errors.Join(errors.New("malformed create ticket task"), ErrTaskNonRetryable)) } cc := c.helpers.ConnectorClient() @@ -41,10 +41,15 @@ func (c *createTicketTaskHandler) HandleTask(ctx context.Context) error { }) if err != nil { l.Error("failed creating ticket", zap.Error(err)) - return c.helpers.FinishTask(ctx, nil, nil, errors.Join(err, ErrTaskNonRetryable)) + return c.helpers.FinishTask(ctx, nil, t.GetAnnotations(), err) } - return c.helpers.FinishTask(ctx, resp, resp.GetAnnotations(), nil) + respAnnos := annotations.Annotations(resp.GetAnnotations()) + respAnnos.Merge(t.GetAnnotations()...) + + resp.Annotations = respAnnos + + return c.helpers.FinishTask(ctx, resp, respAnnos, nil) } func newCreateTicketTaskHandler(task *v1.Task, helpers createTicketTaskHelpers) *createTicketTaskHandler { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/get_ticket.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/get_ticket.go index 09bd185..18ce23e 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/get_ticket.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/get_ticket.go @@ -2,7 +2,7 @@ package c1api import ( "context" - "fmt" + "errors" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" v1 "github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1" @@ -28,24 +28,35 @@ func (c *getTicketTaskHandler) HandleTask(ctx context.Context) error { cc := c.helpers.ConnectorClient() + t := c.task.GetGetTicket() + if t == nil || t.GetTicketId() == "" { + l.Error("get ticket task was nil or missing ticket id", zap.Any("get_ticket_task", t)) + return c.helpers.FinishTask(ctx, nil, nil, errors.Join(errors.New("malformed get ticket task"), ErrTaskNonRetryable)) + } + ticket, err := cc.GetTicket(ctx, &v2.TicketsServiceGetTicketRequest{ - Id: c.task.GetId(), + Id: t.GetTicketId(), }) if err != nil { - return err + return c.helpers.FinishTask(ctx, nil, t.GetAnnotations(), err) } if ticket.GetTicket() == nil { - return fmt.Errorf("connector returned empt ticket schema") + return c.helpers.FinishTask(ctx, nil, t.GetAnnotations(), errors.Join(errors.New("connector returned empty ticket"), ErrTaskNonRetryable)) } resp := &v2.TicketsServiceGetTicketResponse{ Ticket: ticket.GetTicket(), } + respAnnos := annotations.Annotations(resp.GetAnnotations()) + respAnnos.Merge(t.GetAnnotations()...) + + resp.Annotations = respAnnos + l.Debug("GetTicket response", zap.Any("resp", resp)) - return c.helpers.FinishTask(ctx, resp, resp.GetAnnotations(), nil) + return c.helpers.FinishTask(ctx, resp, respAnnos, nil) } func newGetTicketTaskHandler(task *v1.Task, helpers getTicketTaskHelpers) *getTicketTaskHandler { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/list_ticket_schemas.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/list_ticket_schemas.go index a539d1b..d7c5c88 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/list_ticket_schemas.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/list_ticket_schemas.go @@ -60,7 +60,7 @@ func (c *listTicketSchemasTaskHandler) HandleTask(ctx context.Context) error { if err != nil { l.Error("failed listing ticket schemas", zap.Error(err)) - return c.helpers.FinishTask(ctx, nil, nil, errors.Join(err, ErrTaskNonRetryable)) + return c.helpers.FinishTask(ctx, nil, nil, err) } resp := &v2.TicketsServiceListTicketSchemasResponse{ diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/manager.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/manager.go index 8dc532b..e1f2a9f 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/manager.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/c1api/manager.go @@ -20,6 +20,7 @@ import ( v1 "github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1" "github.com/conductorone/baton-sdk/pkg/tasks" "github.com/conductorone/baton-sdk/pkg/types" + taskTypes "github.com/conductorone/baton-sdk/pkg/types/tasks" ) var ( @@ -112,7 +113,7 @@ func (c *c1ApiTaskManager) Next(ctx context.Context) (*v1.Task, time.Duration, e nextPoll := getNextPoll(resp.GetNextPoll().AsDuration()) l = l.With(zap.Duration("next_poll", nextPoll)) - if resp.GetTask() == nil || tasks.Is(resp.GetTask(), tasks.NoneType) { + if resp.GetTask() == nil || tasks.Is(resp.GetTask(), taskTypes.NoneType) { l.Debug("c1_api_task_manager.Next(): no tasks available") return nil, nextPoll, nil } @@ -220,34 +221,34 @@ func (c *c1ApiTaskManager) Process(ctx context.Context, task *v1.Task, cc types. // Handlers may do their work in a goroutine allowing processing to move onto the next task var handler tasks.TaskHandler switch tasks.GetType(task) { - case tasks.FullSyncType: + case taskTypes.FullSyncType: handler = newFullSyncTaskHandler(task, tHelpers) - case tasks.HelloType: + case taskTypes.HelloType: handler = newHelloTaskHandler(task, tHelpers) - case tasks.GrantType: + case taskTypes.GrantType: handler = newGrantTaskHandler(task, tHelpers) - case tasks.RevokeType: + case taskTypes.RevokeType: handler = newRevokeTaskHandler(task, tHelpers) - case tasks.CreateAccountType: + case taskTypes.CreateAccountType: handler = newCreateAccountTaskHandler(task, tHelpers) - case tasks.CreateResourceType: + case taskTypes.CreateResourceType: handler = newCreateResourceTaskHandler(task, tHelpers) - case tasks.DeleteResourceType: + case taskTypes.DeleteResourceType: handler = newDeleteResourceTaskHandler(task, tHelpers) - case tasks.RotateCredentialsType: + case taskTypes.RotateCredentialsType: handler = newRotateCredentialsTaskHandler(task, tHelpers) - case tasks.CreateTicketType: + case taskTypes.CreateTicketType: handler = newCreateTicketTaskHandler(task, tHelpers) - case tasks.ListTicketSchemasType: + case taskTypes.ListTicketSchemasType: handler = newListSchemasTaskHandler(task, tHelpers) - case tasks.GetTicketType: + case taskTypes.GetTicketType: handler = newGetTicketTaskHandler(task, tHelpers) default: return c.finishTask(ctx, task, nil, nil, errors.New("unsupported task type")) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/ticket.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/ticket.go index f0085ce..01d93a7 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/ticket.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/ticket.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/conductorone/baton-sdk/pkg/types/resource" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" @@ -24,13 +25,14 @@ type localCreateTicket struct { } type ticketTemplate struct { - SchemaID string `json:"schema_id"` - StatusId string `json:"status_id"` - TypeId string `json:"type_id"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - Labels []string `json:"labels"` - CustomFields map[string]interface{} `json:"custom_fields"` + SchemaID string `json:"schema_id"` + StatusId string `json:"status_id"` + TypeId string `json:"type_id"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + Labels []string `json:"labels"` + CustomFields map[string]interface{} `json:"custom_fields"` + RequestedForId string `json:"requested_for_id"` } func (m *localCreateTicket) loadTicketTemplate(ctx context.Context) (*ticketTemplate, error) { @@ -78,13 +80,28 @@ func (m *localCreateTicket) Process(ctx context.Context, task *v1.Task, cc types ticketRequestBody := &v2.TicketRequest{ DisplayName: template.DisplayName, Description: template.Description, - Status: &v2.TicketStatus{ - Id: template.StatusId, - }, - Type: &v2.TicketType{ + Labels: template.Labels, + } + + if template.TypeId != "" { + ticketRequestBody.Type = &v2.TicketType{ Id: template.TypeId, - }, - Labels: template.Labels, + } + } + + if template.StatusId != "" { + ticketRequestBody.Status = &v2.TicketStatus{ + Id: template.StatusId, + } + } + + if template.RequestedForId != "" { + rt := resource.NewResourceType("User", []v2.ResourceType_Trait{v2.ResourceType_TRAIT_USER}) + requestedUser, err := resource.NewUserResource(template.RequestedForId, rt, template.RequestedForId, []resource.UserTraitOption{}) + if err != nil { + return err + } + ticketRequestBody.RequestedFor = requestedUser } cfs := make(map[string]*v2.TicketCustomField) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go index 8a60d5c..b2071d2 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go @@ -6,6 +6,7 @@ import ( v1 "github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1" "github.com/conductorone/baton-sdk/pkg/types" + taskTypes "github.com/conductorone/baton-sdk/pkg/types/tasks" ) type Manager interface { @@ -17,59 +18,7 @@ type TaskHandler interface { HandleTask(ctx context.Context) error } -type TaskType uint8 - -func (tt TaskType) String() string { - switch tt { - case FullSyncType: - return "sync_full" - case GrantType: - return "grant" - case RevokeType: - return "revoke" - case HelloType: - return "hello" - case EventFeedType: - return "event_feed" - case NoneType: - return "none" - case CreateAccountType: - return "create_account" - case CreateResourceType: - return "create_resource" - case DeleteResourceType: - return "delete_resource" - case RotateCredentialsType: - return "rotate_credential" - case CreateTicketType: - return "create_ticket" - case ListTicketSchemasType: - return "list_ticket_schemas" - case GetTicketType: - return "get_ticket" - default: - return "unknown" - } -} - -const ( - UnknownType TaskType = iota - NoneType - FullSyncType - GrantType - RevokeType - HelloType - CreateAccountType - CreateResourceType - DeleteResourceType - RotateCredentialsType - EventFeedType - CreateTicketType - ListTicketSchemasType - GetTicketType -) - -func Is(task *v1.Task, target TaskType) bool { +func Is(task *v1.Task, target taskTypes.TaskType) bool { if task == nil || task.TaskType == nil { return false } @@ -77,31 +26,31 @@ func Is(task *v1.Task, target TaskType) bool { var ok bool switch target { - case FullSyncType: + case taskTypes.FullSyncType: _, ok = task.GetTaskType().(*v1.Task_SyncFull) - case GrantType: + case taskTypes.GrantType: _, ok = task.GetTaskType().(*v1.Task_Grant) - case RevokeType: + case taskTypes.RevokeType: _, ok = task.GetTaskType().(*v1.Task_Revoke) - case HelloType: + case taskTypes.HelloType: _, ok = task.GetTaskType().(*v1.Task_Hello) - case EventFeedType: + case taskTypes.EventFeedType: _, ok = task.GetTaskType().(*v1.Task_EventFeed) - case NoneType: + case taskTypes.NoneType: _, ok = task.GetTaskType().(*v1.Task_None) - case CreateAccountType: + case taskTypes.CreateAccountType: _, ok = task.GetTaskType().(*v1.Task_CreateAccount) - case CreateResourceType: + case taskTypes.CreateResourceType: _, ok = task.GetTaskType().(*v1.Task_CreateResource) - case DeleteResourceType: + case taskTypes.DeleteResourceType: _, ok = task.GetTaskType().(*v1.Task_DeleteResource) - case RotateCredentialsType: + case taskTypes.RotateCredentialsType: _, ok = task.GetTaskType().(*v1.Task_RotateCredentials) - case CreateTicketType: + case taskTypes.CreateTicketType: _, ok = task.GetTaskType().(*v1.Task_CreateTicketTask_) - case ListTicketSchemasType: + case taskTypes.ListTicketSchemasType: _, ok = task.GetTaskType().(*v1.Task_ListTicketSchemas) - case GetTicketType: + case taskTypes.GetTicketType: _, ok = task.GetTaskType().(*v1.Task_GetTicket) default: return false @@ -110,39 +59,39 @@ func Is(task *v1.Task, target TaskType) bool { return ok } -func GetType(task *v1.Task) TaskType { +func GetType(task *v1.Task) taskTypes.TaskType { if task == nil || task.TaskType == nil { - return UnknownType + return taskTypes.UnknownType } switch task.GetTaskType().(type) { case *v1.Task_SyncFull: - return FullSyncType + return taskTypes.FullSyncType case *v1.Task_Grant: - return GrantType + return taskTypes.GrantType case *v1.Task_Revoke: - return RevokeType + return taskTypes.RevokeType case *v1.Task_Hello: - return HelloType + return taskTypes.HelloType case *v1.Task_EventFeed: - return EventFeedType + return taskTypes.EventFeedType case *v1.Task_None: - return NoneType + return taskTypes.NoneType case *v1.Task_CreateAccount: - return CreateAccountType + return taskTypes.CreateAccountType case *v1.Task_CreateResource: - return CreateResourceType + return taskTypes.CreateResourceType case *v1.Task_DeleteResource: - return DeleteResourceType + return taskTypes.DeleteResourceType case *v1.Task_RotateCredentials: - return RotateCredentialsType + return taskTypes.RotateCredentialsType case *v1.Task_CreateTicketTask_: - return CreateTicketType + return taskTypes.CreateTicketType case *v1.Task_ListTicketSchemas: - return ListTicketSchemasType + return taskTypes.ListTicketSchemasType case *v1.Task_GetTicket: - return GetTicketType + return taskTypes.GetTicketType default: - return UnknownType + return taskTypes.UnknownType } } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go b/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go new file mode 100644 index 0000000..a98d908 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go @@ -0,0 +1,74 @@ +package tasks + +type TaskType uint8 + +func (tt TaskType) String() string { + switch tt { + case FullSyncType: + return "sync_full" + case GrantType: + return "grant" + case RevokeType: + return "revoke" + case HelloType: + return "hello" + case EventFeedType: + return "event_feed" + case NoneType: + return "none" + case CreateAccountType: + return "create_account" + case CreateResourceType: + return "create_resource" + case DeleteResourceType: + return "delete_resource" + case RotateCredentialsType: + return "rotate_credential" + case CreateTicketType: + return "create_ticket" + case ListTicketSchemasType: + return "list_ticket_schemas" + case GetTicketType: + return "get_ticket" + case GetTicketSchemaType: + return "get_ticket_schema" + case ListResourceTypesType: + return "list_resource_types" + case ListResourcesType: + return "list_resources" + case ListEntitlementsType: + return "list_entitlements" + case ListGrantsType: + return "list_grants" + case GetMetadataType: + return "get_metadata" + case ListEventsType: + return "list_events" + default: + return "unknown" + } +} + +const ( + UnknownType TaskType = iota + NoneType + FullSyncType + GrantType + RevokeType + HelloType + CreateAccountType + CreateResourceType + DeleteResourceType + RotateCredentialsType + EventFeedType + CreateTicketType + ListTicketSchemasType + GetTicketType + GetTicketSchemaType + ListResourceTypesType + ListResourcesType + ListEntitlementsType + ListGrantsType + GetMetadataType + ListEventsType +) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/types/ticket/custom_fields.go b/vendor/github.com/conductorone/baton-sdk/pkg/types/ticket/custom_fields.go index cdb20bb..8d251c6 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/types/ticket/custom_fields.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/types/ticket/custom_fields.go @@ -15,6 +15,8 @@ import ( v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" ) +var ErrFieldNil = errors.New("error: field is nil") + // CustomFieldForSchemaField returns a typed custom field for a given schema field. func CustomFieldForSchemaField(id string, schema *v2.TicketSchema, value interface{}) (*v2.TicketCustomField, error) { field, ok := schema.GetCustomFields()[id] @@ -116,7 +118,7 @@ func CustomFieldForSchemaField(id string, schema *v2.TicketSchema, value interfa func GetStringValue(field *v2.TicketCustomField) (string, error) { if field == nil { - return "", errors.New("error: field is nil") + return "", ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_StringValue) if !ok { @@ -127,7 +129,7 @@ func GetStringValue(field *v2.TicketCustomField) (string, error) { func GetStringsValue(field *v2.TicketCustomField) ([]string, error) { if field == nil { - return nil, errors.New("error: field is nil") + return nil, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_StringValues) if !ok { @@ -138,7 +140,7 @@ func GetStringsValue(field *v2.TicketCustomField) ([]string, error) { func GetBoolValue(field *v2.TicketCustomField) (bool, error) { if field == nil { - return false, errors.New("error: field is nil") + return false, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_BoolValue) if !ok { @@ -149,7 +151,7 @@ func GetBoolValue(field *v2.TicketCustomField) (bool, error) { func GetTimestampValue(field *v2.TicketCustomField) (time.Time, error) { if field == nil { - return time.Time{}, errors.New("error: field is nil") + return time.Time{}, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_TimestampValue) if !ok { @@ -160,7 +162,7 @@ func GetTimestampValue(field *v2.TicketCustomField) (time.Time, error) { func GetPickStringValue(field *v2.TicketCustomField) (string, error) { if field == nil { - return "", errors.New("error: field is nil") + return "", ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_PickStringValue) if !ok { @@ -171,7 +173,7 @@ func GetPickStringValue(field *v2.TicketCustomField) (string, error) { func GetPickMultipleStringValues(field *v2.TicketCustomField) ([]string, error) { if field == nil { - return nil, errors.New("error: field is nil") + return nil, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_PickMultipleStringValues) if !ok { @@ -182,7 +184,7 @@ func GetPickMultipleStringValues(field *v2.TicketCustomField) ([]string, error) func GetPickObjectValue(field *v2.TicketCustomField) (*v2.TicketCustomFieldObjectValue, error) { if field == nil { - return nil, errors.New("error: field is nil") + return nil, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_PickObjectValue) if !ok { @@ -193,7 +195,7 @@ func GetPickObjectValue(field *v2.TicketCustomField) (*v2.TicketCustomFieldObjec func GetPickMultipleObjectValues(field *v2.TicketCustomField) ([]*v2.TicketCustomFieldObjectValue, error) { if field == nil { - return nil, errors.New("error: field is nil") + return nil, ErrFieldNil } v, ok := field.GetValue().(*v2.TicketCustomField_PickMultipleObjectValues) if !ok { @@ -242,38 +244,37 @@ func GetCustomFieldValue(field *v2.TicketCustomField) (interface{}, error) { func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Ticket) (bool, error) { l := ctxzap.Extract(ctx) - // Look for a matching status - foundMatch := false - for _, status := range schema.GetStatuses() { - if ticket.Status == nil { - l.Debug("error: invalid ticket: status is not set") - return false, nil - } - if ticket.Status.GetId() == status.GetId() { - foundMatch = true - break + // Validate the ticket status is one defined in the schema + // Ticket status is not required so if a ticket doesn't have a status + // we don't need to validate, skip the loop in this case + validTicketStatus := ticket.Status == nil + if !validTicketStatus { + for _, status := range schema.GetStatuses() { + if ticket.Status.GetId() == status.GetId() { + validTicketStatus = true + break + } } } - - if !foundMatch { + if !validTicketStatus { l.Debug("error: invalid ticket: could not find status", zap.String("status_id", ticket.Status.GetId())) return false, nil } - // Look for a matching ticket type - foundMatch = false - for _, tType := range schema.GetTypes() { - if ticket.Type == nil { - return false, nil - } - if ticket.Type.GetId() == tType.GetId() { - foundMatch = true - break + // Validate the ticket type is one defined in the schema + // Ticket type is not required so if a ticket doesn't have a type + // we don't need to validate, skip the loop in this case + validTicketType := ticket.Type == nil + if !validTicketType { + for _, tType := range schema.GetTypes() { + if ticket.Type.GetId() == tType.GetId() { + validTicketType = true + break + } } } - - if !foundMatch { - l.Debug("error: invalid ticket: could not find ticket type", zap.String("ticket_type_id", ticket.Status.GetId())) + if !validTicketType { + l.Debug("error: invalid ticket: could not find ticket type", zap.String("ticket_type_id", ticket.Type.GetId())) return false, nil } @@ -300,7 +301,7 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic return false, nil } - if cf.Required && tv.StringValue.Value == "" { + if cf.Required && tv.StringValue.GetValue() == "" { l.Debug("error: invalid ticket: string value is required but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -312,7 +313,7 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic return false, nil } - if cf.Required && len(tv.StringValues.Values) == 0 { + if cf.Required && len(tv.StringValues.GetValues()) == 0 { l.Debug("error: invalid ticket: string values is required but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -331,7 +332,7 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic return false, nil } - if cf.Required && tv.TimestampValue.Value == nil { + if cf.Required && tv.TimestampValue.GetValue() == nil { l.Debug("error: invalid ticket: expected timestamp value for field but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -346,7 +347,13 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic ticketValue := tv.PickStringValue.GetValue() allowedValues := v.PickStringValue.GetAllowedValues() - if cf.Required && ticketValue == "" { + // String value is empty but custom field is not required, skip further validation + if !cf.Required && ticketValue == "" { + continue + } + + // Custom field is required, check if string is empty + if ticketValue == "" { l.Debug("error: invalid ticket: expected string value for field but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -356,7 +363,7 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic return false, nil } - foundMatch = false + foundMatch := false for _, m := range allowedValues { if m == ticketValue { foundMatch = true @@ -383,7 +390,13 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic ticketValues := tv.PickMultipleStringValues.GetValues() allowedValues := v.PickMultipleStringValues.GetAllowedValues() - if cf.Required && len(ticketValues) == 0 { + // String values are empty but custom field is not required, skip further validation + if !cf.Required && len(ticketValues) == 0 { + continue + } + + // Custom field is required so check if string values are empty + if len(ticketValues) == 0 { l.Debug("error: invalid ticket: string values is required but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -421,7 +434,13 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic ticketValue := tv.PickObjectValue.GetValue() allowedValues := v.PickObjectValue.GetAllowedValues() - if cf.Required && ticketValue == nil || ticketValue.GetId() == "" { + // Object value for field is nil, but custom field is not required, skip further validation + if !cf.Required && (ticketValue == nil || ticketValue.GetId() == "") { + continue + } + + // Custom field is required so check if object value for field is nil + if ticketValue == nil || ticketValue.GetId() == "" { l.Debug("error: invalid ticket: expected object value for field but was nil", zap.String("custom_field_id", cf.Id)) return false, nil } @@ -431,7 +450,7 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic return false, nil } - foundMatch = false + foundMatch := false for _, m := range allowedValues { if m.GetId() == ticketValue.GetId() { foundMatch = true @@ -458,7 +477,13 @@ func ValidateTicket(ctx context.Context, schema *v2.TicketSchema, ticket *v2.Tic ticketValues := tv.PickMultipleObjectValues.GetValues() allowedValues := v.PickMultipleObjectValues.GetAllowedValues() - if cf.Required && len(ticketValues) == 0 { + // Object values are empty but custom field is not required, skip further validation + if !cf.Required && len(ticketValues) == 0 { + continue + } + + // Custom field is required so check if object values are empty + if len(ticketValues) == 0 { l.Debug("error: invalid ticket: object values is required but was empty", zap.String("custom_field_id", cf.Id)) return false, nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/authcredentials.go b/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/authcredentials.go index a17624c..6addfbc 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/authcredentials.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/authcredentials.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" "golang.org/x/oauth2/jwt" @@ -146,7 +147,7 @@ func (o *OAuth2JWT) GetClient(ctx context.Context, options ...Option) (*http.Cli } func getHttpClient(ctx context.Context, options ...Option) (*http.Client, error) { - options = append(options, WithLogger(true, nil)) + options = append(options, WithLogger(true, ctxzap.Extract(ctx))) httpClient, err := NewClient(ctx, options...) if err != nil { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/wrapper.go b/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/wrapper.go index 2b4a1e2..0e4fa4e 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/wrapper.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/uhttp/wrapper.go @@ -45,11 +45,17 @@ func NewBaseHttpClient(httpClient *http.Client) *BaseHttpClient { } } +// WithJSONResponse is a wrapper that marshals the returned response body into +// the provided shape. If the API should return an empty JSON body (i.e. HTTP +// status code 204 No Content), then pass a `nil` to `response`. func WithJSONResponse(response interface{}) DoOption { return func(resp *WrapperResponse) error { if !helpers.IsJSONContentType(resp.Header.Get(ContentType)) { return fmt.Errorf("unexpected content type for json response: %s", resp.Header.Get(ContentType)) } + if response == nil && len(resp.Body) == 0 { + return nil + } return json.Unmarshal(resp.Body, response) } } @@ -101,6 +107,9 @@ func WithXMLResponse(response interface{}) DoOption { if !helpers.IsXMLContentType(resp.Header.Get(ContentType)) { return fmt.Errorf("unexpected content type for xml response: %s", resp.Header.Get(ContentType)) } + if response == nil && len(resp.Body) == 0 { + return nil + } return xml.Unmarshal(resp.Body, response) } } diff --git a/vendor/github.com/deckarep/golang-set/v2/.gitignore b/vendor/github.com/deckarep/golang-set/v2/.gitignore new file mode 100644 index 0000000..4eb156d --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +.idea \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/LICENSE b/vendor/github.com/deckarep/golang-set/v2/LICENSE new file mode 100644 index 0000000..efd4827 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/LICENSE @@ -0,0 +1,22 @@ +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +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/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md new file mode 100644 index 0000000..921f0ce --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -0,0 +1,181 @@ +![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) +[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) + +# golang-set + +The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. + +## Update 3/5/2023 +* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. +* supports `new generic` syntax +* Go `1.18.0` or higher +* Workflow tested on Go `1.20` + +![With Generics](new_improved.jpeg) + +Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set collection from Python. +You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs. + +## Install + +Use `go get` to install this package. + +```shell +go get github.com/deckarep/golang-set/v2 +``` + +## Features + +* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) +* One common *interface* to both implementations + * a **non threadsafe** implementation favoring *performance* + * a **threadsafe** implementation favoring *concurrent* use +* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). +* Exhaustive unit-test and benchmark suite + +## Trusted by + +This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. + +* Notable projects/companies using this package + * Ethereum + * Docker + * 1Password + * Hashicorp + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) + + +## Usage + +The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. + +What is considered comparable in Go? +* `Booleans`, `integers`, `strings`, `floats` or basically primitive types. +* `Pointers` +* `Arrays` +* `Structs` if *all of their fields* are also comparable independently + +Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. + +```go +// Syntax example, doesn't compile. +mySet := mapset.NewSet[T]() // where T is some concrete comparable type. + +// Therefore this code creates an int set +mySet := mapset.NewSet[int]() + +// Or perhaps you want a string set +mySet := mapset.NewSet[string]() + +type myStruct struct { + name string + age uint8 +} + +// Alternatively a set of structs +mySet := mapset.NewSet[myStruct]() + +// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. +mySet := mapset.NewSet[any]() +``` + +## Comprehensive Example + +```go +package main + +import ( + "fmt" + mapset "github.com/deckarep/golang-set/v2" +) + +func main() { + // Create a string-based set of required classes. + required := mapset.NewSet[string]() + required.Add("cooking") + required.Add("english") + required.Add("math") + required.Add("biology") + + // Create a string-based set of science classes. + sciences := mapset.NewSet[string]() + sciences.Add("biology") + sciences.Add("chemistry") + + // Create a string-based set of electives. + electives := mapset.NewSet[string]() + electives.Add("welding") + electives.Add("music") + electives.Add("automotive") + + // Create a string-based set of bonus programming classes. + bonus := mapset.NewSet[string]() + bonus.Add("beginner go") + bonus.Add("python for dummies") +} +``` + +Create a set of all unique classes. +Sets will *automatically* deduplicate the same data. + +```go + all := required + .Union(sciences) + .Union(electives) + .Union(bonus) + + fmt.Println(all) +``` + +Output: +```sh +Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} +``` + +Is cooking considered a science class? +```go +result := sciences.Contains("cooking") +fmt.Println(result) +``` + +Output: +```false +false +``` + +Show me all classes that are not science classes, since I don't enjoy science. +```go +notScience := all.Difference(sciences) +fmt.Println(notScience) +``` + +```sh +Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } +``` + +Which science classes are also required classes? +```go +reqScience := sciences.Intersect(required) +``` + +Output: +```sh +Set{biology} +``` + +How many bonus classes do you offer? +```go +fmt.Println(bonus.Cardinality()) +``` +Output: +```sh +2 +``` + +Thanks for visiting! + +-deckarep diff --git a/vendor/github.com/deckarep/golang-set/v2/iterator.go b/vendor/github.com/deckarep/golang-set/v2/iterator.go new file mode 100644 index 0000000..fc14e70 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/iterator.go @@ -0,0 +1,58 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +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. +*/ + +package mapset + +// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's +// elements. +type Iterator[T comparable] struct { + C <-chan T + stop chan struct{} +} + +// Stop stops the Iterator, no further elements will be received on C, C will be closed. +func (i *Iterator[T]) Stop() { + // Allows for Stop() to be called multiple times + // (close() panics when called on already closed channel) + defer func() { + recover() + }() + + close(i.stop) + + // Exhaust any remaining elements. + for range i.C { + } +} + +// newIterator returns a new Iterator instance together with its item and stop channels. +func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { + itemChan := make(chan T) + stopChan := make(chan struct{}) + return &Iterator[T]{ + C: itemChan, + stop: stopChan, + }, itemChan, stopChan +} diff --git a/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..429752a07a94b837434515275aa7645714879341 GIT binary patch literal 120935 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go new file mode 100644 index 0000000..292089d --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -0,0 +1,255 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +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. +*/ + +// Package mapset implements a simple and set collection. +// Items stored within it are unordered and unique. It supports +// typical set operations: membership testing, intersection, union, +// difference, symmetric difference and cloning. +// +// Package mapset provides two implementations of the Set +// interface. The default implementation is safe for concurrent +// access, but a non-thread-safe implementation is also provided for +// programs that can benefit from the slight speed improvement and +// that can enforce mutual exclusion through other means. +package mapset + +// Set is the primary interface provided by the mapset package. It +// represents an unordered set of data and a large number of +// operations that can be applied to that set. +type Set[T comparable] interface { + // Add adds an element to the set. Returns whether + // the item was added. + Add(val T) bool + + // Append multiple elements to the set. Returns + // the number of elements added. + Append(val ...T) int + + // Cardinality returns the number of elements in the set. + Cardinality() int + + // Clear removes all elements from the set, leaving + // the empty set. + Clear() + + // Clone returns a clone of the set using the same + // implementation, duplicating all keys. + Clone() Set[T] + + // Contains returns whether the given items + // are all in the set. + Contains(val ...T) bool + + // ContainsOne returns whether the given item + // is in the set. + // + // Contains may cause the argument to escape to the heap. + // See: https://github.com/deckarep/golang-set/issues/118 + ContainsOne(val T) bool + + // ContainsAny returns whether at least one of the + // given items are in the set. + ContainsAny(val ...T) bool + + // Difference returns the difference between this set + // and other. The returned set will contain + // all elements of this set that are not also + // elements of other. + // + // Note that the argument to Difference + // must be of the same type as the receiver + // of the method. Otherwise, Difference will + // panic. + Difference(other Set[T]) Set[T] + + // Equal determines if two sets are equal to each + // other. If they have the same cardinality + // and contain the same elements, they are + // considered equal. The order in which + // the elements were added is irrelevant. + // + // Note that the argument to Equal must be + // of the same type as the receiver of the + // method. Otherwise, Equal will panic. + Equal(other Set[T]) bool + + // Intersect returns a new set containing only the elements + // that exist only in both sets. + // + // Note that the argument to Intersect + // must be of the same type as the receiver + // of the method. Otherwise, Intersect will + // panic. + Intersect(other Set[T]) Set[T] + + // IsEmpty determines if there are elements in the set. + IsEmpty() bool + + // IsProperSubset determines if every element in this set is in + // the other set but the two sets are not equal. + // + // Note that the argument to IsProperSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsProperSubset + // will panic. + IsProperSubset(other Set[T]) bool + + // IsProperSuperset determines if every element in the other set + // is in this set but the two sets are not + // equal. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsProperSuperset(other Set[T]) bool + + // IsSubset determines if every element in this set is in + // the other set. + // + // Note that the argument to IsSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsSubset will + // panic. + IsSubset(other Set[T]) bool + + // IsSuperset determines if every element in the other set + // is in this set. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsSuperset(other Set[T]) bool + + // Each iterates over elements and executes the passed func against each element. + // If passed func returns true, stop iteration at the time. + Each(func(T) bool) + + // Iter returns a channel of elements that you can + // range over. + Iter() <-chan T + + // Iterator returns an Iterator object that you can + // use to range over the set. + Iterator() *Iterator[T] + + // Remove removes a single element from the set. + Remove(i T) + + // RemoveAll removes multiple elements from the set. + RemoveAll(i ...T) + + // String provides a convenient string representation + // of the current state of the set. + String() string + + // SymmetricDifference returns a new set with all elements which are + // in either this set or the other set but not in both. + // + // Note that the argument to SymmetricDifference + // must be of the same type as the receiver + // of the method. Otherwise, SymmetricDifference + // will panic. + SymmetricDifference(other Set[T]) Set[T] + + // Union returns a new set with all elements in both sets. + // + // Note that the argument to Union must be of the + // same type as the receiver of the method. + // Otherwise, Union will panic. + Union(other Set[T]) Set[T] + + // Pop removes and returns an arbitrary item from the set. + Pop() (T, bool) + + // ToSlice returns the members of the set as a slice. + ToSlice() []T + + // MarshalJSON will marshal the set into a JSON-based representation. + MarshalJSON() ([]byte, error) + + // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + UnmarshalJSON(b []byte) error +} + +// NewSet creates and returns a new set with the given elements. +// Operations on the resulting set are thread-safe. +func NewSet[T comparable](vals ...T) Set[T] { + s := newThreadSafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewSetWithSize creates and returns a reference to an empty set with a specified +// capacity. Operations on the resulting set are thread-safe. +func NewSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadSafeSetWithSize[T](cardinality) + return s +} + +// NewThreadUnsafeSet creates and returns a new set with the given elements. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { + s := newThreadUnsafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with +// a specified capacity. Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadUnsafeSetWithSize[T](cardinality) + return s +} + +// NewSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are thread-safe. +func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} + +// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewThreadUnsafeSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/sorted.go b/vendor/github.com/deckarep/golang-set/v2/sorted.go new file mode 100644 index 0000000..8ee2e70 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/sorted.go @@ -0,0 +1,42 @@ +//go:build go1.21 +// +build go1.21 + +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2023 Ralph Caraveo (deckarep@gmail.com) + +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. +*/ + +package mapset + +import ( + "cmp" + "slices" +) + +// Sorted returns a sorted slice of a set of any ordered type in ascending order. +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sorted[E cmp.Ordered](set Set[E]) []E { + s := set.ToSlice() + slices.Sort(s) + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go new file mode 100644 index 0000000..ad7a834 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -0,0 +1,299 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +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. +*/ + +package mapset + +import "sync" + +type threadSafeSet[T comparable] struct { + sync.RWMutex + uss threadUnsafeSet[T] +} + +func newThreadSafeSet[T comparable]() *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSet[T](), + } +} + +func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSetWithSize[T](cardinality), + } +} + +func (t *threadSafeSet[T]) Add(v T) bool { + t.Lock() + ret := t.uss.Add(v) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Append(v ...T) int { + t.Lock() + ret := t.uss.Append(v...) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Contains(v ...T) bool { + t.RLock() + ret := t.uss.Contains(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsOne(v T) bool { + t.RLock() + ret := t.uss.ContainsOne(v) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { + t.RLock() + ret := t.uss.ContainsAny(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) IsEmpty() bool { + return t.Cardinality() == 0 +} + +func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.IsSubset(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + defer t.RUnlock() + o.RLock() + defer o.RUnlock() + + return t.uss.IsProperSubset(o.uss) +} + +func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(t) +} + +func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { + return other.IsProperSubset(t) +} + +func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeUnion} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeIntersection} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clear() { + t.Lock() + t.uss.Clear() + t.Unlock() +} + +func (t *threadSafeSet[T]) Remove(v T) { + t.Lock() + delete(t.uss, v) + t.Unlock() +} + +func (t *threadSafeSet[T]) RemoveAll(i ...T) { + t.Lock() + t.uss.RemoveAll(i...) + t.Unlock() +} + +func (t *threadSafeSet[T]) Cardinality() int { + t.RLock() + defer t.RUnlock() + return len(t.uss) +} + +func (t *threadSafeSet[T]) Each(cb func(T) bool) { + t.RLock() + for elem := range t.uss { + if cb(elem) { + break + } + } + t.RUnlock() +} + +func (t *threadSafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + t.RLock() + + for elem := range t.uss { + ch <- elem + } + close(ch) + t.RUnlock() + }() + + return ch +} + +func (t *threadSafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + t.RLock() + L: + for elem := range t.uss { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + t.RUnlock() + }() + + return iterator +} + +func (t *threadSafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.Equal(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clone() Set[T] { + t.RLock() + + unsafeClone := t.uss.Clone().(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeClone} + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) String() string { + t.RLock() + ret := t.uss.String() + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Pop() (T, bool) { + t.Lock() + defer t.Unlock() + return t.uss.Pop() +} + +func (t *threadSafeSet[T]) ToSlice() []T { + keys := make([]T, 0, t.Cardinality()) + t.RLock() + for elem := range t.uss { + keys = append(keys, elem) + } + t.RUnlock() + return keys +} + +func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { + t.RLock() + b, err := t.uss.MarshalJSON() + t.RUnlock() + + return b, err +} + +func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { + t.RLock() + err := t.uss.UnmarshalJSON(p) + t.RUnlock() + + return err +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go new file mode 100644 index 0000000..8b17b01 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -0,0 +1,330 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +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. +*/ + +package mapset + +import ( + "encoding/json" + "fmt" + "strings" +) + +type threadUnsafeSet[T comparable] map[T]struct{} + +// Assert concrete type:threadUnsafeSet adheres to Set interface. +var _ Set[string] = (threadUnsafeSet[string])(nil) + +func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] { + return make(threadUnsafeSet[T]) +} + +func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] { + return make(threadUnsafeSet[T], cardinality) +} + +func (s threadUnsafeSet[T]) Add(v T) bool { + prevLen := len(s) + s[v] = struct{}{} + return prevLen != len(s) +} + +func (s threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(s) + for _, val := range v { + (s)[val] = struct{}{} + } + return len(s) - prevLen +} + +// private version of Add which doesn't return a value +func (s threadUnsafeSet[T]) add(v T) { + s[v] = struct{}{} +} + +func (s threadUnsafeSet[T]) Cardinality() int { + return len(s) +} + +func (s threadUnsafeSet[T]) Clear() { + // Constructions like this are optimised by compiler, and replaced by + // mapclear() function, defined in + // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) + for key := range s { + delete(s, key) + } +} + +func (s threadUnsafeSet[T]) Clone() Set[T] { + clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) + for elem := range s { + clonedSet.add(elem) + } + return clonedSet +} + +func (s threadUnsafeSet[T]) Contains(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; !ok { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) ContainsOne(v T) bool { + _, ok := s[v] + return ok +} + +func (s threadUnsafeSet[T]) ContainsAny(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; ok { + return true + } + } + return false +} + +// private version of Contains for a single element v +func (s threadUnsafeSet[T]) contains(v T) (ok bool) { + _, ok = s[v] + return ok +} + +func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + diff := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + diff.add(elem) + } + } + return diff +} + +func (s threadUnsafeSet[T]) Each(cb func(T) bool) { + for elem := range s { + if cb(elem) { + break + } + } +} + +func (s threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + + if s.Cardinality() != other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + intersection := newThreadUnsafeSet[T]() + // loop over smaller set + if s.Cardinality() < other.Cardinality() { + for elem := range s { + if o.contains(elem) { + intersection.add(elem) + } + } + } else { + for elem := range o { + if s.contains(elem) { + intersection.add(elem) + } + } + } + return intersection +} + +func (s threadUnsafeSet[T]) IsEmpty() bool { + return s.Cardinality() == 0 +} + +func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { + return s.Cardinality() < other.Cardinality() && s.IsSubset(other) +} + +func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { + return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) +} + +func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + if s.Cardinality() > other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(s) +} + +func (s threadUnsafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + for elem := range s { + ch <- elem + } + close(ch) + }() + + return ch +} + +func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + L: + for elem := range s { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + }() + + return iterator +} + +// Pop returns a popped item in case set is not empty, or nil-value of T +// if set is already empty +func (s threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range s { + delete(s, item) + return item, true + } + return v, false +} + +func (s threadUnsafeSet[T]) Remove(v T) { + delete(s, v) +} + +func (s threadUnsafeSet[T]) RemoveAll(i ...T) { + for _, elem := range i { + delete(s, elem) + } +} + +func (s threadUnsafeSet[T]) String() string { + items := make([]string, 0, len(s)) + + for elem := range s { + items = append(items, fmt.Sprintf("%v", elem)) + } + return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) +} + +func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + sd := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + sd.add(elem) + } + } + for elem := range o { + if !s.contains(elem) { + sd.add(elem) + } + } + return sd +} + +func (s threadUnsafeSet[T]) ToSlice() []T { + keys := make([]T, 0, s.Cardinality()) + for elem := range s { + keys = append(keys, elem) + } + + return keys +} + +func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + n := s.Cardinality() + if o.Cardinality() > n { + n = o.Cardinality() + } + unionedSet := make(threadUnsafeSet[T], n) + + for elem := range s { + unionedSet.add(elem) + } + for elem := range o { + unionedSet.add(elem) + } + return unionedSet +} + +// MarshalJSON creates a JSON array from the set, it marshals all elements +func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { + items := make([]string, 0, s.Cardinality()) + + for elem := range s { + b, err := json.Marshal(elem) + if err != nil { + return nil, err + } + + items = append(items, string(b)) + } + + return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil +} + +// UnmarshalJSON recreates a set from a JSON array, it only decodes +// primitive types. Numbers are decoded as json.Number. +func (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { + var i []T + err := json.Unmarshal(b, &i) + if err != nil { + return err + } + s.Append(i...) + + return nil +} diff --git a/vendor/github.com/doug-martin/goqu/v9/dialect/sqlite3/sqlite3.go b/vendor/github.com/doug-martin/goqu/v9/dialect/sqlite3/sqlite3.go new file mode 100644 index 0000000..40ddb2a --- /dev/null +++ b/vendor/github.com/doug-martin/goqu/v9/dialect/sqlite3/sqlite3.go @@ -0,0 +1,76 @@ +package sqlite3 + +import ( + "time" + + "github.com/doug-martin/goqu/v9" + "github.com/doug-martin/goqu/v9/exp" +) + +func DialectOptions() *goqu.SQLDialectOptions { + opts := goqu.DefaultDialectOptions() + + opts.SupportsReturn = false + opts.SupportsOrderByOnUpdate = true + opts.SupportsLimitOnUpdate = true + opts.SupportsOrderByOnDelete = true + opts.SupportsLimitOnDelete = true + opts.SupportsConflictUpdateWhere = false + opts.SupportsInsertIgnoreSyntax = true + opts.SupportsConflictTarget = true + opts.SupportsMultipleUpdateTables = false + opts.WrapCompoundsInParens = false + opts.SupportsDistinctOn = false + opts.SupportsWindowFunction = false + opts.SupportsLateral = false + + opts.PlaceHolderFragment = []byte("?") + opts.IncludePlaceholderNum = false + opts.QuoteRune = '`' + opts.DefaultValuesFragment = []byte("") + opts.True = []byte("1") + opts.False = []byte("0") + opts.TimeFormat = time.RFC3339Nano + opts.BooleanOperatorLookup = map[exp.BooleanOperation][]byte{ + exp.EqOp: []byte("="), + exp.NeqOp: []byte("!="), + exp.GtOp: []byte(">"), + exp.GteOp: []byte(">="), + exp.LtOp: []byte("<"), + exp.LteOp: []byte("<="), + exp.InOp: []byte("IN"), + exp.NotInOp: []byte("NOT IN"), + exp.IsOp: []byte("IS"), + exp.IsNotOp: []byte("IS NOT"), + exp.LikeOp: []byte("LIKE"), + exp.NotLikeOp: []byte("NOT LIKE"), + exp.ILikeOp: []byte("LIKE"), + exp.NotILikeOp: []byte("NOT LIKE"), + exp.RegexpLikeOp: []byte("REGEXP"), + exp.RegexpNotLikeOp: []byte("NOT REGEXP"), + exp.RegexpILikeOp: []byte("REGEXP"), + exp.RegexpNotILikeOp: []byte("NOT REGEXP"), + } + opts.UseLiteralIsBools = false + opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{ + exp.BitwiseOrOp: []byte("|"), + exp.BitwiseAndOp: []byte("&"), + exp.BitwiseLeftShiftOp: []byte("<<"), + exp.BitwiseRightShiftOp: []byte(">>"), + } + opts.EscapedRunes = map[rune][]byte{ + '\'': []byte("''"), + } + opts.InsertIgnoreClause = []byte("INSERT OR IGNORE INTO ") + opts.ConflictFragment = []byte(" ON CONFLICT ") + opts.ConflictDoUpdateFragment = []byte(" DO UPDATE SET ") + opts.ConflictDoNothingFragment = []byte(" DO NOTHING ") + opts.ForUpdateFragment = []byte("") + opts.OfFragment = []byte("") + opts.NowaitFragment = []byte("") + return opts +} + +func init() { + goqu.RegisterDialect("sqlite3", DialectOptions()) +} diff --git a/vendor/go.opentelemetry.io/otel/LICENSE b/vendor/go.opentelemetry.io/otel/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/LICENSE @@ -0,0 +1,201 @@ + 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/go.opentelemetry.io/otel/attribute/README.md b/vendor/go.opentelemetry.io/otel/attribute/README.md new file mode 100644 index 0000000..5b3da8f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/README.md @@ -0,0 +1,3 @@ +# Attribute + +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/attribute)](https://pkg.go.dev/go.opentelemetry.io/otel/attribute) diff --git a/vendor/go.opentelemetry.io/otel/attribute/doc.go b/vendor/go.opentelemetry.io/otel/attribute/doc.go new file mode 100644 index 0000000..eef51eb --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/doc.go @@ -0,0 +1,5 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package attribute provides key and value attributes. +package attribute // import "go.opentelemetry.io/otel/attribute" diff --git a/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/vendor/go.opentelemetry.io/otel/attribute/encoder.go new file mode 100644 index 0000000..318e42f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/encoder.go @@ -0,0 +1,135 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "bytes" + "sync" + "sync/atomic" +) + +type ( + // Encoder is a mechanism for serializing an attribute set into a specific + // string representation that supports caching, to avoid repeated + // serialization. An example could be an exporter encoding the attribute + // set into a wire representation. + Encoder interface { + // Encode returns the serialized encoding of the attribute set using + // its Iterator. This result may be cached by a attribute.Set. + Encode(iterator Iterator) string + + // ID returns a value that is unique for each class of attribute + // encoder. Attribute encoders allocate these using `NewEncoderID`. + ID() EncoderID + } + + // EncoderID is used to identify distinct Encoder + // implementations, for caching encoded results. + EncoderID struct { + value uint64 + } + + // defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of + // allocations used in encoding attributes. This implementation encodes a + // comma-separated list of key=value, with '/'-escaping of '=', ',', and + // '\'. + defaultAttrEncoder struct { + // pool is a pool of attribute set builders. The buffers in this pool + // grow to a size that most attribute encodings will not allocate new + // memory. + pool sync.Pool // *bytes.Buffer + } +) + +// escapeChar is used to ensure uniqueness of the attribute encoding where +// keys or values contain either '=' or ','. Since there is no parser needed +// for this encoding and its only requirement is to be unique, this choice is +// arbitrary. Users will see these in some exporters (e.g., stdout), so the +// backslash ('\') is used as a conventional choice. +const escapeChar = '\\' + +var ( + _ Encoder = &defaultAttrEncoder{} + + // encoderIDCounter is for generating IDs for other attribute encoders. + encoderIDCounter uint64 + + defaultEncoderOnce sync.Once + defaultEncoderID = NewEncoderID() + defaultEncoderInstance *defaultAttrEncoder +) + +// NewEncoderID returns a unique attribute encoder ID. It should be called +// once per each type of attribute encoder. Preferably in init() or in var +// definition. +func NewEncoderID() EncoderID { + return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)} +} + +// DefaultEncoder returns an attribute encoder that encodes attributes in such +// a way that each escaped attribute's key is followed by an equal sign and +// then by an escaped attribute's value. All key-value pairs are separated by +// a comma. +// +// Escaping is done by prepending a backslash before either a backslash, equal +// sign or a comma. +func DefaultEncoder() Encoder { + defaultEncoderOnce.Do(func() { + defaultEncoderInstance = &defaultAttrEncoder{ + pool: sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, + }, + } + }) + return defaultEncoderInstance +} + +// Encode is a part of an implementation of the AttributeEncoder interface. +func (d *defaultAttrEncoder) Encode(iter Iterator) string { + buf := d.pool.Get().(*bytes.Buffer) + defer d.pool.Put(buf) + buf.Reset() + + for iter.Next() { + i, keyValue := iter.IndexedAttribute() + if i > 0 { + _, _ = buf.WriteRune(',') + } + copyAndEscape(buf, string(keyValue.Key)) + + _, _ = buf.WriteRune('=') + + if keyValue.Value.Type() == STRING { + copyAndEscape(buf, keyValue.Value.AsString()) + } else { + _, _ = buf.WriteString(keyValue.Value.Emit()) + } + } + return buf.String() +} + +// ID is a part of an implementation of the AttributeEncoder interface. +func (*defaultAttrEncoder) ID() EncoderID { + return defaultEncoderID +} + +// copyAndEscape escapes `=`, `,` and its own escape character (`\`), +// making the default encoding unique. +func copyAndEscape(buf *bytes.Buffer, val string) { + for _, ch := range val { + switch ch { + case '=', ',', escapeChar: + _, _ = buf.WriteRune(escapeChar) + } + _, _ = buf.WriteRune(ch) + } +} + +// Valid returns true if this encoder ID was allocated by +// `NewEncoderID`. Invalid encoder IDs will not be cached. +func (id EncoderID) Valid() bool { + return id.value != 0 +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/filter.go b/vendor/go.opentelemetry.io/otel/attribute/filter.go new file mode 100644 index 0000000..be9cd92 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/filter.go @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +// Filter supports removing certain attributes from attribute sets. When +// the filter returns true, the attribute will be kept in the filtered +// attribute set. When the filter returns false, the attribute is excluded +// from the filtered attribute set, and the attribute instead appears in +// the removed list of excluded attributes. +type Filter func(KeyValue) bool + +// NewAllowKeysFilter returns a Filter that only allows attributes with one of +// the provided keys. +// +// If keys is empty a deny-all filter is returned. +func NewAllowKeysFilter(keys ...Key) Filter { + if len(keys) <= 0 { + return func(kv KeyValue) bool { return false } + } + + allowed := make(map[Key]struct{}) + for _, k := range keys { + allowed[k] = struct{}{} + } + return func(kv KeyValue) bool { + _, ok := allowed[kv.Key] + return ok + } +} + +// NewDenyKeysFilter returns a Filter that only allows attributes +// that do not have one of the provided keys. +// +// If keys is empty an allow-all filter is returned. +func NewDenyKeysFilter(keys ...Key) Filter { + if len(keys) <= 0 { + return func(kv KeyValue) bool { return true } + } + + forbid := make(map[Key]struct{}) + for _, k := range keys { + forbid[k] = struct{}{} + } + return func(kv KeyValue) bool { + _, ok := forbid[kv.Key] + return !ok + } +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/iterator.go b/vendor/go.opentelemetry.io/otel/attribute/iterator.go new file mode 100644 index 0000000..f2ba89c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/iterator.go @@ -0,0 +1,150 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +// Iterator allows iterating over the set of attributes in order, sorted by +// key. +type Iterator struct { + storage *Set + idx int +} + +// MergeIterator supports iterating over two sets of attributes while +// eliminating duplicate values from the combined set. The first iterator +// value takes precedence. +type MergeIterator struct { + one oneIterator + two oneIterator + current KeyValue +} + +type oneIterator struct { + iter Iterator + done bool + attr KeyValue +} + +// Next moves the iterator to the next position. Returns false if there are no +// more attributes. +func (i *Iterator) Next() bool { + i.idx++ + return i.idx < i.Len() +} + +// Label returns current KeyValue. Must be called only after Next returns +// true. +// +// Deprecated: Use Attribute instead. +func (i *Iterator) Label() KeyValue { + return i.Attribute() +} + +// Attribute returns the current KeyValue of the Iterator. It must be called +// only after Next returns true. +func (i *Iterator) Attribute() KeyValue { + kv, _ := i.storage.Get(i.idx) + return kv +} + +// IndexedLabel returns current index and attribute. Must be called only +// after Next returns true. +// +// Deprecated: Use IndexedAttribute instead. +func (i *Iterator) IndexedLabel() (int, KeyValue) { + return i.idx, i.Attribute() +} + +// IndexedAttribute returns current index and attribute. Must be called only +// after Next returns true. +func (i *Iterator) IndexedAttribute() (int, KeyValue) { + return i.idx, i.Attribute() +} + +// Len returns a number of attributes in the iterated set. +func (i *Iterator) Len() int { + return i.storage.Len() +} + +// ToSlice is a convenience function that creates a slice of attributes from +// the passed iterator. The iterator is set up to start from the beginning +// before creating the slice. +func (i *Iterator) ToSlice() []KeyValue { + l := i.Len() + if l == 0 { + return nil + } + i.idx = -1 + slice := make([]KeyValue, 0, l) + for i.Next() { + slice = append(slice, i.Attribute()) + } + return slice +} + +// NewMergeIterator returns a MergeIterator for merging two attribute sets. +// Duplicates are resolved by taking the value from the first set. +func NewMergeIterator(s1, s2 *Set) MergeIterator { + mi := MergeIterator{ + one: makeOne(s1.Iter()), + two: makeOne(s2.Iter()), + } + return mi +} + +func makeOne(iter Iterator) oneIterator { + oi := oneIterator{ + iter: iter, + } + oi.advance() + return oi +} + +func (oi *oneIterator) advance() { + if oi.done = !oi.iter.Next(); !oi.done { + oi.attr = oi.iter.Attribute() + } +} + +// Next returns true if there is another attribute available. +func (m *MergeIterator) Next() bool { + if m.one.done && m.two.done { + return false + } + if m.one.done { + m.current = m.two.attr + m.two.advance() + return true + } + if m.two.done { + m.current = m.one.attr + m.one.advance() + return true + } + if m.one.attr.Key == m.two.attr.Key { + m.current = m.one.attr // first iterator attribute value wins + m.one.advance() + m.two.advance() + return true + } + if m.one.attr.Key < m.two.attr.Key { + m.current = m.one.attr + m.one.advance() + return true + } + m.current = m.two.attr + m.two.advance() + return true +} + +// Label returns the current value after Next() returns true. +// +// Deprecated: Use Attribute instead. +func (m *MergeIterator) Label() KeyValue { + return m.current +} + +// Attribute returns the current value after Next() returns true. +func (m *MergeIterator) Attribute() KeyValue { + return m.current +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/key.go b/vendor/go.opentelemetry.io/otel/attribute/key.go new file mode 100644 index 0000000..d9a22c6 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/key.go @@ -0,0 +1,123 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +// Key represents the key part in key-value pairs. It's a string. The +// allowed character set in the key depends on the use of the key. +type Key string + +// Bool creates a KeyValue instance with a BOOL Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Bool(name, value). +func (k Key) Bool(v bool) KeyValue { + return KeyValue{ + Key: k, + Value: BoolValue(v), + } +} + +// BoolSlice creates a KeyValue instance with a BOOLSLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- BoolSlice(name, value). +func (k Key) BoolSlice(v []bool) KeyValue { + return KeyValue{ + Key: k, + Value: BoolSliceValue(v), + } +} + +// Int creates a KeyValue instance with an INT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int(name, value). +func (k Key) Int(v int) KeyValue { + return KeyValue{ + Key: k, + Value: IntValue(v), + } +} + +// IntSlice creates a KeyValue instance with an INT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- IntSlice(name, value). +func (k Key) IntSlice(v []int) KeyValue { + return KeyValue{ + Key: k, + Value: IntSliceValue(v), + } +} + +// Int64 creates a KeyValue instance with an INT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int64(name, value). +func (k Key) Int64(v int64) KeyValue { + return KeyValue{ + Key: k, + Value: Int64Value(v), + } +} + +// Int64Slice creates a KeyValue instance with an INT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int64Slice(name, value). +func (k Key) Int64Slice(v []int64) KeyValue { + return KeyValue{ + Key: k, + Value: Int64SliceValue(v), + } +} + +// Float64 creates a KeyValue instance with a FLOAT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Float64(name, value). +func (k Key) Float64(v float64) KeyValue { + return KeyValue{ + Key: k, + Value: Float64Value(v), + } +} + +// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Float64(name, value). +func (k Key) Float64Slice(v []float64) KeyValue { + return KeyValue{ + Key: k, + Value: Float64SliceValue(v), + } +} + +// String creates a KeyValue instance with a STRING Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- String(name, value). +func (k Key) String(v string) KeyValue { + return KeyValue{ + Key: k, + Value: StringValue(v), + } +} + +// StringSlice creates a KeyValue instance with a STRINGSLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- StringSlice(name, value). +func (k Key) StringSlice(v []string) KeyValue { + return KeyValue{ + Key: k, + Value: StringSliceValue(v), + } +} + +// Defined returns true for non-empty keys. +func (k Key) Defined() bool { + return len(k) != 0 +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/kv.go b/vendor/go.opentelemetry.io/otel/attribute/kv.go new file mode 100644 index 0000000..3028f9a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/kv.go @@ -0,0 +1,75 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "fmt" +) + +// KeyValue holds a key and value pair. +type KeyValue struct { + Key Key + Value Value +} + +// Valid returns if kv is a valid OpenTelemetry attribute. +func (kv KeyValue) Valid() bool { + return kv.Key.Defined() && kv.Value.Type() != INVALID +} + +// Bool creates a KeyValue with a BOOL Value type. +func Bool(k string, v bool) KeyValue { + return Key(k).Bool(v) +} + +// BoolSlice creates a KeyValue with a BOOLSLICE Value type. +func BoolSlice(k string, v []bool) KeyValue { + return Key(k).BoolSlice(v) +} + +// Int creates a KeyValue with an INT64 Value type. +func Int(k string, v int) KeyValue { + return Key(k).Int(v) +} + +// IntSlice creates a KeyValue with an INT64SLICE Value type. +func IntSlice(k string, v []int) KeyValue { + return Key(k).IntSlice(v) +} + +// Int64 creates a KeyValue with an INT64 Value type. +func Int64(k string, v int64) KeyValue { + return Key(k).Int64(v) +} + +// Int64Slice creates a KeyValue with an INT64SLICE Value type. +func Int64Slice(k string, v []int64) KeyValue { + return Key(k).Int64Slice(v) +} + +// Float64 creates a KeyValue with a FLOAT64 Value type. +func Float64(k string, v float64) KeyValue { + return Key(k).Float64(v) +} + +// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type. +func Float64Slice(k string, v []float64) KeyValue { + return Key(k).Float64Slice(v) +} + +// String creates a KeyValue with a STRING Value type. +func String(k, v string) KeyValue { + return Key(k).String(v) +} + +// StringSlice creates a KeyValue with a STRINGSLICE Value type. +func StringSlice(k string, v []string) KeyValue { + return Key(k).StringSlice(v) +} + +// Stringer creates a new key-value pair with a passed name and a string +// value generated by the passed Stringer interface. +func Stringer(k string, v fmt.Stringer) KeyValue { + return Key(k).String(v.String()) +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/set.go b/vendor/go.opentelemetry.io/otel/attribute/set.go new file mode 100644 index 0000000..bff9c7f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -0,0 +1,431 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "cmp" + "encoding/json" + "reflect" + "slices" + "sort" +) + +type ( + // Set is the representation for a distinct attribute set. It manages an + // immutable set of attributes, with an internal cache for storing + // attribute encodings. + // + // This type will remain comparable for backwards compatibility. The + // equivalence of Sets across versions is not guaranteed to be stable. + // Prior versions may find two Sets to be equal or not when compared + // directly (i.e. ==), but subsequent versions may not. Users should use + // the Equals method to ensure stable equivalence checking. + // + // Users should also use the Distinct returned from Equivalent as a map key + // instead of a Set directly. In addition to that type providing guarantees + // on stable equivalence, it may also provide performance improvements. + Set struct { + equivalent Distinct + } + + // Distinct is a unique identifier of a Set. + // + // Distinct is designed to be ensures equivalence stability: comparisons + // will return the save value across versions. For this reason, Distinct + // should always be used as a map key instead of a Set. + Distinct struct { + iface interface{} + } + + // Sortable implements sort.Interface, used for sorting KeyValue. + // + // Deprecated: This type is no longer used. It was added as a performance + // optimization for Go < 1.21 that is no longer needed (Go < 1.21 is no + // longer supported by the module). + Sortable []KeyValue +) + +var ( + // keyValueType is used in computeDistinctReflect. + keyValueType = reflect.TypeOf(KeyValue{}) + + // emptySet is returned for empty attribute sets. + emptySet = &Set{ + equivalent: Distinct{ + iface: [0]KeyValue{}, + }, + } +) + +// EmptySet returns a reference to a Set with no elements. +// +// This is a convenience provided for optimized calling utility. +func EmptySet() *Set { + return emptySet +} + +// reflectValue abbreviates reflect.ValueOf(d). +func (d Distinct) reflectValue() reflect.Value { + return reflect.ValueOf(d.iface) +} + +// Valid returns true if this value refers to a valid Set. +func (d Distinct) Valid() bool { + return d.iface != nil +} + +// Len returns the number of attributes in this set. +func (l *Set) Len() int { + if l == nil || !l.equivalent.Valid() { + return 0 + } + return l.equivalent.reflectValue().Len() +} + +// Get returns the KeyValue at ordered position idx in this set. +func (l *Set) Get(idx int) (KeyValue, bool) { + if l == nil || !l.equivalent.Valid() { + return KeyValue{}, false + } + value := l.equivalent.reflectValue() + + if idx >= 0 && idx < value.Len() { + // Note: The Go compiler successfully avoids an allocation for + // the interface{} conversion here: + return value.Index(idx).Interface().(KeyValue), true + } + + return KeyValue{}, false +} + +// Value returns the value of a specified key in this set. +func (l *Set) Value(k Key) (Value, bool) { + if l == nil || !l.equivalent.Valid() { + return Value{}, false + } + rValue := l.equivalent.reflectValue() + vlen := rValue.Len() + + idx := sort.Search(vlen, func(idx int) bool { + return rValue.Index(idx).Interface().(KeyValue).Key >= k + }) + if idx >= vlen { + return Value{}, false + } + keyValue := rValue.Index(idx).Interface().(KeyValue) + if k == keyValue.Key { + return keyValue.Value, true + } + return Value{}, false +} + +// HasValue tests whether a key is defined in this set. +func (l *Set) HasValue(k Key) bool { + if l == nil { + return false + } + _, ok := l.Value(k) + return ok +} + +// Iter returns an iterator for visiting the attributes in this set. +func (l *Set) Iter() Iterator { + return Iterator{ + storage: l, + idx: -1, + } +} + +// ToSlice returns the set of attributes belonging to this set, sorted, where +// keys appear no more than once. +func (l *Set) ToSlice() []KeyValue { + iter := l.Iter() + return iter.ToSlice() +} + +// Equivalent returns a value that may be used as a map key. The Distinct type +// guarantees that the result will equal the equivalent. Distinct value of any +// attribute set with the same elements as this, where sets are made unique by +// choosing the last value in the input for any given key. +func (l *Set) Equivalent() Distinct { + if l == nil || !l.equivalent.Valid() { + return emptySet.equivalent + } + return l.equivalent +} + +// Equals returns true if the argument set is equivalent to this set. +func (l *Set) Equals(o *Set) bool { + return l.Equivalent() == o.Equivalent() +} + +// Encoded returns the encoded form of this set, according to encoder. +func (l *Set) Encoded(encoder Encoder) string { + if l == nil || encoder == nil { + return "" + } + + return encoder.Encode(l.Iter()) +} + +func empty() Set { + return Set{ + equivalent: emptySet.equivalent, + } +} + +// NewSet returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. +// +// Except for empty sets, this method adds an additional allocation compared +// with calls that include a Sortable. +func NewSet(kvs ...KeyValue) Set { + s, _ := NewSetWithFiltered(kvs, nil) + return s +} + +// NewSetWithSortable returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. +// +// This call includes a Sortable option as a memory optimization. +// +// Deprecated: Use [NewSet] instead. +func NewSetWithSortable(kvs []KeyValue, _ *Sortable) Set { + s, _ := NewSetWithFiltered(kvs, nil) + return s +} + +// NewSetWithFiltered returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. +// +// This call includes a Filter to include/exclude attribute keys from the +// return value. Excluded keys are returned as a slice of attribute values. +func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { + // Check for empty set. + if len(kvs) == 0 { + return empty(), nil + } + + // Stable sort so the following de-duplication can implement + // last-value-wins semantics. + slices.SortStableFunc(kvs, func(a, b KeyValue) int { + return cmp.Compare(a.Key, b.Key) + }) + + position := len(kvs) - 1 + offset := position - 1 + + // The requirements stated above require that the stable + // result be placed in the end of the input slice, while + // overwritten values are swapped to the beginning. + // + // De-duplicate with last-value-wins semantics. Preserve + // duplicate values at the beginning of the input slice. + for ; offset >= 0; offset-- { + if kvs[offset].Key == kvs[position].Key { + continue + } + position-- + kvs[offset], kvs[position] = kvs[position], kvs[offset] + } + kvs = kvs[position:] + + if filter != nil { + if div := filteredToFront(kvs, filter); div != 0 { + return Set{equivalent: computeDistinct(kvs[div:])}, kvs[:div] + } + } + return Set{equivalent: computeDistinct(kvs)}, nil +} + +// NewSetWithSortableFiltered returns a new Set. +// +// Duplicate keys are eliminated by taking the last value. This +// re-orders the input slice so that unique last-values are contiguous +// at the end of the slice. +// +// This ensures the following: +// +// - Last-value-wins semantics +// - Caller sees the reordering, but doesn't lose values +// - Repeated call preserve last-value wins. +// +// Note that methods are defined on Set, although this returns Set. Callers +// can avoid memory allocations by: +// +// - allocating a Sortable for use as a temporary in this method +// - allocating a Set for storing the return value of this constructor. +// +// The result maintains a cache of encoded attributes, by attribute.EncoderID. +// This value should not be copied after its first use. +// +// The second []KeyValue return value is a list of attributes that were +// excluded by the Filter (if non-nil). +// +// Deprecated: Use [NewSetWithFiltered] instead. +func NewSetWithSortableFiltered(kvs []KeyValue, _ *Sortable, filter Filter) (Set, []KeyValue) { + return NewSetWithFiltered(kvs, filter) +} + +// filteredToFront filters slice in-place using keep function. All KeyValues that need to +// be removed are moved to the front. All KeyValues that need to be kept are +// moved (in-order) to the back. The index for the first KeyValue to be kept is +// returned. +func filteredToFront(slice []KeyValue, keep Filter) int { + n := len(slice) + j := n + for i := n - 1; i >= 0; i-- { + if keep(slice[i]) { + j-- + slice[i], slice[j] = slice[j], slice[i] + } + } + return j +} + +// Filter returns a filtered copy of this Set. See the documentation for +// NewSetWithSortableFiltered for more details. +func (l *Set) Filter(re Filter) (Set, []KeyValue) { + if re == nil { + return *l, nil + } + + // Iterate in reverse to the first attribute that will be filtered out. + n := l.Len() + first := n - 1 + for ; first >= 0; first-- { + kv, _ := l.Get(first) + if !re(kv) { + break + } + } + + // No attributes will be dropped, return the immutable Set l and nil. + if first < 0 { + return *l, nil + } + + // Copy now that we know we need to return a modified set. + // + // Do not do this in-place on the underlying storage of *Set l. Sets are + // immutable and filtering should not change this. + slice := l.ToSlice() + + // Don't re-iterate the slice if only slice[0] is filtered. + if first == 0 { + // It is safe to assume len(slice) >= 1 given we found at least one + // attribute above that needs to be filtered out. + return Set{equivalent: computeDistinct(slice[1:])}, slice[:1] + } + + // Move the filtered slice[first] to the front (preserving order). + kv := slice[first] + copy(slice[1:first+1], slice[:first]) + slice[0] = kv + + // Do not re-evaluate re(slice[first+1:]). + div := filteredToFront(slice[1:first+1], re) + 1 + return Set{equivalent: computeDistinct(slice[div:])}, slice[:div] +} + +// computeDistinct returns a Distinct using either the fixed- or +// reflect-oriented code path, depending on the size of the input. The input +// slice is assumed to already be sorted and de-duplicated. +func computeDistinct(kvs []KeyValue) Distinct { + iface := computeDistinctFixed(kvs) + if iface == nil { + iface = computeDistinctReflect(kvs) + } + return Distinct{ + iface: iface, + } +} + +// computeDistinctFixed computes a Distinct for small slices. It returns nil +// if the input is too large for this code path. +func computeDistinctFixed(kvs []KeyValue) interface{} { + switch len(kvs) { + case 1: + ptr := new([1]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 2: + ptr := new([2]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 3: + ptr := new([3]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 4: + ptr := new([4]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 5: + ptr := new([5]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 6: + ptr := new([6]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 7: + ptr := new([7]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 8: + ptr := new([8]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 9: + ptr := new([9]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 10: + ptr := new([10]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + default: + return nil + } +} + +// computeDistinctReflect computes a Distinct using reflection, works for any +// size input. +func computeDistinctReflect(kvs []KeyValue) interface{} { + at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() + for i, keyValue := range kvs { + *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue + } + return at.Interface() +} + +// MarshalJSON returns the JSON encoding of the Set. +func (l *Set) MarshalJSON() ([]byte, error) { + return json.Marshal(l.equivalent.iface) +} + +// MarshalLog is the marshaling function used by the logging system to represent this Set. +func (l Set) MarshalLog() interface{} { + kvs := make(map[string]string) + for _, kv := range l.ToSlice() { + kvs[string(kv.Key)] = kv.Value.Emit() + } + return kvs +} + +// Len implements sort.Interface. +func (l *Sortable) Len() int { + return len(*l) +} + +// Swap implements sort.Interface. +func (l *Sortable) Swap(i, j int) { + (*l)[i], (*l)[j] = (*l)[j], (*l)[i] +} + +// Less implements sort.Interface. +func (l *Sortable) Less(i, j int) bool { + return (*l)[i].Key < (*l)[j].Key +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/type_string.go b/vendor/go.opentelemetry.io/otel/attribute/type_string.go new file mode 100644 index 0000000..e584b24 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/type_string.go @@ -0,0 +1,31 @@ +// Code generated by "stringer -type=Type"; DO NOT EDIT. + +package attribute + +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[INVALID-0] + _ = x[BOOL-1] + _ = x[INT64-2] + _ = x[FLOAT64-3] + _ = x[STRING-4] + _ = x[BOOLSLICE-5] + _ = x[INT64SLICE-6] + _ = x[FLOAT64SLICE-7] + _ = x[STRINGSLICE-8] +} + +const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE" + +var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/value.go b/vendor/go.opentelemetry.io/otel/attribute/value.go new file mode 100644 index 0000000..9ea0ecb --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -0,0 +1,271 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "go.opentelemetry.io/otel/internal" + "go.opentelemetry.io/otel/internal/attribute" +) + +//go:generate stringer -type=Type + +// Type describes the type of the data Value holds. +type Type int // nolint: revive // redefines builtin Type. + +// Value represents the value part in key-value pairs. +type Value struct { + vtype Type + numeric uint64 + stringly string + slice interface{} +} + +const ( + // INVALID is used for a Value with no value set. + INVALID Type = iota + // BOOL is a boolean Type Value. + BOOL + // INT64 is a 64-bit signed integral Type Value. + INT64 + // FLOAT64 is a 64-bit floating point Type Value. + FLOAT64 + // STRING is a string Type Value. + STRING + // BOOLSLICE is a slice of booleans Type Value. + BOOLSLICE + // INT64SLICE is a slice of 64-bit signed integral numbers Type Value. + INT64SLICE + // FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value. + FLOAT64SLICE + // STRINGSLICE is a slice of strings Type Value. + STRINGSLICE +) + +// BoolValue creates a BOOL Value. +func BoolValue(v bool) Value { + return Value{ + vtype: BOOL, + numeric: internal.BoolToRaw(v), + } +} + +// BoolSliceValue creates a BOOLSLICE Value. +func BoolSliceValue(v []bool) Value { + return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)} +} + +// IntValue creates an INT64 Value. +func IntValue(v int) Value { + return Int64Value(int64(v)) +} + +// IntSliceValue creates an INTSLICE Value. +func IntSliceValue(v []int) Value { + var int64Val int64 + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(int64Val))) + for i, val := range v { + cp.Elem().Index(i).SetInt(int64(val)) + } + return Value{ + vtype: INT64SLICE, + slice: cp.Elem().Interface(), + } +} + +// Int64Value creates an INT64 Value. +func Int64Value(v int64) Value { + return Value{ + vtype: INT64, + numeric: internal.Int64ToRaw(v), + } +} + +// Int64SliceValue creates an INT64SLICE Value. +func Int64SliceValue(v []int64) Value { + return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)} +} + +// Float64Value creates a FLOAT64 Value. +func Float64Value(v float64) Value { + return Value{ + vtype: FLOAT64, + numeric: internal.Float64ToRaw(v), + } +} + +// Float64SliceValue creates a FLOAT64SLICE Value. +func Float64SliceValue(v []float64) Value { + return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)} +} + +// StringValue creates a STRING Value. +func StringValue(v string) Value { + return Value{ + vtype: STRING, + stringly: v, + } +} + +// StringSliceValue creates a STRINGSLICE Value. +func StringSliceValue(v []string) Value { + return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)} +} + +// Type returns a type of the Value. +func (v Value) Type() Type { + return v.vtype +} + +// AsBool returns the bool value. Make sure that the Value's type is +// BOOL. +func (v Value) AsBool() bool { + return internal.RawToBool(v.numeric) +} + +// AsBoolSlice returns the []bool value. Make sure that the Value's type is +// BOOLSLICE. +func (v Value) AsBoolSlice() []bool { + if v.vtype != BOOLSLICE { + return nil + } + return v.asBoolSlice() +} + +func (v Value) asBoolSlice() []bool { + return attribute.AsBoolSlice(v.slice) +} + +// AsInt64 returns the int64 value. Make sure that the Value's type is +// INT64. +func (v Value) AsInt64() int64 { + return internal.RawToInt64(v.numeric) +} + +// AsInt64Slice returns the []int64 value. Make sure that the Value's type is +// INT64SLICE. +func (v Value) AsInt64Slice() []int64 { + if v.vtype != INT64SLICE { + return nil + } + return v.asInt64Slice() +} + +func (v Value) asInt64Slice() []int64 { + return attribute.AsInt64Slice(v.slice) +} + +// AsFloat64 returns the float64 value. Make sure that the Value's +// type is FLOAT64. +func (v Value) AsFloat64() float64 { + return internal.RawToFloat64(v.numeric) +} + +// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is +// FLOAT64SLICE. +func (v Value) AsFloat64Slice() []float64 { + if v.vtype != FLOAT64SLICE { + return nil + } + return v.asFloat64Slice() +} + +func (v Value) asFloat64Slice() []float64 { + return attribute.AsFloat64Slice(v.slice) +} + +// AsString returns the string value. Make sure that the Value's type +// is STRING. +func (v Value) AsString() string { + return v.stringly +} + +// AsStringSlice returns the []string value. Make sure that the Value's type is +// STRINGSLICE. +func (v Value) AsStringSlice() []string { + if v.vtype != STRINGSLICE { + return nil + } + return v.asStringSlice() +} + +func (v Value) asStringSlice() []string { + return attribute.AsStringSlice(v.slice) +} + +type unknownValueType struct{} + +// AsInterface returns Value's data as interface{}. +func (v Value) AsInterface() interface{} { + switch v.Type() { + case BOOL: + return v.AsBool() + case BOOLSLICE: + return v.asBoolSlice() + case INT64: + return v.AsInt64() + case INT64SLICE: + return v.asInt64Slice() + case FLOAT64: + return v.AsFloat64() + case FLOAT64SLICE: + return v.asFloat64Slice() + case STRING: + return v.stringly + case STRINGSLICE: + return v.asStringSlice() + } + return unknownValueType{} +} + +// Emit returns a string representation of Value's data. +func (v Value) Emit() string { + switch v.Type() { + case BOOLSLICE: + return fmt.Sprint(v.asBoolSlice()) + case BOOL: + return strconv.FormatBool(v.AsBool()) + case INT64SLICE: + j, err := json.Marshal(v.asInt64Slice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asInt64Slice()) + } + return string(j) + case INT64: + return strconv.FormatInt(v.AsInt64(), 10) + case FLOAT64SLICE: + j, err := json.Marshal(v.asFloat64Slice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asFloat64Slice()) + } + return string(j) + case FLOAT64: + return fmt.Sprint(v.AsFloat64()) + case STRINGSLICE: + j, err := json.Marshal(v.asStringSlice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asStringSlice()) + } + return string(j) + case STRING: + return v.stringly + default: + return "unknown" + } +} + +// MarshalJSON returns the JSON encoding of the Value. +func (v Value) MarshalJSON() ([]byte, error) { + var jsonVal struct { + Type string + Value interface{} + } + jsonVal.Type = v.Type().String() + jsonVal.Value = v.AsInterface() + return json.Marshal(jsonVal) +} diff --git a/vendor/go.opentelemetry.io/otel/internal/attribute/attribute.go b/vendor/go.opentelemetry.io/otel/internal/attribute/attribute.go new file mode 100644 index 0000000..f32766e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/attribute/attribute.go @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +/* +Package attribute provide several helper functions for some commonly used +logic of processing attributes. +*/ +package attribute // import "go.opentelemetry.io/otel/internal/attribute" + +import ( + "reflect" +) + +// BoolSliceValue converts a bool slice into an array with same elements as slice. +func BoolSliceValue(v []bool) interface{} { + var zero bool + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) + copy(cp.Elem().Slice(0, len(v)).Interface().([]bool), v) + return cp.Elem().Interface() +} + +// Int64SliceValue converts an int64 slice into an array with same elements as slice. +func Int64SliceValue(v []int64) interface{} { + var zero int64 + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) + copy(cp.Elem().Slice(0, len(v)).Interface().([]int64), v) + return cp.Elem().Interface() +} + +// Float64SliceValue converts a float64 slice into an array with same elements as slice. +func Float64SliceValue(v []float64) interface{} { + var zero float64 + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) + copy(cp.Elem().Slice(0, len(v)).Interface().([]float64), v) + return cp.Elem().Interface() +} + +// StringSliceValue converts a string slice into an array with same elements as slice. +func StringSliceValue(v []string) interface{} { + var zero string + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) + copy(cp.Elem().Slice(0, len(v)).Interface().([]string), v) + return cp.Elem().Interface() +} + +// AsBoolSlice converts a bool array into a slice into with same elements as array. +func AsBoolSlice(v interface{}) []bool { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Array { + return nil + } + var zero bool + correctLen := rv.Len() + correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) + cpy := reflect.New(correctType) + _ = reflect.Copy(cpy.Elem(), rv) + return cpy.Elem().Slice(0, correctLen).Interface().([]bool) +} + +// AsInt64Slice converts an int64 array into a slice into with same elements as array. +func AsInt64Slice(v interface{}) []int64 { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Array { + return nil + } + var zero int64 + correctLen := rv.Len() + correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) + cpy := reflect.New(correctType) + _ = reflect.Copy(cpy.Elem(), rv) + return cpy.Elem().Slice(0, correctLen).Interface().([]int64) +} + +// AsFloat64Slice converts a float64 array into a slice into with same elements as array. +func AsFloat64Slice(v interface{}) []float64 { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Array { + return nil + } + var zero float64 + correctLen := rv.Len() + correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) + cpy := reflect.New(correctType) + _ = reflect.Copy(cpy.Elem(), rv) + return cpy.Elem().Slice(0, correctLen).Interface().([]float64) +} + +// AsStringSlice converts a string array into a slice into with same elements as array. +func AsStringSlice(v interface{}) []string { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Array { + return nil + } + var zero string + correctLen := rv.Len() + correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) + cpy := reflect.New(correctType) + _ = reflect.Copy(cpy.Elem(), rv) + return cpy.Elem().Slice(0, correctLen).Interface().([]string) +} diff --git a/vendor/go.opentelemetry.io/otel/internal/gen.go b/vendor/go.opentelemetry.io/otel/internal/gen.go new file mode 100644 index 0000000..4259f03 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/gen.go @@ -0,0 +1,18 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/otel/internal" + +//go:generate gotmpl --body=./shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go +//go:generate gotmpl --body=./shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go +//go:generate gotmpl --body=./shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go + +//go:generate gotmpl --body=./shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go +//go:generate gotmpl --body=./shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go +//go:generate gotmpl --body=./shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go +//go:generate gotmpl --body=./shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go +//go:generate gotmpl --body=./shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/internal/matchers\"}" --out=internaltest/harness.go +//go:generate gotmpl --body=./shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go +//go:generate gotmpl --body=./shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go +//go:generate gotmpl --body=./shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go +//go:generate gotmpl --body=./shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go diff --git a/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go new file mode 100644 index 0000000..3e7bb3b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/otel/internal" + +import ( + "math" + "unsafe" +) + +func BoolToRaw(b bool) uint64 { // nolint:revive // b is not a control flag. + if b { + return 1 + } + return 0 +} + +func RawToBool(r uint64) bool { + return r != 0 +} + +func Int64ToRaw(i int64) uint64 { + return uint64(i) +} + +func RawToInt64(r uint64) int64 { + return int64(r) +} + +func Float64ToRaw(f float64) uint64 { + return math.Float64bits(f) +} + +func RawToFloat64(r uint64) float64 { + return math.Float64frombits(r) +} + +func RawPtrToFloat64Ptr(r *uint64) *float64 { + return (*float64)(unsafe.Pointer(r)) +} + +func RawPtrToInt64Ptr(r *uint64) *int64 { + return (*int64)(unsafe.Pointer(r)) +} diff --git a/vendor/go.opentelemetry.io/otel/metric/LICENSE b/vendor/go.opentelemetry.io/otel/metric/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/LICENSE @@ -0,0 +1,201 @@ + 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/go.opentelemetry.io/otel/metric/README.md b/vendor/go.opentelemetry.io/otel/metric/README.md new file mode 100644 index 0000000..0cf902e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/README.md @@ -0,0 +1,3 @@ +# Metric API + +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/metric)](https://pkg.go.dev/go.opentelemetry.io/otel/metric) diff --git a/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go b/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go new file mode 100644 index 0000000..cf23db7 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go @@ -0,0 +1,260 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import ( + "context" + + "go.opentelemetry.io/otel/metric/embedded" +) + +// Float64Observable describes a set of instruments used asynchronously to +// record float64 measurements once per collection cycle. Observations of +// these instruments are only made within a callback. +// +// Warning: Methods may be added to this interface in minor releases. +type Float64Observable interface { + Observable + + float64Observable() +} + +// Float64ObservableCounter is an instrument used to asynchronously record +// increasing float64 measurements once per collection cycle. Observations are +// only made within a callback for this instrument. The value observed is +// assumed the to be the cumulative sum of the count. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for +// unimplemented methods. +type Float64ObservableCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64ObservableCounter + + Float64Observable +} + +// Float64ObservableCounterConfig contains options for asynchronous counter +// instruments that record float64 values. +type Float64ObservableCounterConfig struct { + description string + unit string + callbacks []Float64Callback +} + +// NewFloat64ObservableCounterConfig returns a new +// [Float64ObservableCounterConfig] with all opts applied. +func NewFloat64ObservableCounterConfig(opts ...Float64ObservableCounterOption) Float64ObservableCounterConfig { + var config Float64ObservableCounterConfig + for _, o := range opts { + config = o.applyFloat64ObservableCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Float64ObservableCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64ObservableCounterConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Float64ObservableCounterConfig) Callbacks() []Float64Callback { + return c.callbacks +} + +// Float64ObservableCounterOption applies options to a +// [Float64ObservableCounterConfig]. See [Float64ObservableOption] and +// [InstrumentOption] for other options that can be used as a +// Float64ObservableCounterOption. +type Float64ObservableCounterOption interface { + applyFloat64ObservableCounter(Float64ObservableCounterConfig) Float64ObservableCounterConfig +} + +// Float64ObservableUpDownCounter is an instrument used to asynchronously +// record float64 measurements once per collection cycle. Observations are only +// made within a callback for this instrument. The value observed is assumed +// the to be the cumulative sum of the count. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64ObservableUpDownCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64ObservableUpDownCounter + + Float64Observable +} + +// Float64ObservableUpDownCounterConfig contains options for asynchronous +// counter instruments that record float64 values. +type Float64ObservableUpDownCounterConfig struct { + description string + unit string + callbacks []Float64Callback +} + +// NewFloat64ObservableUpDownCounterConfig returns a new +// [Float64ObservableUpDownCounterConfig] with all opts applied. +func NewFloat64ObservableUpDownCounterConfig(opts ...Float64ObservableUpDownCounterOption) Float64ObservableUpDownCounterConfig { + var config Float64ObservableUpDownCounterConfig + for _, o := range opts { + config = o.applyFloat64ObservableUpDownCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Float64ObservableUpDownCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64ObservableUpDownCounterConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Float64ObservableUpDownCounterConfig) Callbacks() []Float64Callback { + return c.callbacks +} + +// Float64ObservableUpDownCounterOption applies options to a +// [Float64ObservableUpDownCounterConfig]. See [Float64ObservableOption] and +// [InstrumentOption] for other options that can be used as a +// Float64ObservableUpDownCounterOption. +type Float64ObservableUpDownCounterOption interface { + applyFloat64ObservableUpDownCounter(Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig +} + +// Float64ObservableGauge is an instrument used to asynchronously record +// instantaneous float64 measurements once per collection cycle. Observations +// are only made within a callback for this instrument. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64ObservableGauge interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64ObservableGauge + + Float64Observable +} + +// Float64ObservableGaugeConfig contains options for asynchronous counter +// instruments that record float64 values. +type Float64ObservableGaugeConfig struct { + description string + unit string + callbacks []Float64Callback +} + +// NewFloat64ObservableGaugeConfig returns a new [Float64ObservableGaugeConfig] +// with all opts applied. +func NewFloat64ObservableGaugeConfig(opts ...Float64ObservableGaugeOption) Float64ObservableGaugeConfig { + var config Float64ObservableGaugeConfig + for _, o := range opts { + config = o.applyFloat64ObservableGauge(config) + } + return config +} + +// Description returns the configured description. +func (c Float64ObservableGaugeConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64ObservableGaugeConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Float64ObservableGaugeConfig) Callbacks() []Float64Callback { + return c.callbacks +} + +// Float64ObservableGaugeOption applies options to a +// [Float64ObservableGaugeConfig]. See [Float64ObservableOption] and +// [InstrumentOption] for other options that can be used as a +// Float64ObservableGaugeOption. +type Float64ObservableGaugeOption interface { + applyFloat64ObservableGauge(Float64ObservableGaugeConfig) Float64ObservableGaugeConfig +} + +// Float64Observer is a recorder of float64 measurements. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64Observer interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64Observer + + // Observe records the float64 value. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Observe(value float64, options ...ObserveOption) +} + +// Float64Callback is a function registered with a Meter that makes +// observations for a Float64Observerable instrument it is registered with. +// Calls to the Float64Observer record measurement values for the +// Float64Observable. +// +// The function needs to complete in a finite amount of time and the deadline +// of the passed context is expected to be honored. +// +// The function needs to make unique observations across all registered +// Float64Callbacks. Meaning, it should not report measurements with the same +// attributes as another Float64Callbacks also registered for the same +// instrument. +// +// The function needs to be concurrent safe. +type Float64Callback func(context.Context, Float64Observer) error + +// Float64ObservableOption applies options to float64 Observer instruments. +type Float64ObservableOption interface { + Float64ObservableCounterOption + Float64ObservableUpDownCounterOption + Float64ObservableGaugeOption +} + +type float64CallbackOpt struct { + cback Float64Callback +} + +func (o float64CallbackOpt) applyFloat64ObservableCounter(cfg Float64ObservableCounterConfig) Float64ObservableCounterConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +func (o float64CallbackOpt) applyFloat64ObservableUpDownCounter(cfg Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +func (o float64CallbackOpt) applyFloat64ObservableGauge(cfg Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +// WithFloat64Callback adds callback to be called for an instrument. +func WithFloat64Callback(callback Float64Callback) Float64ObservableOption { + return float64CallbackOpt{callback} +} diff --git a/vendor/go.opentelemetry.io/otel/metric/asyncint64.go b/vendor/go.opentelemetry.io/otel/metric/asyncint64.go new file mode 100644 index 0000000..c82ba53 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/asyncint64.go @@ -0,0 +1,258 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import ( + "context" + + "go.opentelemetry.io/otel/metric/embedded" +) + +// Int64Observable describes a set of instruments used asynchronously to record +// int64 measurements once per collection cycle. Observations of these +// instruments are only made within a callback. +// +// Warning: Methods may be added to this interface in minor releases. +type Int64Observable interface { + Observable + + int64Observable() +} + +// Int64ObservableCounter is an instrument used to asynchronously record +// increasing int64 measurements once per collection cycle. Observations are +// only made within a callback for this instrument. The value observed is +// assumed the to be the cumulative sum of the count. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64ObservableCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64ObservableCounter + + Int64Observable +} + +// Int64ObservableCounterConfig contains options for asynchronous counter +// instruments that record int64 values. +type Int64ObservableCounterConfig struct { + description string + unit string + callbacks []Int64Callback +} + +// NewInt64ObservableCounterConfig returns a new [Int64ObservableCounterConfig] +// with all opts applied. +func NewInt64ObservableCounterConfig(opts ...Int64ObservableCounterOption) Int64ObservableCounterConfig { + var config Int64ObservableCounterConfig + for _, o := range opts { + config = o.applyInt64ObservableCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Int64ObservableCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64ObservableCounterConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Int64ObservableCounterConfig) Callbacks() []Int64Callback { + return c.callbacks +} + +// Int64ObservableCounterOption applies options to a +// [Int64ObservableCounterConfig]. See [Int64ObservableOption] and +// [InstrumentOption] for other options that can be used as an +// Int64ObservableCounterOption. +type Int64ObservableCounterOption interface { + applyInt64ObservableCounter(Int64ObservableCounterConfig) Int64ObservableCounterConfig +} + +// Int64ObservableUpDownCounter is an instrument used to asynchronously record +// int64 measurements once per collection cycle. Observations are only made +// within a callback for this instrument. The value observed is assumed the to +// be the cumulative sum of the count. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64ObservableUpDownCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64ObservableUpDownCounter + + Int64Observable +} + +// Int64ObservableUpDownCounterConfig contains options for asynchronous counter +// instruments that record int64 values. +type Int64ObservableUpDownCounterConfig struct { + description string + unit string + callbacks []Int64Callback +} + +// NewInt64ObservableUpDownCounterConfig returns a new +// [Int64ObservableUpDownCounterConfig] with all opts applied. +func NewInt64ObservableUpDownCounterConfig(opts ...Int64ObservableUpDownCounterOption) Int64ObservableUpDownCounterConfig { + var config Int64ObservableUpDownCounterConfig + for _, o := range opts { + config = o.applyInt64ObservableUpDownCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Int64ObservableUpDownCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64ObservableUpDownCounterConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Int64ObservableUpDownCounterConfig) Callbacks() []Int64Callback { + return c.callbacks +} + +// Int64ObservableUpDownCounterOption applies options to a +// [Int64ObservableUpDownCounterConfig]. See [Int64ObservableOption] and +// [InstrumentOption] for other options that can be used as an +// Int64ObservableUpDownCounterOption. +type Int64ObservableUpDownCounterOption interface { + applyInt64ObservableUpDownCounter(Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig +} + +// Int64ObservableGauge is an instrument used to asynchronously record +// instantaneous int64 measurements once per collection cycle. Observations are +// only made within a callback for this instrument. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64ObservableGauge interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64ObservableGauge + + Int64Observable +} + +// Int64ObservableGaugeConfig contains options for asynchronous counter +// instruments that record int64 values. +type Int64ObservableGaugeConfig struct { + description string + unit string + callbacks []Int64Callback +} + +// NewInt64ObservableGaugeConfig returns a new [Int64ObservableGaugeConfig] +// with all opts applied. +func NewInt64ObservableGaugeConfig(opts ...Int64ObservableGaugeOption) Int64ObservableGaugeConfig { + var config Int64ObservableGaugeConfig + for _, o := range opts { + config = o.applyInt64ObservableGauge(config) + } + return config +} + +// Description returns the configured description. +func (c Int64ObservableGaugeConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64ObservableGaugeConfig) Unit() string { + return c.unit +} + +// Callbacks returns the configured callbacks. +func (c Int64ObservableGaugeConfig) Callbacks() []Int64Callback { + return c.callbacks +} + +// Int64ObservableGaugeOption applies options to a +// [Int64ObservableGaugeConfig]. See [Int64ObservableOption] and +// [InstrumentOption] for other options that can be used as an +// Int64ObservableGaugeOption. +type Int64ObservableGaugeOption interface { + applyInt64ObservableGauge(Int64ObservableGaugeConfig) Int64ObservableGaugeConfig +} + +// Int64Observer is a recorder of int64 measurements. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64Observer interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64Observer + + // Observe records the int64 value. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Observe(value int64, options ...ObserveOption) +} + +// Int64Callback is a function registered with a Meter that makes observations +// for an Int64Observerable instrument it is registered with. Calls to the +// Int64Observer record measurement values for the Int64Observable. +// +// The function needs to complete in a finite amount of time and the deadline +// of the passed context is expected to be honored. +// +// The function needs to make unique observations across all registered +// Int64Callbacks. Meaning, it should not report measurements with the same +// attributes as another Int64Callbacks also registered for the same +// instrument. +// +// The function needs to be concurrent safe. +type Int64Callback func(context.Context, Int64Observer) error + +// Int64ObservableOption applies options to int64 Observer instruments. +type Int64ObservableOption interface { + Int64ObservableCounterOption + Int64ObservableUpDownCounterOption + Int64ObservableGaugeOption +} + +type int64CallbackOpt struct { + cback Int64Callback +} + +func (o int64CallbackOpt) applyInt64ObservableCounter(cfg Int64ObservableCounterConfig) Int64ObservableCounterConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +func (o int64CallbackOpt) applyInt64ObservableUpDownCounter(cfg Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +func (o int64CallbackOpt) applyInt64ObservableGauge(cfg Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { + cfg.callbacks = append(cfg.callbacks, o.cback) + return cfg +} + +// WithInt64Callback adds callback to be called for an instrument. +func WithInt64Callback(callback Int64Callback) Int64ObservableOption { + return int64CallbackOpt{callback} +} diff --git a/vendor/go.opentelemetry.io/otel/metric/config.go b/vendor/go.opentelemetry.io/otel/metric/config.go new file mode 100644 index 0000000..d9e3b13 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/config.go @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import "go.opentelemetry.io/otel/attribute" + +// MeterConfig contains options for Meters. +type MeterConfig struct { + instrumentationVersion string + schemaURL string + attrs attribute.Set + + // Ensure forward compatibility by explicitly making this not comparable. + noCmp [0]func() //nolint: unused // This is indeed used. +} + +// InstrumentationVersion returns the version of the library providing +// instrumentation. +func (cfg MeterConfig) InstrumentationVersion() string { + return cfg.instrumentationVersion +} + +// InstrumentationAttributes returns the attributes associated with the library +// providing instrumentation. +func (cfg MeterConfig) InstrumentationAttributes() attribute.Set { + return cfg.attrs +} + +// SchemaURL is the schema_url of the library providing instrumentation. +func (cfg MeterConfig) SchemaURL() string { + return cfg.schemaURL +} + +// MeterOption is an interface for applying Meter options. +type MeterOption interface { + // applyMeter is used to set a MeterOption value of a MeterConfig. + applyMeter(MeterConfig) MeterConfig +} + +// NewMeterConfig creates a new MeterConfig and applies +// all the given options. +func NewMeterConfig(opts ...MeterOption) MeterConfig { + var config MeterConfig + for _, o := range opts { + config = o.applyMeter(config) + } + return config +} + +type meterOptionFunc func(MeterConfig) MeterConfig + +func (fn meterOptionFunc) applyMeter(cfg MeterConfig) MeterConfig { + return fn(cfg) +} + +// WithInstrumentationVersion sets the instrumentation version. +func WithInstrumentationVersion(version string) MeterOption { + return meterOptionFunc(func(config MeterConfig) MeterConfig { + config.instrumentationVersion = version + return config + }) +} + +// WithInstrumentationAttributes sets the instrumentation attributes. +// +// The passed attributes will be de-duplicated. +func WithInstrumentationAttributes(attr ...attribute.KeyValue) MeterOption { + return meterOptionFunc(func(config MeterConfig) MeterConfig { + config.attrs = attribute.NewSet(attr...) + return config + }) +} + +// WithSchemaURL sets the schema URL. +func WithSchemaURL(schemaURL string) MeterOption { + return meterOptionFunc(func(config MeterConfig) MeterConfig { + config.schemaURL = schemaURL + return config + }) +} diff --git a/vendor/go.opentelemetry.io/otel/metric/doc.go b/vendor/go.opentelemetry.io/otel/metric/doc.go new file mode 100644 index 0000000..075234b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/doc.go @@ -0,0 +1,159 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +/* +Package metric provides the OpenTelemetry API used to measure metrics about +source code operation. + +This API is separate from its implementation so the instrumentation built from +it is reusable. See [go.opentelemetry.io/otel/sdk/metric] for the official +OpenTelemetry implementation of this API. + +All measurements made with this package are made via instruments. These +instruments are created by a [Meter] which itself is created by a +[MeterProvider]. Applications need to accept a [MeterProvider] implementation +as a starting point when instrumenting. This can be done directly, or by using +the OpenTelemetry global MeterProvider via [GetMeterProvider]. Using an +appropriately named [Meter] from the accepted [MeterProvider], instrumentation +can then be built from the [Meter]'s instruments. + +# Instruments + +Each instrument is designed to make measurements of a particular type. Broadly, +all instruments fall into two overlapping logical categories: asynchronous or +synchronous, and int64 or float64. + +All synchronous instruments ([Int64Counter], [Int64UpDownCounter], +[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and +[Float64Histogram]) are used to measure the operation and performance of source +code during the source code execution. These instruments only make measurements +when the source code they instrument is run. + +All asynchronous instruments ([Int64ObservableCounter], +[Int64ObservableUpDownCounter], [Int64ObservableGauge], +[Float64ObservableCounter], [Float64ObservableUpDownCounter], and +[Float64ObservableGauge]) are used to measure metrics outside of the execution +of source code. They are said to make "observations" via a callback function +called once every measurement collection cycle. + +Each instrument is also grouped by the value type it measures. Either int64 or +float64. The value being measured will dictate which instrument in these +categories to use. + +Outside of these two broad categories, instruments are described by the +function they are designed to serve. All Counters ([Int64Counter], +[Float64Counter], [Int64ObservableCounter], and [Float64ObservableCounter]) are +designed to measure values that never decrease in value, but instead only +incrementally increase in value. UpDownCounters ([Int64UpDownCounter], +[Float64UpDownCounter], [Int64ObservableUpDownCounter], and +[Float64ObservableUpDownCounter]) on the other hand, are designed to measure +values that can increase and decrease. When more information needs to be +conveyed about all the synchronous measurements made during a collection cycle, +a Histogram ([Int64Histogram] and [Float64Histogram]) should be used. Finally, +when just the most recent measurement needs to be conveyed about an +asynchronous measurement, a Gauge ([Int64ObservableGauge] and +[Float64ObservableGauge]) should be used. + +See the [OpenTelemetry documentation] for more information about instruments +and their intended use. + +# Measurements + +Measurements are made by recording values and information about the values with +an instrument. How these measurements are recorded depends on the instrument. + +Measurements for synchronous instruments ([Int64Counter], [Int64UpDownCounter], +[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and +[Float64Histogram]) are recorded using the instrument methods directly. All +counter instruments have an Add method that is used to measure an increment +value, and all histogram instruments have a Record method to measure a data +point. + +Asynchronous instruments ([Int64ObservableCounter], +[Int64ObservableUpDownCounter], [Int64ObservableGauge], +[Float64ObservableCounter], [Float64ObservableUpDownCounter], and +[Float64ObservableGauge]) record measurements within a callback function. The +callback is registered with the Meter which ensures the callback is called once +per collection cycle. A callback can be registered two ways: during the +instrument's creation using an option, or later using the RegisterCallback +method of the [Meter] that created the instrument. + +If the following criteria are met, an option ([WithInt64Callback] or +[WithFloat64Callback]) can be used during the asynchronous instrument's +creation to register a callback ([Int64Callback] or [Float64Callback], +respectively): + + - The measurement process is known when the instrument is created + - Only that instrument will make a measurement within the callback + - The callback never needs to be unregistered + +If the criteria are not met, use the RegisterCallback method of the [Meter] that +created the instrument to register a [Callback]. + +# API Implementations + +This package does not conform to the standard Go versioning policy, all of its +interfaces may have methods added to them without a package major version bump. +This non-standard API evolution could surprise an uninformed implementation +author. They could unknowingly build their implementation in a way that would +result in a runtime panic for their users that update to the new API. + +The API is designed to help inform an instrumentation author about this +non-standard API evolution. It requires them to choose a default behavior for +unimplemented interface methods. There are three behavior choices they can +make: + + - Compilation failure + - Panic + - Default to another implementation + +All interfaces in this API embed a corresponding interface from +[go.opentelemetry.io/otel/metric/embedded]. If an author wants the default +behavior of their implementations to be a compilation failure, signaling to +their users they need to update to the latest version of that implementation, +they need to embed the corresponding interface from +[go.opentelemetry.io/otel/metric/embedded] in their implementation. For +example, + + import "go.opentelemetry.io/otel/metric/embedded" + + type MeterProvider struct { + embedded.MeterProvider + // ... + } + +If an author wants the default behavior of their implementations to a panic, +they need to embed the API interface directly. + + import "go.opentelemetry.io/otel/metric" + + type MeterProvider struct { + metric.MeterProvider + // ... + } + +This is not a recommended behavior as it could lead to publishing packages that +contain runtime panics when users update other package that use newer versions +of [go.opentelemetry.io/otel/metric]. + +Finally, an author can embed another implementation in theirs. The embedded +implementation will be used for methods not defined by the author. For example, +an author who wants to default to silently dropping the call can use +[go.opentelemetry.io/otel/metric/noop]: + + import "go.opentelemetry.io/otel/metric/noop" + + type MeterProvider struct { + noop.MeterProvider + // ... + } + +It is strongly recommended that authors only embed +[go.opentelemetry.io/otel/metric/noop] if they choose this default behavior. +That implementation is the only one OpenTelemetry authors can guarantee will +fully implement all the API interfaces when a user updates their API. + +[OpenTelemetry documentation]: https://opentelemetry.io/docs/concepts/signals/metrics/ +[GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider +*/ +package metric // import "go.opentelemetry.io/otel/metric" diff --git a/vendor/go.opentelemetry.io/otel/metric/embedded/README.md b/vendor/go.opentelemetry.io/otel/metric/embedded/README.md new file mode 100644 index 0000000..1f6e0ef --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/embedded/README.md @@ -0,0 +1,3 @@ +# Metric Embedded + +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/metric/embedded)](https://pkg.go.dev/go.opentelemetry.io/otel/metric/embedded) diff --git a/vendor/go.opentelemetry.io/otel/metric/embedded/embedded.go b/vendor/go.opentelemetry.io/otel/metric/embedded/embedded.go new file mode 100644 index 0000000..1a9dc68 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/embedded/embedded.go @@ -0,0 +1,243 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package embedded provides interfaces embedded within the [OpenTelemetry +// metric API]. +// +// Implementers of the [OpenTelemetry metric API] can embed the relevant type +// from this package into their implementation directly. Doing so will result +// in a compilation error for users when the [OpenTelemetry metric API] is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [OpenTelemetry metric API]: https://pkg.go.dev/go.opentelemetry.io/otel/metric +package embedded // import "go.opentelemetry.io/otel/metric/embedded" + +// MeterProvider is embedded in +// [go.opentelemetry.io/otel/metric.MeterProvider]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.MeterProvider] if you want users to +// experience a compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.MeterProvider] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type MeterProvider interface{ meterProvider() } + +// Meter is embedded in [go.opentelemetry.io/otel/metric.Meter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Meter] if you want users to experience a +// compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.Meter] interface +// is extended (which is something that can happen without a major version bump +// of the API package). +type Meter interface{ meter() } + +// Float64Observer is embedded in +// [go.opentelemetry.io/otel/metric.Float64Observer]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64Observer] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64Observer] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Float64Observer interface{ float64Observer() } + +// Int64Observer is embedded in +// [go.opentelemetry.io/otel/metric.Int64Observer]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64Observer] if you want users +// to experience a compilation error, signaling they need to update to your +// latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64Observer] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Int64Observer interface{ int64Observer() } + +// Observer is embedded in [go.opentelemetry.io/otel/metric.Observer]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Observer] if you want users to experience a +// compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.Observer] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Observer interface{ observer() } + +// Registration is embedded in [go.opentelemetry.io/otel/metric.Registration]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Registration] if you want users to +// experience a compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.Registration] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Registration interface{ registration() } + +// Float64Counter is embedded in +// [go.opentelemetry.io/otel/metric.Float64Counter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64Counter] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64Counter] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Float64Counter interface{ float64Counter() } + +// Float64Histogram is embedded in +// [go.opentelemetry.io/otel/metric.Float64Histogram]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64Histogram] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64Histogram] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Float64Histogram interface{ float64Histogram() } + +// Float64Gauge is embedded in [go.opentelemetry.io/otel/metric.Float64Gauge]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64Gauge] if you want users to +// experience a compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.Float64Gauge] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Float64Gauge interface{ float64Gauge() } + +// Float64ObservableCounter is embedded in +// [go.opentelemetry.io/otel/metric.Float64ObservableCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64ObservableCounter] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64ObservableCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Float64ObservableCounter interface{ float64ObservableCounter() } + +// Float64ObservableGauge is embedded in +// [go.opentelemetry.io/otel/metric.Float64ObservableGauge]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64ObservableGauge] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64ObservableGauge] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Float64ObservableGauge interface{ float64ObservableGauge() } + +// Float64ObservableUpDownCounter is embedded in +// [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter] +// if you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Float64ObservableUpDownCounter interface{ float64ObservableUpDownCounter() } + +// Float64UpDownCounter is embedded in +// [go.opentelemetry.io/otel/metric.Float64UpDownCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Float64UpDownCounter] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Float64UpDownCounter] interface +// is extended (which is something that can happen without a major version bump +// of the API package). +type Float64UpDownCounter interface{ float64UpDownCounter() } + +// Int64Counter is embedded in +// [go.opentelemetry.io/otel/metric.Int64Counter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64Counter] if you want users +// to experience a compilation error, signaling they need to update to your +// latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64Counter] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Int64Counter interface{ int64Counter() } + +// Int64Histogram is embedded in +// [go.opentelemetry.io/otel/metric.Int64Histogram]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64Histogram] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64Histogram] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Int64Histogram interface{ int64Histogram() } + +// Int64Gauge is embedded in [go.opentelemetry.io/otel/metric.Int64Gauge]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64Gauge] if you want users to experience +// a compilation error, signaling they need to update to your latest +// implementation, when the [go.opentelemetry.io/otel/metric.Int64Gauge] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Int64Gauge interface{ int64Gauge() } + +// Int64ObservableCounter is embedded in +// [go.opentelemetry.io/otel/metric.Int64ObservableCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64ObservableCounter] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64ObservableCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Int64ObservableCounter interface{ int64ObservableCounter() } + +// Int64ObservableGauge is embedded in +// [go.opentelemetry.io/otel/metric.Int64ObservableGauge]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64ObservableGauge] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64ObservableGauge] interface +// is extended (which is something that can happen without a major version bump +// of the API package). +type Int64ObservableGauge interface{ int64ObservableGauge() } + +// Int64ObservableUpDownCounter is embedded in +// [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter] if +// you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +type Int64ObservableUpDownCounter interface{ int64ObservableUpDownCounter() } + +// Int64UpDownCounter is embedded in +// [go.opentelemetry.io/otel/metric.Int64UpDownCounter]. +// +// Embed this interface in your implementation of the +// [go.opentelemetry.io/otel/metric.Int64UpDownCounter] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the +// [go.opentelemetry.io/otel/metric.Int64UpDownCounter] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +type Int64UpDownCounter interface{ int64UpDownCounter() } diff --git a/vendor/go.opentelemetry.io/otel/metric/instrument.go b/vendor/go.opentelemetry.io/otel/metric/instrument.go new file mode 100644 index 0000000..ea52e40 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/instrument.go @@ -0,0 +1,368 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import "go.opentelemetry.io/otel/attribute" + +// Observable is used as a grouping mechanism for all instruments that are +// updated within a Callback. +type Observable interface { + observable() +} + +// InstrumentOption applies options to all instruments. +type InstrumentOption interface { + Int64CounterOption + Int64UpDownCounterOption + Int64HistogramOption + Int64GaugeOption + Int64ObservableCounterOption + Int64ObservableUpDownCounterOption + Int64ObservableGaugeOption + + Float64CounterOption + Float64UpDownCounterOption + Float64HistogramOption + Float64GaugeOption + Float64ObservableCounterOption + Float64ObservableUpDownCounterOption + Float64ObservableGaugeOption +} + +// HistogramOption applies options to histogram instruments. +type HistogramOption interface { + Int64HistogramOption + Float64HistogramOption +} + +type descOpt string + +func (o descOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64ObservableUpDownCounter(c Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64ObservableUpDownCounter(c Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { + c.description = string(o) + return c +} + +func (o descOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { + c.description = string(o) + return c +} + +// WithDescription sets the instrument description. +func WithDescription(desc string) InstrumentOption { return descOpt(desc) } + +type unitOpt string + +func (o unitOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64ObservableUpDownCounter(c Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64ObservableUpDownCounter(c Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { + c.unit = string(o) + return c +} + +func (o unitOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { + c.unit = string(o) + return c +} + +// WithUnit sets the instrument unit. +// +// The unit u should be defined using the appropriate [UCUM](https://ucum.org) case-sensitive code. +func WithUnit(u string) InstrumentOption { return unitOpt(u) } + +// WithExplicitBucketBoundaries sets the instrument explicit bucket boundaries. +// +// This option is considered "advisory", and may be ignored by API implementations. +func WithExplicitBucketBoundaries(bounds ...float64) HistogramOption { return bucketOpt(bounds) } + +type bucketOpt []float64 + +func (o bucketOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig { + c.explicitBucketBoundaries = o + return c +} + +func (o bucketOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig { + c.explicitBucketBoundaries = o + return c +} + +// AddOption applies options to an addition measurement. See +// [MeasurementOption] for other options that can be used as an AddOption. +type AddOption interface { + applyAdd(AddConfig) AddConfig +} + +// AddConfig contains options for an addition measurement. +type AddConfig struct { + attrs attribute.Set +} + +// NewAddConfig returns a new [AddConfig] with all opts applied. +func NewAddConfig(opts []AddOption) AddConfig { + config := AddConfig{attrs: *attribute.EmptySet()} + for _, o := range opts { + config = o.applyAdd(config) + } + return config +} + +// Attributes returns the configured attribute set. +func (c AddConfig) Attributes() attribute.Set { + return c.attrs +} + +// RecordOption applies options to an addition measurement. See +// [MeasurementOption] for other options that can be used as a RecordOption. +type RecordOption interface { + applyRecord(RecordConfig) RecordConfig +} + +// RecordConfig contains options for a recorded measurement. +type RecordConfig struct { + attrs attribute.Set +} + +// NewRecordConfig returns a new [RecordConfig] with all opts applied. +func NewRecordConfig(opts []RecordOption) RecordConfig { + config := RecordConfig{attrs: *attribute.EmptySet()} + for _, o := range opts { + config = o.applyRecord(config) + } + return config +} + +// Attributes returns the configured attribute set. +func (c RecordConfig) Attributes() attribute.Set { + return c.attrs +} + +// ObserveOption applies options to an addition measurement. See +// [MeasurementOption] for other options that can be used as a ObserveOption. +type ObserveOption interface { + applyObserve(ObserveConfig) ObserveConfig +} + +// ObserveConfig contains options for an observed measurement. +type ObserveConfig struct { + attrs attribute.Set +} + +// NewObserveConfig returns a new [ObserveConfig] with all opts applied. +func NewObserveConfig(opts []ObserveOption) ObserveConfig { + config := ObserveConfig{attrs: *attribute.EmptySet()} + for _, o := range opts { + config = o.applyObserve(config) + } + return config +} + +// Attributes returns the configured attribute set. +func (c ObserveConfig) Attributes() attribute.Set { + return c.attrs +} + +// MeasurementOption applies options to all instrument measurement. +type MeasurementOption interface { + AddOption + RecordOption + ObserveOption +} + +type attrOpt struct { + set attribute.Set +} + +// mergeSets returns the union of keys between a and b. Any duplicate keys will +// use the value associated with b. +func mergeSets(a, b attribute.Set) attribute.Set { + // NewMergeIterator uses the first value for any duplicates. + iter := attribute.NewMergeIterator(&b, &a) + merged := make([]attribute.KeyValue, 0, a.Len()+b.Len()) + for iter.Next() { + merged = append(merged, iter.Attribute()) + } + return attribute.NewSet(merged...) +} + +func (o attrOpt) applyAdd(c AddConfig) AddConfig { + switch { + case o.set.Len() == 0: + case c.attrs.Len() == 0: + c.attrs = o.set + default: + c.attrs = mergeSets(c.attrs, o.set) + } + return c +} + +func (o attrOpt) applyRecord(c RecordConfig) RecordConfig { + switch { + case o.set.Len() == 0: + case c.attrs.Len() == 0: + c.attrs = o.set + default: + c.attrs = mergeSets(c.attrs, o.set) + } + return c +} + +func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig { + switch { + case o.set.Len() == 0: + case c.attrs.Len() == 0: + c.attrs = o.set + default: + c.attrs = mergeSets(c.attrs, o.set) + } + return c +} + +// WithAttributeSet sets the attribute Set associated with a measurement is +// made with. +// +// If multiple WithAttributeSet or WithAttributes options are passed the +// attributes will be merged together in the order they are passed. Attributes +// with duplicate keys will use the last value passed. +func WithAttributeSet(attributes attribute.Set) MeasurementOption { + return attrOpt{set: attributes} +} + +// WithAttributes converts attributes into an attribute Set and sets the Set to +// be associated with a measurement. This is shorthand for: +// +// cp := make([]attribute.KeyValue, len(attributes)) +// copy(cp, attributes) +// WithAttributes(attribute.NewSet(cp...)) +// +// [attribute.NewSet] may modify the passed attributes so this will make a copy +// of attributes before creating a set in order to ensure this function is +// concurrent safe. This makes this option function less optimized in +// comparison to [WithAttributeSet]. Therefore, [WithAttributeSet] should be +// preferred for performance sensitive code. +// +// See [WithAttributeSet] for information about how multiple WithAttributes are +// merged. +func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption { + cp := make([]attribute.KeyValue, len(attributes)) + copy(cp, attributes) + return attrOpt{set: attribute.NewSet(cp...)} +} diff --git a/vendor/go.opentelemetry.io/otel/metric/meter.go b/vendor/go.opentelemetry.io/otel/metric/meter.go new file mode 100644 index 0000000..460b3f9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/meter.go @@ -0,0 +1,209 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import ( + "context" + + "go.opentelemetry.io/otel/metric/embedded" +) + +// MeterProvider provides access to named Meter instances, for instrumenting +// an application or package. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type MeterProvider interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.MeterProvider + + // Meter returns a new Meter with the provided name and configuration. + // + // A Meter should be scoped at most to a single package. The name needs to + // be unique so it does not collide with other names used by + // an application, nor other applications. To achieve this, the import path + // of the instrumentation package is recommended to be used as name. + // + // If the name is empty, then an implementation defined default name will + // be used instead. + Meter(name string, opts ...MeterOption) Meter +} + +// Meter provides access to instrument instances for recording metrics. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Meter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Meter + + // Int64Counter returns a new Int64Counter instrument identified by name + // and configured with options. The instrument is used to synchronously + // record increasing int64 measurements during a computational operation. + Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error) + // Int64UpDownCounter returns a new Int64UpDownCounter instrument + // identified by name and configured with options. The instrument is used + // to synchronously record int64 measurements during a computational + // operation. + Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error) + // Int64Histogram returns a new Int64Histogram instrument identified by + // name and configured with options. The instrument is used to + // synchronously record the distribution of int64 measurements during a + // computational operation. + Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error) + // Int64Gauge returns a new Int64Gauge instrument identified by name and + // configured with options. The instrument is used to synchronously record + // instantaneous int64 measurements during a computational operation. + Int64Gauge(name string, options ...Int64GaugeOption) (Int64Gauge, error) + // Int64ObservableCounter returns a new Int64ObservableCounter identified + // by name and configured with options. The instrument is used to + // asynchronously record increasing int64 measurements once per a + // measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithInt64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error) + // Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter + // instrument identified by name and configured with options. The + // instrument is used to asynchronously record int64 measurements once per + // a measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithInt64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Int64ObservableUpDownCounter(name string, options ...Int64ObservableUpDownCounterOption) (Int64ObservableUpDownCounter, error) + // Int64ObservableGauge returns a new Int64ObservableGauge instrument + // identified by name and configured with options. The instrument is used + // to asynchronously record instantaneous int64 measurements once per a + // measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithInt64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error) + + // Float64Counter returns a new Float64Counter instrument identified by + // name and configured with options. The instrument is used to + // synchronously record increasing float64 measurements during a + // computational operation. + Float64Counter(name string, options ...Float64CounterOption) (Float64Counter, error) + // Float64UpDownCounter returns a new Float64UpDownCounter instrument + // identified by name and configured with options. The instrument is used + // to synchronously record float64 measurements during a computational + // operation. + Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error) + // Float64Histogram returns a new Float64Histogram instrument identified by + // name and configured with options. The instrument is used to + // synchronously record the distribution of float64 measurements during a + // computational operation. + Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error) + // Float64Gauge returns a new Float64Gauge instrument identified by name and + // configured with options. The instrument is used to synchronously record + // instantaneous float64 measurements during a computational operation. + Float64Gauge(name string, options ...Float64GaugeOption) (Float64Gauge, error) + // Float64ObservableCounter returns a new Float64ObservableCounter + // instrument identified by name and configured with options. The + // instrument is used to asynchronously record increasing float64 + // measurements once per a measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithFloat64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error) + // Float64ObservableUpDownCounter returns a new + // Float64ObservableUpDownCounter instrument identified by name and + // configured with options. The instrument is used to asynchronously record + // float64 measurements once per a measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithFloat64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Float64ObservableUpDownCounter(name string, options ...Float64ObservableUpDownCounterOption) (Float64ObservableUpDownCounter, error) + // Float64ObservableGauge returns a new Float64ObservableGauge instrument + // identified by name and configured with options. The instrument is used + // to asynchronously record instantaneous float64 measurements once per a + // measurement collection cycle. + // + // Measurements for the returned instrument are made via a callback. Use + // the WithFloat64Callback option to register the callback here, or use the + // RegisterCallback method of this Meter to register one later. See the + // Measurements section of the package documentation for more information. + Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error) + + // RegisterCallback registers f to be called during the collection of a + // measurement cycle. + // + // If Unregister of the returned Registration is called, f needs to be + // unregistered and not called during collection. + // + // The instruments f is registered with are the only instruments that f may + // observe values for. + // + // If no instruments are passed, f should not be registered nor called + // during collection. + // + // The function f needs to be concurrent safe. + RegisterCallback(f Callback, instruments ...Observable) (Registration, error) +} + +// Callback is a function registered with a Meter that makes observations for +// the set of instruments it is registered with. The Observer parameter is used +// to record measurement observations for these instruments. +// +// The function needs to complete in a finite amount of time and the deadline +// of the passed context is expected to be honored. +// +// The function needs to make unique observations across all registered +// Callbacks. Meaning, it should not report measurements for an instrument with +// the same attributes as another Callback will report. +// +// The function needs to be concurrent safe. +type Callback func(context.Context, Observer) error + +// Observer records measurements for multiple instruments in a Callback. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Observer interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Observer + + // ObserveFloat64 records the float64 value for obsrv. + ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption) + // ObserveInt64 records the int64 value for obsrv. + ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption) +} + +// Registration is an token representing the unique registration of a callback +// for a set of instruments with a Meter. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Registration interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Registration + + // Unregister removes the callback registration from a Meter. + // + // This method needs to be idempotent and concurrent safe. + Unregister() error +} diff --git a/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go b/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go new file mode 100644 index 0000000..8403a4b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go @@ -0,0 +1,226 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import ( + "context" + + "go.opentelemetry.io/otel/metric/embedded" +) + +// Float64Counter is an instrument that records increasing float64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64Counter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64Counter + + // Add records a change to the counter. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Add(ctx context.Context, incr float64, options ...AddOption) +} + +// Float64CounterConfig contains options for synchronous counter instruments that +// record float64 values. +type Float64CounterConfig struct { + description string + unit string +} + +// NewFloat64CounterConfig returns a new [Float64CounterConfig] with all opts +// applied. +func NewFloat64CounterConfig(opts ...Float64CounterOption) Float64CounterConfig { + var config Float64CounterConfig + for _, o := range opts { + config = o.applyFloat64Counter(config) + } + return config +} + +// Description returns the configured description. +func (c Float64CounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64CounterConfig) Unit() string { + return c.unit +} + +// Float64CounterOption applies options to a [Float64CounterConfig]. See +// [InstrumentOption] for other options that can be used as a +// Float64CounterOption. +type Float64CounterOption interface { + applyFloat64Counter(Float64CounterConfig) Float64CounterConfig +} + +// Float64UpDownCounter is an instrument that records increasing or decreasing +// float64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64UpDownCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64UpDownCounter + + // Add records a change to the counter. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Add(ctx context.Context, incr float64, options ...AddOption) +} + +// Float64UpDownCounterConfig contains options for synchronous counter +// instruments that record float64 values. +type Float64UpDownCounterConfig struct { + description string + unit string +} + +// NewFloat64UpDownCounterConfig returns a new [Float64UpDownCounterConfig] +// with all opts applied. +func NewFloat64UpDownCounterConfig(opts ...Float64UpDownCounterOption) Float64UpDownCounterConfig { + var config Float64UpDownCounterConfig + for _, o := range opts { + config = o.applyFloat64UpDownCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Float64UpDownCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64UpDownCounterConfig) Unit() string { + return c.unit +} + +// Float64UpDownCounterOption applies options to a +// [Float64UpDownCounterConfig]. See [InstrumentOption] for other options that +// can be used as a Float64UpDownCounterOption. +type Float64UpDownCounterOption interface { + applyFloat64UpDownCounter(Float64UpDownCounterConfig) Float64UpDownCounterConfig +} + +// Float64Histogram is an instrument that records a distribution of float64 +// values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64Histogram interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64Histogram + + // Record adds an additional value to the distribution. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Record(ctx context.Context, incr float64, options ...RecordOption) +} + +// Float64HistogramConfig contains options for synchronous histogram +// instruments that record float64 values. +type Float64HistogramConfig struct { + description string + unit string + explicitBucketBoundaries []float64 +} + +// NewFloat64HistogramConfig returns a new [Float64HistogramConfig] with all +// opts applied. +func NewFloat64HistogramConfig(opts ...Float64HistogramOption) Float64HistogramConfig { + var config Float64HistogramConfig + for _, o := range opts { + config = o.applyFloat64Histogram(config) + } + return config +} + +// Description returns the configured description. +func (c Float64HistogramConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64HistogramConfig) Unit() string { + return c.unit +} + +// ExplicitBucketBoundaries returns the configured explicit bucket boundaries. +func (c Float64HistogramConfig) ExplicitBucketBoundaries() []float64 { + return c.explicitBucketBoundaries +} + +// Float64HistogramOption applies options to a [Float64HistogramConfig]. See +// [InstrumentOption] for other options that can be used as a +// Float64HistogramOption. +type Float64HistogramOption interface { + applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig +} + +// Float64Gauge is an instrument that records instantaneous float64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Float64Gauge interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Float64Gauge + + // Record records the instantaneous value. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Record(ctx context.Context, value float64, options ...RecordOption) +} + +// Float64GaugeConfig contains options for synchronous gauge instruments that +// record float64 values. +type Float64GaugeConfig struct { + description string + unit string +} + +// NewFloat64GaugeConfig returns a new [Float64GaugeConfig] with all opts +// applied. +func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig { + var config Float64GaugeConfig + for _, o := range opts { + config = o.applyFloat64Gauge(config) + } + return config +} + +// Description returns the configured description. +func (c Float64GaugeConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Float64GaugeConfig) Unit() string { + return c.unit +} + +// Float64GaugeOption applies options to a [Float64GaugeConfig]. See +// [InstrumentOption] for other options that can be used as a +// Float64GaugeOption. +type Float64GaugeOption interface { + applyFloat64Gauge(Float64GaugeConfig) Float64GaugeConfig +} diff --git a/vendor/go.opentelemetry.io/otel/metric/syncint64.go b/vendor/go.opentelemetry.io/otel/metric/syncint64.go new file mode 100644 index 0000000..783fdfb --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/metric/syncint64.go @@ -0,0 +1,226 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metric // import "go.opentelemetry.io/otel/metric" + +import ( + "context" + + "go.opentelemetry.io/otel/metric/embedded" +) + +// Int64Counter is an instrument that records increasing int64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64Counter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64Counter + + // Add records a change to the counter. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Add(ctx context.Context, incr int64, options ...AddOption) +} + +// Int64CounterConfig contains options for synchronous counter instruments that +// record int64 values. +type Int64CounterConfig struct { + description string + unit string +} + +// NewInt64CounterConfig returns a new [Int64CounterConfig] with all opts +// applied. +func NewInt64CounterConfig(opts ...Int64CounterOption) Int64CounterConfig { + var config Int64CounterConfig + for _, o := range opts { + config = o.applyInt64Counter(config) + } + return config +} + +// Description returns the configured description. +func (c Int64CounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64CounterConfig) Unit() string { + return c.unit +} + +// Int64CounterOption applies options to a [Int64CounterConfig]. See +// [InstrumentOption] for other options that can be used as an +// Int64CounterOption. +type Int64CounterOption interface { + applyInt64Counter(Int64CounterConfig) Int64CounterConfig +} + +// Int64UpDownCounter is an instrument that records increasing or decreasing +// int64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64UpDownCounter interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64UpDownCounter + + // Add records a change to the counter. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Add(ctx context.Context, incr int64, options ...AddOption) +} + +// Int64UpDownCounterConfig contains options for synchronous counter +// instruments that record int64 values. +type Int64UpDownCounterConfig struct { + description string + unit string +} + +// NewInt64UpDownCounterConfig returns a new [Int64UpDownCounterConfig] with +// all opts applied. +func NewInt64UpDownCounterConfig(opts ...Int64UpDownCounterOption) Int64UpDownCounterConfig { + var config Int64UpDownCounterConfig + for _, o := range opts { + config = o.applyInt64UpDownCounter(config) + } + return config +} + +// Description returns the configured description. +func (c Int64UpDownCounterConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64UpDownCounterConfig) Unit() string { + return c.unit +} + +// Int64UpDownCounterOption applies options to a [Int64UpDownCounterConfig]. +// See [InstrumentOption] for other options that can be used as an +// Int64UpDownCounterOption. +type Int64UpDownCounterOption interface { + applyInt64UpDownCounter(Int64UpDownCounterConfig) Int64UpDownCounterConfig +} + +// Int64Histogram is an instrument that records a distribution of int64 +// values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64Histogram interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64Histogram + + // Record adds an additional value to the distribution. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Record(ctx context.Context, incr int64, options ...RecordOption) +} + +// Int64HistogramConfig contains options for synchronous histogram instruments +// that record int64 values. +type Int64HistogramConfig struct { + description string + unit string + explicitBucketBoundaries []float64 +} + +// NewInt64HistogramConfig returns a new [Int64HistogramConfig] with all opts +// applied. +func NewInt64HistogramConfig(opts ...Int64HistogramOption) Int64HistogramConfig { + var config Int64HistogramConfig + for _, o := range opts { + config = o.applyInt64Histogram(config) + } + return config +} + +// Description returns the configured description. +func (c Int64HistogramConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64HistogramConfig) Unit() string { + return c.unit +} + +// ExplicitBucketBoundaries returns the configured explicit bucket boundaries. +func (c Int64HistogramConfig) ExplicitBucketBoundaries() []float64 { + return c.explicitBucketBoundaries +} + +// Int64HistogramOption applies options to a [Int64HistogramConfig]. See +// [InstrumentOption] for other options that can be used as an +// Int64HistogramOption. +type Int64HistogramOption interface { + applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig +} + +// Int64Gauge is an instrument that records instantaneous int64 values. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. +type Int64Gauge interface { + // Users of the interface can ignore this. This embedded type is only used + // by implementations of this interface. See the "API Implementations" + // section of the package documentation for more information. + embedded.Int64Gauge + + // Record records the instantaneous value. + // + // Use the WithAttributeSet (or, if performance is not a concern, + // the WithAttributes) option to include measurement attributes. + Record(ctx context.Context, value int64, options ...RecordOption) +} + +// Int64GaugeConfig contains options for synchronous gauge instruments that +// record int64 values. +type Int64GaugeConfig struct { + description string + unit string +} + +// NewInt64GaugeConfig returns a new [Int64GaugeConfig] with all opts +// applied. +func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig { + var config Int64GaugeConfig + for _, o := range opts { + config = o.applyInt64Gauge(config) + } + return config +} + +// Description returns the configured description. +func (c Int64GaugeConfig) Description() string { + return c.description +} + +// Unit returns the configured unit. +func (c Int64GaugeConfig) Unit() string { + return c.unit +} + +// Int64GaugeOption applies options to a [Int64GaugeConfig]. See +// [InstrumentOption] for other options that can be used as a +// Int64GaugeOption. +type Int64GaugeOption interface { + applyInt64Gauge(Int64GaugeConfig) Int64GaugeConfig +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index df4ebda..7967665 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -904,6 +904,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { return errors.New("ssh: signature did not verify") } +func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey { + return &k.PublicKey +} + type skEd25519PublicKey struct { // application is a URL-like string, typically "ssh:" for SSH. // see openssh/PROTOCOL.u2f for details. @@ -1000,6 +1004,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { return nil } +func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey { + return k.PublicKey +} + // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, // *ecdsa.PrivateKey or any other crypto.Signer and returns a // corresponding Signer instance. ECDSA keys must use P-256, P-384 or diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index e2ae4f8..3ca9e89 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -462,6 +462,24 @@ func (p *PartialSuccessError) Error() string { // It is returned in ServerAuthError.Errors from NewServerConn. var ErrNoAuth = errors.New("ssh: no auth passed yet") +// BannerError is an error that can be returned by authentication handlers in +// ServerConfig to send a banner message to the client. +type BannerError struct { + Err error + Message string +} + +func (b *BannerError) Unwrap() error { + return b.Err +} + +func (b *BannerError) Error() string { + if b.Err == nil { + return b.Message + } + return b.Err.Error() +} + func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { sessionID := s.transport.getSessionID() var cache pubKeyCache @@ -734,6 +752,18 @@ userAuthLoop: config.AuthLogCallback(s, userAuthReq.Method, authErr) } + var bannerErr *BannerError + if errors.As(authErr, &bannerErr) { + if bannerErr.Message != "" { + bannerMsg := &userAuthBannerMsg{ + Message: bannerErr.Message, + } + if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil { + return nil, err + } + } + } + if authErr == nil { break userAuthLoop } diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index 6f2df28..003e649 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -17,6 +17,7 @@ package http2 // import "golang.org/x/net/http2" import ( "bufio" + "context" "crypto/tls" "fmt" "io" @@ -26,6 +27,7 @@ import ( "strconv" "strings" "sync" + "time" "golang.org/x/net/http/httpguts" ) @@ -210,12 +212,6 @@ type stringWriter interface { WriteString(s string) (n int, err error) } -// A gate lets two goroutines coordinate their activities. -type gate chan struct{} - -func (g gate) Done() { g <- struct{}{} } -func (g gate) Wait() { <-g } - // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). type closeWaiter chan struct{} @@ -383,3 +379,14 @@ func validPseudoPath(v string) bool { // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). type incomparable [0]func() + +// synctestGroupInterface is the methods of synctestGroup used by Server and Transport. +// It's defined as an interface here to let us keep synctestGroup entirely test-only +// and not a part of non-test builds. +type synctestGroupInterface interface { + Join() + Now() time.Time + NewTimer(d time.Duration) timer + AfterFunc(d time.Duration, f func()) timer + ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) +} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index c5d0810..6c349f3 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -154,6 +154,39 @@ type Server struct { // so that we don't embed a Mutex in this struct, which will make the // struct non-copyable, which might break some callers. state *serverInternalState + + // Synchronization group used for testing. + // Outside of tests, this is nil. + group synctestGroupInterface +} + +func (s *Server) markNewGoroutine() { + if s.group != nil { + s.group.Join() + } +} + +func (s *Server) now() time.Time { + if s.group != nil { + return s.group.Now() + } + return time.Now() +} + +// newTimer creates a new time.Timer, or a synthetic timer in tests. +func (s *Server) newTimer(d time.Duration) timer { + if s.group != nil { + return s.group.NewTimer(d) + } + return timeTimer{time.NewTimer(d)} +} + +// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. +func (s *Server) afterFunc(d time.Duration, f func()) timer { + if s.group != nil { + return s.group.AfterFunc(d, f) + } + return timeTimer{time.AfterFunc(d, f)} } func (s *Server) initialConnRecvWindowSize() int32 { @@ -400,6 +433,10 @@ func (o *ServeConnOpts) handler() http.Handler { // // The opts parameter is optional. If nil, default values are used. func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { + s.serveConn(c, opts, nil) +} + +func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverConn)) { baseCtx, cancel := serverConnBaseContext(c, opts) defer cancel() @@ -426,6 +463,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { pushEnabled: true, sawClientPreface: opts.SawClientPreface, } + if newf != nil { + newf(sc) + } s.state.registerConn(sc) defer s.state.unregisterConn(sc) @@ -599,8 +639,8 @@ type serverConn struct { inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop needToSendGoAway bool // we need to schedule a GOAWAY frame write goAwayCode ErrCode - shutdownTimer *time.Timer // nil until used - idleTimer *time.Timer // nil if unused + shutdownTimer timer // nil until used + idleTimer timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -649,12 +689,12 @@ type stream struct { flow outflow // limits writing from Handler to client inflow inflow // what the client is allowed to POST/etc to us state streamState - resetQueued bool // RST_STREAM queued for write; set by sc.resetStream - gotTrailerHeader bool // HEADER frame for trailers was seen - wroteHeaders bool // whether we wrote headers (not status 100) - readDeadline *time.Timer // nil if unused - writeDeadline *time.Timer // nil if unused - closeErr error // set before cw is closed + resetQueued bool // RST_STREAM queued for write; set by sc.resetStream + gotTrailerHeader bool // HEADER frame for trailers was seen + wroteHeaders bool // whether we wrote headers (not status 100) + readDeadline timer // nil if unused + writeDeadline timer // nil if unused + closeErr error // set before cw is closed trailer http.Header // accumulated trailers reqTrailer http.Header // handler's Request.Trailer @@ -811,8 +851,9 @@ type readFrameResult struct { // consumer is done with the frame. // It's run on its own goroutine. func (sc *serverConn) readFrames() { - gate := make(gate) - gateDone := gate.Done + sc.srv.markNewGoroutine() + gate := make(chan struct{}) + gateDone := func() { gate <- struct{}{} } for { f, err := sc.framer.ReadFrame() select { @@ -843,6 +884,7 @@ type frameWriteResult struct { // At most one goroutine can be running writeFrameAsync at a time per // serverConn. func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) { + sc.srv.markNewGoroutine() var err error if wd == nil { err = wr.write.writeFrame(sc) @@ -922,13 +964,13 @@ func (sc *serverConn) serve() { sc.setConnState(http.StateIdle) if sc.srv.IdleTimeout > 0 { - sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) + sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) defer sc.idleTimer.Stop() } go sc.readFrames() // closed by defer sc.conn.Close above - settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer) + settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() loopNum := 0 @@ -1057,10 +1099,10 @@ func (sc *serverConn) readPreface() error { errc <- nil } }() - timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server? + timer := sc.srv.newTimer(prefaceTimeout) // TODO: configurable on *Server? defer timer.Stop() select { - case <-timer.C: + case <-timer.C(): return errPrefaceTimeout case err := <-errc: if err == nil { @@ -1425,7 +1467,7 @@ func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) shutDownIn(d time.Duration) { sc.serveG.check() - sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer) + sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer) } func (sc *serverConn) resetStream(se StreamError) { @@ -1639,7 +1681,7 @@ func (sc *serverConn) closeStream(st *stream, err error) { delete(sc.streams, st.id) if len(sc.streams) == 0 { sc.setConnState(http.StateIdle) - if sc.srv.IdleTimeout > 0 { + if sc.srv.IdleTimeout > 0 && sc.idleTimer != nil { sc.idleTimer.Reset(sc.srv.IdleTimeout) } if h1ServerKeepAlivesDisabled(sc.hs) { @@ -1661,6 +1703,7 @@ func (sc *serverConn) closeStream(st *stream, err error) { } } st.closeErr = err + st.cancelCtx() st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc sc.writeSched.CloseStream(st.id) } @@ -2021,7 +2064,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { // (in Go 1.8), though. That's a more sane option anyway. if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) - st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) + st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout) } return sc.scheduleHandler(id, rw, req, handler) @@ -2119,7 +2162,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.flow.add(sc.initialStreamSendWindowSize) st.inflow.init(sc.srv.initialStreamRecvWindowSize()) if sc.hs.WriteTimeout > 0 { - st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) + st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } sc.streams[id] = st @@ -2343,6 +2386,7 @@ func (sc *serverConn) handlerDone() { // Run on its own goroutine. func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) { + sc.srv.markNewGoroutine() defer sc.sendServeMsg(handlerDoneMsg) didPanic := true defer func() { @@ -2639,7 +2683,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { var date string if _, ok := rws.snapHeader["Date"]; !ok { // TODO(bradfitz): be faster here, like net/http? measure. - date = time.Now().UTC().Format(http.TimeFormat) + date = rws.conn.srv.now().UTC().Format(http.TimeFormat) } for _, v := range rws.snapHeader["Trailer"] { @@ -2761,7 +2805,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() { func (w *responseWriter) SetReadDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(time.Now()) { + if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onReadTimeout() @@ -2777,9 +2821,9 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { if deadline.IsZero() { st.readDeadline = nil } else if st.readDeadline == nil { - st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout) + st.readDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onReadTimeout) } else { - st.readDeadline.Reset(deadline.Sub(time.Now())) + st.readDeadline.Reset(deadline.Sub(sc.srv.now())) } }) return nil @@ -2787,7 +2831,7 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(time.Now()) { + if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onWriteTimeout() @@ -2803,9 +2847,9 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { if deadline.IsZero() { st.writeDeadline = nil } else if st.writeDeadline == nil { - st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout) + st.writeDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onWriteTimeout) } else { - st.writeDeadline.Reset(deadline.Sub(time.Now())) + st.writeDeadline.Reset(deadline.Sub(sc.srv.now())) } }) return nil diff --git a/vendor/golang.org/x/net/http2/testsync.go b/vendor/golang.org/x/net/http2/testsync.go deleted file mode 100644 index 61075bd..0000000 --- a/vendor/golang.org/x/net/http2/testsync.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package http2 - -import ( - "context" - "sync" - "time" -) - -// testSyncHooks coordinates goroutines in tests. -// -// For example, a call to ClientConn.RoundTrip involves several goroutines, including: -// - the goroutine running RoundTrip; -// - the clientStream.doRequest goroutine, which writes the request; and -// - the clientStream.readLoop goroutine, which reads the response. -// -// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines -// are blocked waiting for some condition such as reading the Request.Body or waiting for -// flow control to become available. -// -// The testSyncHooks also manage timers and synthetic time in tests. -// This permits us to, for example, start a request and cause it to time out waiting for -// response headers without resorting to time.Sleep calls. -type testSyncHooks struct { - // active/inactive act as a mutex and condition variable. - // - // - neither chan contains a value: testSyncHooks is locked. - // - active contains a value: unlocked, and at least one goroutine is not blocked - // - inactive contains a value: unlocked, and all goroutines are blocked - active chan struct{} - inactive chan struct{} - - // goroutine counts - total int // total goroutines - condwait map[*sync.Cond]int // blocked in sync.Cond.Wait - blocked []*testBlockedGoroutine // otherwise blocked - - // fake time - now time.Time - timers []*fakeTimer - - // Transport testing: Report various events. - newclientconn func(*ClientConn) - newstream func(*clientStream) -} - -// testBlockedGoroutine is a blocked goroutine. -type testBlockedGoroutine struct { - f func() bool // blocked until f returns true - ch chan struct{} // closed when unblocked -} - -func newTestSyncHooks() *testSyncHooks { - h := &testSyncHooks{ - active: make(chan struct{}, 1), - inactive: make(chan struct{}, 1), - condwait: map[*sync.Cond]int{}, - } - h.inactive <- struct{}{} - h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) - return h -} - -// lock acquires the testSyncHooks mutex. -func (h *testSyncHooks) lock() { - select { - case <-h.active: - case <-h.inactive: - } -} - -// waitInactive waits for all goroutines to become inactive. -func (h *testSyncHooks) waitInactive() { - for { - <-h.inactive - if !h.unlock() { - break - } - } -} - -// unlock releases the testSyncHooks mutex. -// It reports whether any goroutines are active. -func (h *testSyncHooks) unlock() (active bool) { - // Look for a blocked goroutine which can be unblocked. - blocked := h.blocked[:0] - unblocked := false - for _, b := range h.blocked { - if !unblocked && b.f() { - unblocked = true - close(b.ch) - } else { - blocked = append(blocked, b) - } - } - h.blocked = blocked - - // Count goroutines blocked on condition variables. - condwait := 0 - for _, count := range h.condwait { - condwait += count - } - - if h.total > condwait+len(blocked) { - h.active <- struct{}{} - return true - } else { - h.inactive <- struct{}{} - return false - } -} - -// goRun starts a new goroutine. -func (h *testSyncHooks) goRun(f func()) { - h.lock() - h.total++ - h.unlock() - go func() { - defer func() { - h.lock() - h.total-- - h.unlock() - }() - f() - }() -} - -// blockUntil indicates that a goroutine is blocked waiting for some condition to become true. -// It waits until f returns true before proceeding. -// -// Example usage: -// -// h.blockUntil(func() bool { -// // Is the context done yet? -// select { -// case <-ctx.Done(): -// default: -// return false -// } -// return true -// }) -// // Wait for the context to become done. -// <-ctx.Done() -// -// The function f passed to blockUntil must be non-blocking and idempotent. -func (h *testSyncHooks) blockUntil(f func() bool) { - if f() { - return - } - ch := make(chan struct{}) - h.lock() - h.blocked = append(h.blocked, &testBlockedGoroutine{ - f: f, - ch: ch, - }) - h.unlock() - <-ch -} - -// broadcast is sync.Cond.Broadcast. -func (h *testSyncHooks) condBroadcast(cond *sync.Cond) { - h.lock() - delete(h.condwait, cond) - h.unlock() - cond.Broadcast() -} - -// broadcast is sync.Cond.Wait. -func (h *testSyncHooks) condWait(cond *sync.Cond) { - h.lock() - h.condwait[cond]++ - h.unlock() -} - -// newTimer creates a new fake timer. -func (h *testSyncHooks) newTimer(d time.Duration) timer { - h.lock() - defer h.unlock() - t := &fakeTimer{ - hooks: h, - when: h.now.Add(d), - c: make(chan time.Time), - } - h.timers = append(h.timers, t) - return t -} - -// afterFunc creates a new fake AfterFunc timer. -func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer { - h.lock() - defer h.unlock() - t := &fakeTimer{ - hooks: h, - when: h.now.Add(d), - f: f, - } - h.timers = append(h.timers, t) - return t -} - -func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(ctx) - t := h.afterFunc(d, cancel) - return ctx, func() { - t.Stop() - cancel() - } -} - -func (h *testSyncHooks) timeUntilEvent() time.Duration { - h.lock() - defer h.unlock() - var next time.Time - for _, t := range h.timers { - if next.IsZero() || t.when.Before(next) { - next = t.when - } - } - if d := next.Sub(h.now); d > 0 { - return d - } - return 0 -} - -// advance advances time and causes synthetic timers to fire. -func (h *testSyncHooks) advance(d time.Duration) { - h.lock() - defer h.unlock() - h.now = h.now.Add(d) - timers := h.timers[:0] - for _, t := range h.timers { - t := t // remove after go.mod depends on go1.22 - t.mu.Lock() - switch { - case t.when.After(h.now): - timers = append(timers, t) - case t.when.IsZero(): - // stopped timer - default: - t.when = time.Time{} - if t.c != nil { - close(t.c) - } - if t.f != nil { - h.total++ - go func() { - defer func() { - h.lock() - h.total-- - h.unlock() - }() - t.f() - }() - } - } - t.mu.Unlock() - } - h.timers = timers -} - -// A timer wraps a time.Timer, or a synthetic equivalent in tests. -// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires. -type timer interface { - C() <-chan time.Time - Stop() bool - Reset(d time.Duration) bool -} - -// timeTimer implements timer using real time. -type timeTimer struct { - t *time.Timer - c chan time.Time -} - -// newTimeTimer creates a new timer using real time. -func newTimeTimer(d time.Duration) timer { - ch := make(chan time.Time) - t := time.AfterFunc(d, func() { - close(ch) - }) - return &timeTimer{t, ch} -} - -// newTimeAfterFunc creates an AfterFunc timer using real time. -func newTimeAfterFunc(d time.Duration, f func()) timer { - return &timeTimer{ - t: time.AfterFunc(d, f), - } -} - -func (t timeTimer) C() <-chan time.Time { return t.c } -func (t timeTimer) Stop() bool { return t.t.Stop() } -func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) } - -// fakeTimer implements timer using fake time. -type fakeTimer struct { - hooks *testSyncHooks - - mu sync.Mutex - when time.Time // when the timer will fire - c chan time.Time // closed when the timer fires; mutually exclusive with f - f func() // called when the timer fires; mutually exclusive with c -} - -func (t *fakeTimer) C() <-chan time.Time { return t.c } - -func (t *fakeTimer) Stop() bool { - t.mu.Lock() - defer t.mu.Unlock() - stopped := t.when.IsZero() - t.when = time.Time{} - return stopped -} - -func (t *fakeTimer) Reset(d time.Duration) bool { - if t.c != nil || t.f == nil { - panic("fakeTimer only supports Reset on AfterFunc timers") - } - t.mu.Lock() - defer t.mu.Unlock() - t.hooks.lock() - defer t.hooks.unlock() - active := !t.when.IsZero() - t.when = t.hooks.now.Add(d) - if !active { - t.hooks.timers = append(t.hooks.timers, t) - } - return active -} diff --git a/vendor/golang.org/x/net/http2/timer.go b/vendor/golang.org/x/net/http2/timer.go new file mode 100644 index 0000000..0b1c17b --- /dev/null +++ b/vendor/golang.org/x/net/http2/timer.go @@ -0,0 +1,20 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package http2 + +import "time" + +// A timer is a time.Timer, as an interface which can be replaced in tests. +type timer = interface { + C() <-chan time.Time + Reset(d time.Duration) bool + Stop() bool +} + +// timeTimer adapts a time.Timer to the timer interface. +type timeTimer struct { + *time.Timer +} + +func (t timeTimer) C() <-chan time.Time { return t.Timer.C } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 2fa4949..98a49c6 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -185,7 +185,45 @@ type Transport struct { connPoolOnce sync.Once connPoolOrDef ClientConnPool // non-nil version of ConnPool - syncHooks *testSyncHooks + *transportTestHooks +} + +// Hook points used for testing. +// Outside of tests, t.transportTestHooks is nil and these all have minimal implementations. +// Inside tests, see the testSyncHooks function docs. + +type transportTestHooks struct { + newclientconn func(*ClientConn) + group synctestGroupInterface +} + +func (t *Transport) markNewGoroutine() { + if t != nil && t.transportTestHooks != nil { + t.transportTestHooks.group.Join() + } +} + +// newTimer creates a new time.Timer, or a synthetic timer in tests. +func (t *Transport) newTimer(d time.Duration) timer { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.NewTimer(d) + } + return timeTimer{time.NewTimer(d)} +} + +// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. +func (t *Transport) afterFunc(d time.Duration, f func()) timer { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.AfterFunc(d, f) + } + return timeTimer{time.AfterFunc(d, f)} +} + +func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.ContextWithTimeout(ctx, d) + } + return context.WithTimeout(ctx, d) } func (t *Transport) maxHeaderListSize() uint32 { @@ -352,60 +390,6 @@ type ClientConn struct { werr error // first write error that has occurred hbuf bytes.Buffer // HPACK encoder writes into this henc *hpack.Encoder - - syncHooks *testSyncHooks // can be nil -} - -// Hook points used for testing. -// Outside of tests, cc.syncHooks is nil and these all have minimal implementations. -// Inside tests, see the testSyncHooks function docs. - -// goRun starts a new goroutine. -func (cc *ClientConn) goRun(f func()) { - if cc.syncHooks != nil { - cc.syncHooks.goRun(f) - return - } - go f() -} - -// condBroadcast is cc.cond.Broadcast. -func (cc *ClientConn) condBroadcast() { - if cc.syncHooks != nil { - cc.syncHooks.condBroadcast(cc.cond) - } - cc.cond.Broadcast() -} - -// condWait is cc.cond.Wait. -func (cc *ClientConn) condWait() { - if cc.syncHooks != nil { - cc.syncHooks.condWait(cc.cond) - } - cc.cond.Wait() -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (cc *ClientConn) newTimer(d time.Duration) timer { - if cc.syncHooks != nil { - return cc.syncHooks.newTimer(d) - } - return newTimeTimer(d) -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer { - if cc.syncHooks != nil { - return cc.syncHooks.afterFunc(d, f) - } - return newTimeAfterFunc(d, f) -} - -func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - if cc.syncHooks != nil { - return cc.syncHooks.contextWithTimeout(ctx, d) - } - return context.WithTimeout(ctx, d) } // clientStream is the state for a single HTTP/2 stream. One of these @@ -487,7 +471,7 @@ func (cs *clientStream) abortStreamLocked(err error) { // TODO(dneil): Clean up tests where cs.cc.cond is nil. if cs.cc.cond != nil { // Wake up writeRequestBody if it is waiting on flow control. - cs.cc.condBroadcast() + cs.cc.cond.Broadcast() } } @@ -497,7 +481,7 @@ func (cs *clientStream) abortRequestBodyWrite() { defer cc.mu.Unlock() if cs.reqBody != nil && cs.reqBodyClosed == nil { cs.closeReqBodyLocked() - cc.condBroadcast() + cc.cond.Broadcast() } } @@ -507,10 +491,11 @@ func (cs *clientStream) closeReqBodyLocked() { } cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed - cs.cc.goRun(func() { + go func() { + cs.cc.t.markNewGoroutine() cs.reqBody.Close() close(reqBodyClosed) - }) + }() } type stickyErrWriter struct { @@ -626,21 +611,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) - var tm timer - if t.syncHooks != nil { - tm = t.syncHooks.newTimer(d) - t.syncHooks.blockUntil(func() bool { - select { - case <-tm.C(): - case <-req.Context().Done(): - default: - return false - } - return true - }) - } else { - tm = newTimeTimer(d) - } + tm := t.newTimer(d) select { case <-tm.C(): t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) @@ -725,8 +696,8 @@ func canRetryError(err error) bool { } func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { - if t.syncHooks != nil { - return t.newClientConn(nil, singleUse, t.syncHooks) + if t.transportTestHooks != nil { + return t.newClientConn(nil, singleUse) } host, _, err := net.SplitHostPort(addr) if err != nil { @@ -736,7 +707,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b if err != nil { return nil, err } - return t.newClientConn(tconn, singleUse, nil) + return t.newClientConn(tconn, singleUse) } func (t *Transport) newTLSConfig(host string) *tls.Config { @@ -802,10 +773,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 { } func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { - return t.newClientConn(c, t.disableKeepAlives(), nil) + return t.newClientConn(c, t.disableKeepAlives()) } -func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) { +func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { cc := &ClientConn{ t: t, tconn: c, @@ -820,16 +791,12 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo wantSettingsAck: true, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), - syncHooks: hooks, } - if hooks != nil { - hooks.newclientconn(cc) + if t.transportTestHooks != nil { + t.markNewGoroutine() + t.transportTestHooks.newclientconn(cc) c = cc.tconn } - if d := t.idleConnTimeout(); d != 0 { - cc.idleTimeout = d - cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout) - } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) } @@ -893,7 +860,13 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo return nil, cc.werr } - cc.goRun(cc.readLoop) + // Start the idle timer after the connection is fully initialized. + if d := t.idleConnTimeout(); d != 0 { + cc.idleTimeout = d + cc.idleTimer = t.afterFunc(d, cc.onIdleTimeout) + } + + go cc.readLoop() return cc, nil } @@ -901,7 +874,7 @@ func (cc *ClientConn) healthCheck() { pingTimeout := cc.t.pingTimeout() // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. - ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout) + ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) @@ -1144,7 +1117,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { // Wait for all in-flight streams to complete or connection to close done := make(chan struct{}) cancelled := false // guarded by cc.mu - cc.goRun(func() { + go func() { + cc.t.markNewGoroutine() cc.mu.Lock() defer cc.mu.Unlock() for { @@ -1156,9 +1130,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { if cancelled { break } - cc.condWait() + cc.cond.Wait() } - }) + }() shutdownEnterWaitStateHook() select { case <-done: @@ -1168,7 +1142,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { cc.mu.Lock() // Free the goroutine above cancelled = true - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() return ctx.Err() } @@ -1206,7 +1180,7 @@ func (cc *ClientConn) closeForError(err error) { for _, cs := range cc.streams { cs.abortStreamLocked(err) } - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() cc.closeConn() } @@ -1321,23 +1295,30 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) respHeaderRecv: make(chan struct{}), donec: make(chan struct{}), } - cc.goRun(func() { - cs.doRequest(req) - }) + + // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? + if !cc.t.disableCompression() && + req.Header.Get("Accept-Encoding") == "" && + req.Header.Get("Range") == "" && + !cs.isHead { + // Request gzip only, not deflate. Deflate is ambiguous and + // not as universally supported anyway. + // See: https://zlib.net/zlib_faq.html#faq39 + // + // Note that we don't request this for HEAD requests, + // due to a bug in nginx: + // http://trac.nginx.org/nginx/ticket/358 + // https://golang.org/issue/5522 + // + // We don't request gzip if the request is for a range, since + // auto-decoding a portion of a gzipped document will just fail + // anyway. See https://golang.org/issue/8923 + cs.requestedGzip = true + } + + go cs.doRequest(req, streamf) waitDone := func() error { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.donec: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.donec: return nil @@ -1398,24 +1379,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) return err } - if streamf != nil { - streamf(cs) - } - for { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.respHeaderRecv: - case <-cs.abort: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.respHeaderRecv: return handleResponseHeaders() @@ -1445,8 +1409,9 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) // doRequest runs for the duration of the request lifetime. // // It sends the request and performs post-request cleanup (closing Request.Body, etc.). -func (cs *clientStream) doRequest(req *http.Request) { - err := cs.writeRequest(req) +func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)) { + cs.cc.t.markNewGoroutine() + err := cs.writeRequest(req, streamf) cs.cleanupWriteRequest(err) } @@ -1457,7 +1422,7 @@ func (cs *clientStream) doRequest(req *http.Request) { // // It returns non-nil if the request ends otherwise. // If the returned error is StreamError, the error Code may be used in resetting the stream. -func (cs *clientStream) writeRequest(req *http.Request) (err error) { +func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStream)) (err error) { cc := cs.cc ctx := cs.ctx @@ -1471,21 +1436,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } - var newStreamHook func(*clientStream) - if cc.syncHooks != nil { - newStreamHook = cc.syncHooks.newstream - cc.syncHooks.blockUntil(func() bool { - select { - case cc.reqHeaderMu <- struct{}{}: - <-cc.reqHeaderMu - case <-cs.reqCancel: - case <-ctx.Done(): - default: - return false - } - return true - }) - } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: @@ -1510,28 +1460,8 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { } cc.mu.Unlock() - if newStreamHook != nil { - newStreamHook(cs) - } - - // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? - if !cc.t.disableCompression() && - req.Header.Get("Accept-Encoding") == "" && - req.Header.Get("Range") == "" && - !cs.isHead { - // Request gzip only, not deflate. Deflate is ambiguous and - // not as universally supported anyway. - // See: https://zlib.net/zlib_faq.html#faq39 - // - // Note that we don't request this for HEAD requests, - // due to a bug in nginx: - // http://trac.nginx.org/nginx/ticket/358 - // https://golang.org/issue/5522 - // - // We don't request gzip if the request is for a range, since - // auto-decoding a portion of a gzipped document will just fail - // anyway. See https://golang.org/issue/8923 - cs.requestedGzip = true + if streamf != nil { + streamf(cs) } continueTimeout := cc.t.expectContinueTimeout() @@ -1594,7 +1524,7 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { - timer := cc.newTimer(d) + timer := cc.t.newTimer(d) defer timer.Stop() respHeaderTimer = timer.C() respHeaderRecv = cs.respHeaderRecv @@ -1603,21 +1533,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { // or until the request is aborted (via context, error, or otherwise), // whichever comes first. for { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.peerClosed: - case <-respHeaderTimer: - case <-respHeaderRecv: - case <-cs.abort: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.peerClosed: return nil @@ -1766,7 +1681,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { return nil } cc.pendingRequests++ - cc.condWait() + cc.cond.Wait() cc.pendingRequests-- select { case <-cs.abort: @@ -2028,7 +1943,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) cs.flow.take(take) return take, nil } - cc.condWait() + cc.cond.Wait() } } @@ -2311,7 +2226,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) { } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. - cc.condBroadcast() + cc.cond.Broadcast() closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { @@ -2333,6 +2248,7 @@ type clientConnReadLoop struct { // readLoop runs in its own goroutine and reads and dispatches frames. func (cc *ClientConn) readLoop() { + cc.t.markNewGoroutine() rl := &clientConnReadLoop{cc: cc} defer rl.cleanup() cc.readerErr = rl.run() @@ -2399,7 +2315,7 @@ func (rl *clientConnReadLoop) cleanup() { cs.abortStreamLocked(err) } } - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() } @@ -2436,7 +2352,7 @@ func (rl *clientConnReadLoop) run() error { readIdleTimeout := cc.t.ReadIdleTimeout var t timer if readIdleTimeout != 0 { - t = cc.afterFunc(readIdleTimeout, cc.healthCheck) + t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() @@ -3034,7 +2950,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { for _, cs := range cc.streams { cs.flow.add(delta) } - cc.condBroadcast() + cc.cond.Broadcast() cc.initialWindowSize = s.Val case SettingHeaderTableSize: @@ -3089,7 +3005,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { return ConnectionError(ErrCodeFlowControl) } - cc.condBroadcast() + cc.cond.Broadcast() return nil } @@ -3133,7 +3049,8 @@ func (cc *ClientConn) Ping(ctx context.Context) error { } var pingError error errc := make(chan struct{}) - cc.goRun(func() { + go func() { + cc.t.markNewGoroutine() cc.wmu.Lock() defer cc.wmu.Unlock() if pingError = cc.fr.WritePing(false, p); pingError != nil { @@ -3144,20 +3061,7 @@ func (cc *ClientConn) Ping(ctx context.Context) error { close(errc) return } - }) - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-c: - case <-errc: - case <-ctx.Done(): - case <-cc.readerDone: - default: - return false - } - return true - }) - } + }() select { case <-c: return nil diff --git a/vendor/golang.org/x/net/http2/writesched_priority.go b/vendor/golang.org/x/net/http2/writesched_priority.go index 0a242c6..f678333 100644 --- a/vendor/golang.org/x/net/http2/writesched_priority.go +++ b/vendor/golang.org/x/net/http2/writesched_priority.go @@ -443,8 +443,8 @@ func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, max } func (ws *priorityWriteScheduler) removeNode(n *priorityNode) { - for k := n.kids; k != nil; k = k.next { - k.setParent(n.parent) + for n.kids != nil { + n.kids.setParent(n.parent) } n.setParent(nil) delete(ws.nodes, n.id) diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index fdcaa97..4ed2e48 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -263,6 +263,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -549,6 +550,7 @@ ccflags="$@" $2 !~ "NLA_TYPE_MASK" && $2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ && $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ || + $2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ || $2 ~ /^FIORDCHK$/ || $2 ~ /^SIOC/ || $2 ~ /^TIOC/ || diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 93a38a9..877a62b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -502,6 +502,7 @@ const ( BPF_IMM = 0x0 BPF_IND = 0x40 BPF_JA = 0x0 + BPF_JCOND = 0xe0 BPF_JEQ = 0x10 BPF_JGE = 0x30 BPF_JGT = 0x20 @@ -657,6 +658,9 @@ const ( CAN_NPROTO = 0x8 CAN_RAW = 0x1 CAN_RAW_FILTER_MAX = 0x200 + CAN_RAW_XL_VCID_RX_FILTER = 0x4 + CAN_RAW_XL_VCID_TX_PASS = 0x2 + CAN_RAW_XL_VCID_TX_SET = 0x1 CAN_RTR_FLAG = 0x40000000 CAN_SFF_ID_BITS = 0xb CAN_SFF_MASK = 0x7ff @@ -1339,6 +1343,7 @@ const ( F_OFD_SETLK = 0x25 F_OFD_SETLKW = 0x26 F_OK = 0x0 + F_SEAL_EXEC = 0x20 F_SEAL_FUTURE_WRITE = 0x10 F_SEAL_GROW = 0x4 F_SEAL_SEAL = 0x1 @@ -1627,6 +1632,7 @@ const ( IP_FREEBIND = 0xf IP_HDRINCL = 0x3 IP_IPSEC_POLICY = 0x10 + IP_LOCAL_PORT_RANGE = 0x33 IP_MAXPACKET = 0xffff IP_MAX_MEMBERSHIPS = 0x14 IP_MF = 0x2000 @@ -1653,6 +1659,7 @@ const ( IP_PMTUDISC_OMIT = 0x5 IP_PMTUDISC_PROBE = 0x3 IP_PMTUDISC_WANT = 0x1 + IP_PROTOCOL = 0x34 IP_RECVERR = 0xb IP_RECVERR_RFC4884 = 0x1a IP_RECVFRAGSIZE = 0x19 @@ -2169,7 +2176,7 @@ const ( NFT_SECMARK_CTX_MAXLEN = 0x100 NFT_SET_MAXNAMELEN = 0x100 NFT_SOCKET_MAX = 0x3 - NFT_TABLE_F_MASK = 0x3 + NFT_TABLE_F_MASK = 0x7 NFT_TABLE_MAXNAMELEN = 0x100 NFT_TRACETYPE_MAX = 0x3 NFT_TUNNEL_F_MASK = 0x7 @@ -2403,6 +2410,7 @@ const ( PERF_RECORD_MISC_USER = 0x2 PERF_SAMPLE_BRANCH_PLM_ALL = 0x7 PERF_SAMPLE_WEIGHT_TYPE = 0x1004000 + PID_FS_MAGIC = 0x50494446 PIPEFS_MAGIC = 0x50495045 PPPIOCGNPMODE = 0xc008744c PPPIOCNEWUNIT = 0xc004743e @@ -2896,8 +2904,9 @@ const ( RWF_APPEND = 0x10 RWF_DSYNC = 0x2 RWF_HIPRI = 0x1 + RWF_NOAPPEND = 0x20 RWF_NOWAIT = 0x8 - RWF_SUPPORTED = 0x1f + RWF_SUPPORTED = 0x3f RWF_SYNC = 0x4 RWF_WRITE_LIFE_NOT_SET = 0x0 SCHED_BATCH = 0x3 @@ -2918,7 +2927,9 @@ const ( SCHED_RESET_ON_FORK = 0x40000000 SCHED_RR = 0x2 SCM_CREDENTIALS = 0x2 + SCM_PIDFD = 0x4 SCM_RIGHTS = 0x1 + SCM_SECURITY = 0x3 SCM_TIMESTAMP = 0x1d SC_LOG_FLUSH = 0x100000 SECCOMP_ADDFD_FLAG_SEND = 0x2 @@ -3051,6 +3062,8 @@ const ( SIOCSMIIREG = 0x8949 SIOCSRARP = 0x8962 SIOCWANDEV = 0x894a + SK_DIAG_BPF_STORAGE_MAX = 0x3 + SK_DIAG_BPF_STORAGE_REQ_MAX = 0x1 SMACK_MAGIC = 0x43415d53 SMART_AUTOSAVE = 0xd2 SMART_AUTO_OFFLINE = 0xdb @@ -3071,6 +3084,8 @@ const ( SOCKFS_MAGIC = 0x534f434b SOCK_BUF_LOCK_MASK = 0x3 SOCK_DCCP = 0x6 + SOCK_DESTROY = 0x15 + SOCK_DIAG_BY_FAMILY = 0x14 SOCK_IOC_TYPE = 0x89 SOCK_PACKET = 0xa SOCK_RAW = 0x3 @@ -3260,6 +3275,7 @@ const ( TCP_MAX_WINSHIFT = 0xe TCP_MD5SIG = 0xe TCP_MD5SIG_EXT = 0x20 + TCP_MD5SIG_FLAG_IFINDEX = 0x2 TCP_MD5SIG_FLAG_PREFIX = 0x1 TCP_MD5SIG_MAXKEYLEN = 0x50 TCP_MSS = 0x200 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 42ff8c3..e4bc0bd 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -118,6 +118,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 MAP_32BIT = 0x40 + MAP_ABOVE4G = 0x80 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 MAP_DENYWRITE = 0x800 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index dca4360..689317a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -118,6 +118,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 MAP_32BIT = 0x40 + MAP_ABOVE4G = 0x80 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 MAP_DENYWRITE = 0x800 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index d8cae6d..1427050 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -87,6 +87,7 @@ const ( FICLONE = 0x40049409 FICLONERANGE = 0x4020940d FLUSHO = 0x1000 + FPMR_MAGIC = 0x46504d52 FPSIMD_MAGIC = 0x46508001 FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_GETFLAGS = 0x80086601 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 0036746..4740b83 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -4605,7 +4605,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x149 + NL80211_ATTR_MAX = 0x14a NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -5209,7 +5209,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x1f + NL80211_FREQUENCY_ATTR_MAX = 0x20 NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc @@ -5703,7 +5703,7 @@ const ( NL80211_STA_FLAG_ASSOCIATED = 0x7 NL80211_STA_FLAG_AUTHENTICATED = 0x5 NL80211_STA_FLAG_AUTHORIZED = 0x1 - NL80211_STA_FLAG_MAX = 0x7 + NL80211_STA_FLAG_MAX = 0x8 NL80211_STA_FLAG_MAX_OLD_API = 0x6 NL80211_STA_FLAG_MFP = 0x4 NL80211_STA_FLAG_SHORT_PREAMBLE = 0x2 @@ -6001,3 +6001,34 @@ type CachestatRange struct { Off uint64 Len uint64 } + +const ( + SK_MEMINFO_RMEM_ALLOC = 0x0 + SK_MEMINFO_RCVBUF = 0x1 + SK_MEMINFO_WMEM_ALLOC = 0x2 + SK_MEMINFO_SNDBUF = 0x3 + SK_MEMINFO_FWD_ALLOC = 0x4 + SK_MEMINFO_WMEM_QUEUED = 0x5 + SK_MEMINFO_OPTMEM = 0x6 + SK_MEMINFO_BACKLOG = 0x7 + SK_MEMINFO_DROPS = 0x8 + SK_MEMINFO_VARS = 0x9 + SKNLGRP_NONE = 0x0 + SKNLGRP_INET_TCP_DESTROY = 0x1 + SKNLGRP_INET_UDP_DESTROY = 0x2 + SKNLGRP_INET6_TCP_DESTROY = 0x3 + SKNLGRP_INET6_UDP_DESTROY = 0x4 + SK_DIAG_BPF_STORAGE_REQ_NONE = 0x0 + SK_DIAG_BPF_STORAGE_REQ_MAP_FD = 0x1 + SK_DIAG_BPF_STORAGE_REP_NONE = 0x0 + SK_DIAG_BPF_STORAGE = 0x1 + SK_DIAG_BPF_STORAGE_NONE = 0x0 + SK_DIAG_BPF_STORAGE_PAD = 0x1 + SK_DIAG_BPF_STORAGE_MAP_ID = 0x2 + SK_DIAG_BPF_STORAGE_MAP_VALUE = 0x3 +) + +type SockDiagReq struct { + Family uint8 + Protocol uint8 +} diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go index 26be94a..6f7d2ac 100644 --- a/vendor/golang.org/x/sys/windows/security_windows.go +++ b/vendor/golang.org/x/sys/windows/security_windows.go @@ -68,6 +68,7 @@ type UserInfo10 struct { //sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo //sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation //sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree +//sys NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) = netapi32.NetUserEnum const ( // do not reorder diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 5c6035d..9f73df7 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -401,6 +401,7 @@ var ( procTransmitFile = modmswsock.NewProc("TransmitFile") procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree") procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation") + procNetUserEnum = modnetapi32.NewProc("NetUserEnum") procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") procNtCreateFile = modntdll.NewProc("NtCreateFile") procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") @@ -3486,6 +3487,14 @@ func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (nete return } +func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) { + r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle)), 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) { r0, _, _ := syscall.Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0) if r0 != 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index c624669..e5c280e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -154,7 +154,7 @@ github.com/aws/smithy-go/waiter # github.com/benbjohnson/clock v1.3.5 ## explicit; go 1.15 github.com/benbjohnson/clock -# github.com/conductorone/baton-sdk v0.1.38 +# github.com/conductorone/baton-sdk v0.2.7 ## explicit; go 1.21 github.com/conductorone/baton-sdk/internal/connector github.com/conductorone/baton-sdk/pb/c1/c1z/v1 @@ -176,13 +176,16 @@ github.com/conductorone/baton-sdk/pkg/dotc1z github.com/conductorone/baton-sdk/pkg/dotc1z/manager github.com/conductorone/baton-sdk/pkg/dotc1z/manager/local github.com/conductorone/baton-sdk/pkg/dotc1z/manager/s3 +github.com/conductorone/baton-sdk/pkg/field github.com/conductorone/baton-sdk/pkg/helpers github.com/conductorone/baton-sdk/pkg/logging +github.com/conductorone/baton-sdk/pkg/metrics github.com/conductorone/baton-sdk/pkg/pagination github.com/conductorone/baton-sdk/pkg/provisioner github.com/conductorone/baton-sdk/pkg/ratelimit github.com/conductorone/baton-sdk/pkg/sdk github.com/conductorone/baton-sdk/pkg/sync +github.com/conductorone/baton-sdk/pkg/sync/expand github.com/conductorone/baton-sdk/pkg/tasks github.com/conductorone/baton-sdk/pkg/tasks/c1api github.com/conductorone/baton-sdk/pkg/tasks/local @@ -190,14 +193,19 @@ github.com/conductorone/baton-sdk/pkg/types github.com/conductorone/baton-sdk/pkg/types/entitlement github.com/conductorone/baton-sdk/pkg/types/grant github.com/conductorone/baton-sdk/pkg/types/resource +github.com/conductorone/baton-sdk/pkg/types/tasks github.com/conductorone/baton-sdk/pkg/types/ticket github.com/conductorone/baton-sdk/pkg/ugrpc github.com/conductorone/baton-sdk/pkg/uhttp github.com/conductorone/baton-sdk/pkg/us3 github.com/conductorone/baton-sdk/pkg/utls +# github.com/deckarep/golang-set/v2 v2.6.0 +## explicit; go 1.18 +github.com/deckarep/golang-set/v2 # github.com/doug-martin/goqu/v9 v9.19.0 ## explicit; go 1.12 github.com/doug-martin/goqu/v9 +github.com/doug-martin/goqu/v9/dialect/sqlite3 github.com/doug-martin/goqu/v9/exec github.com/doug-martin/goqu/v9/exp github.com/doug-martin/goqu/v9/internal/errors @@ -370,6 +378,15 @@ github.com/tklauser/numcpus # github.com/yusufpapurcu/wmi v1.2.4 ## explicit; go 1.16 github.com/yusufpapurcu/wmi +# go.opentelemetry.io/otel v1.27.0 +## explicit; go 1.21 +go.opentelemetry.io/otel/attribute +go.opentelemetry.io/otel/internal +go.opentelemetry.io/otel/internal/attribute +# go.opentelemetry.io/otel/metric v1.27.0 +## explicit; go 1.21 +go.opentelemetry.io/otel/metric +go.opentelemetry.io/otel/metric/embedded # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr @@ -387,7 +404,7 @@ go.uber.org/zap/internal/exit go.uber.org/zap/internal/pool go.uber.org/zap/internal/stacktrace go.uber.org/zap/zapcore -# golang.org/x/crypto v0.23.0 +# golang.org/x/crypto v0.24.0 ## explicit; go 1.18 golang.org/x/crypto/blowfish golang.org/x/crypto/chacha20 @@ -410,7 +427,7 @@ golang.org/x/exp/slices golang.org/x/exp/slog golang.org/x/exp/slog/internal golang.org/x/exp/slog/internal/buffer -# golang.org/x/net v0.25.0 +# golang.org/x/net v0.26.0 ## explicit; go 1.18 golang.org/x/net/http/httpguts golang.org/x/net/http2 @@ -428,7 +445,7 @@ golang.org/x/oauth2/jwt # golang.org/x/sync v0.7.0 ## explicit; go 1.18 golang.org/x/sync/semaphore -# golang.org/x/sys v0.20.0 +# golang.org/x/sys v0.21.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/unix @@ -438,7 +455,7 @@ golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/debug golang.org/x/sys/windows/svc/eventlog golang.org/x/sys/windows/svc/mgr -# golang.org/x/text v0.15.0 +# golang.org/x/text v0.16.0 ## explicit; go 1.18 golang.org/x/text/encoding golang.org/x/text/encoding/internal