From 51108f8cbb02539f463b077dfc72708fa5a969e8 Mon Sep 17 00:00:00 2001 From: Thomas Krampl Date: Thu, 13 Jun 2024 16:13:23 +0200 Subject: [PATCH] Even morerer directerer integration with kolide Co-authored-by: Vegar Sechmann Molvig --- cmd/apiserver/main.go | 30 +- cmd/gateway-agent/main.go | 5 +- cmd/naisdevice-agent/main.go | 2 +- cmd/prometheus-agent/main.go | 2 +- go.mod | 51 +- go.sum | 43 ++ internal/apiserver/api/admin.go | 8 +- internal/apiserver/api/device.go | 68 +-- internal/apiserver/api/filters.go | 3 +- internal/apiserver/api/grpcserver_test.go | 39 +- internal/apiserver/auth/sessionstore_test.go | 15 +- internal/apiserver/database/database.go | 139 ++++- internal/apiserver/database/database_test.go | 11 +- internal/apiserver/database/interface.go | 3 + .../apiserver/database/mock_api_server.go | 110 ++++ .../apiserver/database/queries/devices.sql | 13 +- .../schema/0004_device_issues.down.sql | 6 + .../database/schema/0004_device_issues.up.sql | 6 + internal/apiserver/kolide/cache.go | 6 + internal/apiserver/kolide/check.go | 7 +- internal/apiserver/kolide/client.go | 127 +++-- internal/apiserver/kolide/client_test.go | 2 + internal/apiserver/kolide/event_handler.go | 7 +- internal/apiserver/kolide/fakeclient.go | 36 +- internal/apiserver/kolide/transport.go | 3 + internal/apiserver/kolide/types.go | 5 + internal/apiserver/metrics/metrics.go | 18 +- internal/apiserver/sqlc/db.go | 10 + internal/apiserver/sqlc/devices.sql.go | 54 +- internal/apiserver/sqlc/models.go | 3 + internal/apiserver/sqlc/querier.go | 3 +- internal/apiserver/sqlc/sessions.sql.go | 15 +- internal/controlplane-cli/enrollgateway.go | 9 +- internal/controlplane-cli/kolide.go | 6 +- internal/controlplane-cli/sessions.go | 8 +- .../runtimeconfig/runtimeconfig.go | 5 +- .../integration_test/device_agent_test.go | 2 +- .../integration_test/gateway_agent_test.go | 2 +- internal/integration_test/integration_test.go | 76 +-- internal/pb/protobuf-api.pb.go | 530 +++++++++--------- internal/pb/protobuf-api.proto | 7 +- internal/pb/protobuf-api_grpc.pb.go | 2 +- internal/systray/systray.go | 2 +- 43 files changed, 918 insertions(+), 581 deletions(-) create mode 100644 internal/apiserver/database/schema/0004_device_issues.down.sql create mode 100644 internal/apiserver/database/schema/0004_device_issues.up.sql diff --git a/cmd/apiserver/main.go b/cmd/apiserver/main.go index ae4de8c9e..c9df1cd9c 100644 --- a/cmd/apiserver/main.go +++ b/cmd/apiserver/main.go @@ -9,6 +9,7 @@ import ( "net/netip" "os" "os/signal" + "slices" "syscall" "time" @@ -200,18 +201,17 @@ func run(log *logrus.Entry, cfg config.Config) error { return fmt.Errorf("kolide integration enabled but no kolide-api-token provided") } - log.Info("Kolide client configured, populating cache...") - - kolideClient = kolide.New(cfg.KolideApiToken, log.WithField("component", "kolide-client")) - err := kolideClient.RefreshCache(ctx) - if err != nil { - return fmt.Errorf("initial kolide cache warmup: %w", err) - } + go func() { + log.Info("Kolide client configured, populating cache...") - kolideRefreshInterval := 1 * time.Minute - log.Infof("Kolide cache populated, will auto refresh every %v", kolideRefreshInterval) + kolideClient = kolide.New(cfg.KolideApiToken, log.WithField("component", "kolide-client")) + err := kolideClient.RefreshCache(ctx) + if err != nil { + log.Errorf("initial kolide cache warmup: %v", err) + } - go func() { + kolideRefreshInterval := 1 * time.Minute + log.Infof("Kolide cache populated, will auto refresh every %v", kolideRefreshInterval) sleep := time.NewTicker(kolideRefreshInterval) for { select { @@ -324,18 +324,22 @@ func run(log *logrus.Entry, cfg config.Config) error { } changed := false - if device.Healthy != event.GetState().Healthy() { + failures, err := kolideClient.GetDeviceFailures(ctx, device.ExternalID) + if err != nil { + return err + } + if slices.ContainsFunc(device.Issues, api.AfterGracePeriod) != slices.ContainsFunc(failures, api.AfterGracePeriod) { changed = true } - device.Healthy = event.GetState().Healthy() + device.Issues = failures device.LastUpdated = event.GetTimestamp() + sessions.UpdateDevice(device) err = db.UpdateDevices(ctx, []*pb.Device{device}) if err != nil { return err } if changed { - sessions.UpdateDevice(device) grpcHandler.SendDeviceConfiguration(device) grpcHandler.SendAllGatewayConfigurations() } diff --git a/cmd/gateway-agent/main.go b/cmd/gateway-agent/main.go index acc3e1eda..0216e7b3e 100644 --- a/cmd/gateway-agent/main.go +++ b/cmd/gateway-agent/main.go @@ -13,7 +13,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" - "github.com/nais/device/internal/gateway-agent" + gateway_agent "github.com/nais/device/internal/gateway-agent" "github.com/nais/device/internal/gateway-agent/config" "github.com/nais/device/internal/passwordhash" "github.com/nais/device/internal/pb" @@ -160,8 +160,7 @@ func run(log *logrus.Entry, cfg config.Config) error { } log.Infof("Attempting gRPC connection to API server on %s...", cfg.APIServerURL) - apiserver, err := grpc.DialContext( - ctx, + apiserver, err := grpc.NewClient( cfg.APIServerURL, grpc.WithTransportCredentials(insecure.NewCredentials()), ) diff --git a/cmd/naisdevice-agent/main.go b/cmd/naisdevice-agent/main.go index 1c6b5c6e3..71fd55dd0 100644 --- a/cmd/naisdevice-agent/main.go +++ b/cmd/naisdevice-agent/main.go @@ -131,7 +131,7 @@ func run(ctx context.Context, log *logrus.Entry, cfg *config.Config, notifier no if cfg.LocalAPIServer { client = pb.NewMockHelperClient(log) } else { - connection, err := grpc.Dial( + connection, err := grpc.NewClient( "unix:"+cfg.DeviceAgentHelperAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithIdleTimeout(10*time.Hour), diff --git a/cmd/prometheus-agent/main.go b/cmd/prometheus-agent/main.go index 1b1da7576..a9c5da34a 100644 --- a/cmd/prometheus-agent/main.go +++ b/cmd/prometheus-agent/main.go @@ -108,7 +108,7 @@ func run(log *logrus.Entry, cfg config.Config) error { return fmt.Errorf("apply initial WireGuard config: %w", err) } - grpcClient, err := grpc.DialContext(ctx, cfg.APIServerURL, grpc.WithTransportCredentials(insecure.NewCredentials())) + grpcClient, err := grpc.NewClient(cfg.APIServerURL, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return fmt.Errorf("grpc dial: %w", err) } diff --git a/go.mod b/go.mod index dc83bd1be..2e8f91516 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e github.com/akavel/rsrc v0.10.2 github.com/coreos/go-iptables v0.7.0 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1 github.com/golang-migrate/migrate/v4 v4.16.2 github.com/golangci/golangci-lint v1.55.2 @@ -16,7 +17,7 @@ require ( github.com/jackmordaunt/icns/v2 v2.2.6 github.com/kelseyhightower/envconfig v1.4.0 github.com/lestrrat-go/jwx v1.2.29 - github.com/nais/kolide-event-handler v0.0.0-20220214150046-ca39d969eea0 + github.com/nais/kolide-event-handler v0.0.0-20240613124908-c26ee6800776 github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da github.com/prometheus/client_golang v1.17.0 github.com/sirupsen/logrus v1.9.3 @@ -25,26 +26,26 @@ require ( github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.25.7 github.com/vektra/mockery/v2 v2.40.1 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 - go.opentelemetry.io/otel v1.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 + go.opentelemetry.io/otel v1.24.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1 - go.opentelemetry.io/otel/metric v1.23.1 + go.opentelemetry.io/otel/metric v1.24.0 go.opentelemetry.io/otel/sdk v1.23.1 go.opentelemetry.io/otel/sdk/metric v1.23.1 - go.opentelemetry.io/otel/trace v1.23.1 - golang.org/x/crypto v0.23.0 + go.opentelemetry.io/otel/trace v1.24.0 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 golang.org/x/oauth2 v0.18.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.20.0 - golang.org/x/tools v0.21.0 - golang.org/x/vuln v1.0.4 - google.golang.org/api v0.167.0 - google.golang.org/grpc v1.62.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 - google.golang.org/protobuf v1.33.0 + golang.org/x/sys v0.21.0 + golang.org/x/tools v0.22.0 + golang.org/x/vuln v1.1.2 + google.golang.org/api v0.169.0 + google.golang.org/grpc v1.64.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 + google.golang.org/protobuf v1.34.2 honnef.co/go/tools v0.4.7 mvdan.cc/gofumpt v0.6.0 ) @@ -53,7 +54,7 @@ require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute v1.24.0 // indirect + cloud.google.com/go/compute v1.25.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.6 // indirect github.com/4meepo/tagalign v1.3.3 // indirect @@ -61,7 +62,7 @@ require ( github.com/Antonboom/errname v0.1.12 // indirect github.com/Antonboom/nilnil v0.1.7 // indirect github.com/Antonboom/testifylint v0.2.3 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -93,7 +94,6 @@ require ( github.com/curioswitch/go-reassign v0.2.0 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/daixiang0/gci v0.11.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -124,7 +124,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect @@ -277,16 +277,17 @@ require ( go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index be3dcb6c4..a630a764d 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,7 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -64,6 +65,8 @@ github.com/Antonboom/testifylint v0.2.3/go.mod h1:IYaXaOX9NbfAyO+Y04nfjGI8wDemC1 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= @@ -141,6 +144,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -280,6 +284,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= @@ -518,6 +523,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nais/kolide-event-handler v0.0.0-20220214150046-ca39d969eea0 h1:LxyCRFoauunfCNPR5Xr41nObno+zr4egCP6gHi1RfUc= github.com/nais/kolide-event-handler v0.0.0-20220214150046-ca39d969eea0/go.mod h1:LGJuW4TjMUA6TlddXLlJi9jy2mflgR2eZdFQ8VwJXTE= +github.com/nais/kolide-event-handler v0.0.0-20240613123606-f7c87d8946e8 h1:njwxkfpT65h/3GhX3aNDknE6MWey0QyX+iGWT5b2XcA= +github.com/nais/kolide-event-handler v0.0.0-20240613123606-f7c87d8946e8/go.mod h1:7Dl7mqto/Jb4Ng8NntCAsnnYCX2clIuOUd6X7b5s+7o= +github.com/nais/kolide-event-handler v0.0.0-20240613124908-c26ee6800776 h1:W5G0sRZnAOvr7Gt6Su3BUpf0dqVotuokB1Z1sKUPYQI= +github.com/nais/kolide-event-handler v0.0.0-20240613124908-c26ee6800776/go.mod h1:1Ta1n1Q+EtH7EIHuU3/svAkCQ8AWk9/qfs6nBj0lhxE= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -769,10 +778,13 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1 h1:q/Nj5/2TZRIt6PderQ9oU0M00fzoe8UZuINGw6ETGTw= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1/go.mod h1:DTE9yAu6r08jU3xa68GiSeI7oRcSEQ2RpKbbQGO+dWM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 h1:o8iWeVFa1BcLtVEV0LzrCxV2/55tB3xLxADr6Kyoey4= @@ -781,12 +793,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1 h1:cfuy3 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1/go.mod h1:22jr92C6KwlwItJmQzfixzQM3oyyuYLCfHiMY+rpsPU= go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= go.opentelemetry.io/otel/sdk/metric v1.23.1 h1:T9/8WsYg+ZqIpMWwdISVVrlGb/N0Jr1OHjR/alpKwzg= go.opentelemetry.io/otel/sdk/metric v1.23.1/go.mod h1:8WX6WnNtHCgUruJ4TJ+UssQjMtpxkpX0zveQC8JG/E0= go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.tmz.dev/musttag v0.7.2 h1:1J6S9ipDbalBSODNT5jCep8dhZyMr4ttnjQagmGYR5s= @@ -818,6 +832,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -834,6 +850,8 @@ golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20231006140011-7918f672742d h1:NRn/Afz91uVUyEsxMp4lGGxpr5y1qz+Iko60dbkfvLQ= golang.org/x/exp/typeparams v0.0.0-20231006140011-7918f672742d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8 h1:WKP3FgLqWfVutBnw/dr+LNg4fzjyTQP5o+ELTIyoBrs= +golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -864,6 +882,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -907,6 +927,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 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.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -992,6 +1014,10 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.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/telemetry v0.0.0-20240612191826-8cad58b3fcbb h1:0Ge50tvTqbHEyuQDgCYypgL2afqNjRNdl4GHPJuN9QY= +golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb/go.mod h1:n38mvGdgc4dA684EC4NwQwoPKSw4jyKw8/DgZHDA1Dk= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1003,6 +1029,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1018,6 +1046,8 @@ 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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1088,8 +1118,12 @@ golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= 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/vuln v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I= golang.org/x/vuln v1.0.4/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ= +golang.org/x/vuln v1.1.2 h1:UkLxe+kAMcrNBpGrFbU0Mc5l7cX97P2nhy21wx5+Qbk= +golang.org/x/vuln v1.1.2/go.mod h1:2o3fRKD8Uz9AraAL3lwd/grWBv+t+SeJnPcqBUJrY24= 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= @@ -1114,6 +1148,7 @@ google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSr google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE= google.golang.org/api v0.167.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= +google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1155,8 +1190,10 @@ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 h1:SzXBGiWM1LNVYLCRP3e0/Gsze804l4jGoJ5lYysEO5I= google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 h1:DKU1r6Tj5s1vlU/moGhuGz7E3xRfwjdAfDzbsaQJtEY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1173,8 +1210,12 @@ google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1190,6 +1231,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/apiserver/api/admin.go b/internal/apiserver/api/admin.go index 78f9e1234..f24e6510b 100644 --- a/internal/apiserver/api/admin.go +++ b/internal/apiserver/api/admin.go @@ -88,18 +88,12 @@ func (s *grpcServer) GetKolideCache(ctx context.Context, r *pb.GetKolideCacheReq return nil, status.Errorf(codes.Unauthenticated, err.Error()) } - devices, err := s.kolideClient.DumpDevices() - if err != nil { - return nil, err - } - checks, err := s.kolideClient.DumpChecks() if err != nil { return nil, err } return &pb.GetKolideCacheResponse{ - RawDevices: devices, - RawChecks: checks, + RawChecks: checks, }, nil } diff --git a/internal/apiserver/api/device.go b/internal/apiserver/api/device.go index fbf0e6bd0..f756991f0 100644 --- a/internal/apiserver/api/device.go +++ b/internal/apiserver/api/device.go @@ -4,10 +4,8 @@ import ( "context" "fmt" "slices" - "sync" "time" - "github.com/nais/device/internal/apiserver/kolide" apiserver_metrics "github.com/nais/device/internal/apiserver/metrics" "github.com/nais/device/internal/pb" "google.golang.org/grpc/codes" @@ -30,24 +28,6 @@ func (s *grpcServer) GetDeviceConfiguration(request *pb.GetDeviceConfigurationRe apiserver_metrics.DevicesConnected.Set(float64(len(s.deviceConfigTrigger))) s.deviceConfigTriggerLock.Unlock() - issues := []*pb.DeviceIssue{} - kolideDeviceStream := make(chan kolide.Device) - if s.kolideClient != nil { - kolideDevice, err := s.kolideClient.GetDevice(stream.Context(), session.GetDevice().GetUsername(), session.GetDevice().GetPlatform(), session.GetDevice().GetSerial()) - if err != nil { - return err - } - issues = kolideDevice.Issues() - - watchDone := &sync.Mutex{} - watchDone.Lock() - go s.watchKolideDevice(stream.Context(), s.kolideClient, kolideDevice, kolideDeviceStream, watchDone) - defer func() { - watchDone.Lock() - close(kolideDeviceStream) - }() - } - if len(session.GetGroups()) == 0 { s.log.WithField("deviceId", session.GetDevice().GetId()).Warnf("session with no groups detected") } @@ -61,23 +41,36 @@ func (s *grpcServer) GetDeviceConfiguration(request *pb.GetDeviceConfigurationRe timeout := time.After(time.Until(session.GetExpiry().AsTime())) + updateDevice := time.NewTicker(1 * time.Minute) for { select { + + case <-updateDevice.C: + device, err := s.db.ReadDeviceById(stream.Context(), deviceId) + if err != nil { + s.log.Errorf("get device from kolide: %v", err) + continue + } + + if len(device.Issues) > 0 { + select { + case trigger <- struct{}{}: + default: + } + } case <-timeout: s.log.Debugf("session for device %d timed out, tearing down", deviceId) return nil case <-stream.Context().Done(): // Disconnect s.log.Debugf("stream context for device %d done, tearing down", deviceId) return nil - case d := <-kolideDeviceStream: - issues = d.Issues() case <-trigger: // Send config triggered session, err := s.sessionStore.Get(stream.Context(), request.SessionKey) if err != nil { return err } - cfg, err := s.makeDeviceConfiguration(stream.Context(), session, issues) + cfg, err := s.makeDeviceConfiguration(stream.Context(), session) if err != nil { s.log.Errorf("make device config: %v", err) } else { @@ -90,37 +83,16 @@ func (s *grpcServer) GetDeviceConfiguration(request *pb.GetDeviceConfigurationRe } } -func (s *grpcServer) watchKolideDevice(ctx context.Context, kolideClient kolide.Client, d kolide.Device, deviceStream chan<- kolide.Device, done *sync.Mutex) { - defer done.Unlock() - - ticker := time.NewTicker(5 * time.Minute) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - d, err := kolideClient.GetDevice(ctx, d.AssignedOwner.Email, d.Platform, d.Serial) - if err != nil { - s.log.Errorf("get device from kolide: %v", err) - } else { - deviceStream <- d - } - } - } -} - -func (s *grpcServer) makeDeviceConfiguration(ctx context.Context, sessionInfo *pb.Session, issues []*pb.DeviceIssue) (*pb.GetDeviceConfigurationResponse, error) { +func (s *grpcServer) makeDeviceConfiguration(ctx context.Context, sessionInfo *pb.Session) (*pb.GetDeviceConfigurationResponse, error) { device, err := s.db.ReadDeviceById(ctx, sessionInfo.GetDevice().GetId()) if err != nil { return nil, fmt.Errorf("read device from db: %w", err) } - if !device.GetHealthy() || slices.ContainsFunc(issues, AfterGracePeriod) { + if slices.ContainsFunc(device.Issues, AfterGracePeriod) { return &pb.GetDeviceConfigurationResponse{ Status: pb.DeviceConfigurationStatus_DeviceUnhealthy, - Issues: issues, + Issues: device.Issues, }, nil } @@ -133,7 +105,7 @@ func (s *grpcServer) makeDeviceConfiguration(ctx context.Context, sessionInfo *p return &pb.GetDeviceConfigurationResponse{ Status: pb.DeviceConfigurationStatus_DeviceHealthy, - Issues: issues, + Issues: device.Issues, Gateways: gateways, }, nil } diff --git a/internal/apiserver/api/filters.go b/internal/apiserver/api/filters.go index ab3d0432a..62dc675c3 100644 --- a/internal/apiserver/api/filters.go +++ b/internal/apiserver/api/filters.go @@ -1,6 +1,7 @@ package api import ( + "slices" "time" "github.com/nais/device/internal/apiserver/jita" @@ -39,7 +40,7 @@ func healthy(devices []*pb.Device) []*pb.Device { var healthyDevices []*pb.Device for _, device := range devices { - if device.GetHealthy() { + if !slices.ContainsFunc(device.GetIssues(), AfterGracePeriod) { healthyDevices = append(healthyDevices, device) } } diff --git a/internal/apiserver/api/grpcserver_test.go b/internal/apiserver/api/grpcserver_test.go index 9b5f7eafc..c5bef51a4 100644 --- a/internal/apiserver/api/grpcserver_test.go +++ b/internal/apiserver/api/grpcserver_test.go @@ -24,23 +24,11 @@ import ( const bufSize = 1024 * 1024 -var ( - testDevice = &pb.Device{ - Healthy: true, - Serial: "serial", - Platform: "darwin", - Username: "user@example.com", - } - now = time.Now() - testKolideDevice = kolide.Device{ - LastSeenAt: &now, - Serial: testDevice.Serial, - Platform: testDevice.Platform, - AssignedOwner: kolide.DeviceOwner{ - Email: testDevice.Username, - }, - } -) +var testDevice = &pb.Device{ + Serial: "serial", + Platform: "darwin", + Username: "user@example.com", +} func contextBufDialer(listener *bufconn.Listener) func(context.Context, string) (net.Conn, error) { return func(context.Context, string) (net.Conn, error) { @@ -74,7 +62,7 @@ func TestGetDeviceConfiguration(t *testing.T) { }, }, nil) - kolideClient := kolide.NewFakeClient().WithDevice(testKolideDevice).Build() + kolideClient := kolide.NewFakeClient().Build() gatewayAuthenticator := auth.NewGatewayAuthenticator(db) @@ -88,9 +76,8 @@ func TestGetDeviceConfiguration(t *testing.T) { assert.NoError(t, err) }() - conn, err := grpc.DialContext( - ctx, - "bufnet", + conn, err := grpc.NewClient( + "passthrough:///bufnet", grpc.WithContextDialer(contextBufDialer(lis)), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -147,9 +134,8 @@ func TestGatewayPasswordAuthentication(t *testing.T) { pb.RegisterAPIServerServer(s, server) go s.Serve(lis) - conn, err := grpc.DialContext( - ctx, - "bufnet", + conn, err := grpc.NewClient( + "passthrough:///bufnet", grpc.WithContextDialer(contextBufDialer(lis)), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -203,9 +189,8 @@ func TestGatewayPasswordAuthenticationFail(t *testing.T) { pb.RegisterAPIServerServer(s, server) go s.Serve(lis) - conn, err := grpc.DialContext( - ctx, - "bufnet", + conn, err := grpc.NewClient( + "passthrough:///bufnet", grpc.WithContextDialer(contextBufDialer(lis)), grpc.WithTransportCredentials(insecure.NewCredentials()), ) diff --git a/internal/apiserver/auth/sessionstore_test.go b/internal/apiserver/auth/sessionstore_test.go index bc8c5e8a0..c9d1b7644 100644 --- a/internal/apiserver/auth/sessionstore_test.go +++ b/internal/apiserver/auth/sessionstore_test.go @@ -5,12 +5,14 @@ import ( "errors" "strconv" "testing" + "time" "github.com/nais/device/internal/apiserver/auth" "github.com/nais/device/internal/apiserver/database" "github.com/nais/device/internal/pb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestSessionStore_SetAndGetFromCache(t *testing.T) { @@ -125,13 +127,14 @@ func TestSessionStore_UpdateDevice(t *testing.T) { db := database.NewMockAPIServer(t) store := auth.NewSessionStore(db) + now := time.Now() sessions := make([]*pb.Session, 20) for i := range sessions { sessions[i] = &pb.Session{ Key: strconv.Itoa(i), Device: &pb.Device{ - Id: int64(i), - Healthy: false, + Id: int64(i), + LastSeen: timestamppb.New(now), }, } } @@ -145,17 +148,17 @@ func TestSessionStore_UpdateDevice(t *testing.T) { assert.NoError(t, err) updatedDevice := &pb.Device{ - Id: int64(0), - Healthy: true, + Id: int64(0), + LastSeen: timestamppb.New(now.Add(2 * time.Hour)), } sess, err := store.Get(ctx, "0") assert.NoError(t, err) - assert.False(t, sess.GetDevice().GetHealthy()) + assert.False(t, sess.GetDevice().GetLastSeen().AsTime().Equal(updatedDevice.GetLastSeen().AsTime())) store.UpdateDevice(updatedDevice) sess, err = store.Get(ctx, "0") assert.NoError(t, err) - assert.True(t, sess.GetDevice().GetHealthy()) + assert.True(t, sess.GetDevice().GetLastSeen().AsTime().Equal(updatedDevice.GetLastSeen().AsTime())) } diff --git a/internal/apiserver/database/database.go b/internal/apiserver/database/database.go index 19a8c9cf7..4368cc139 100644 --- a/internal/apiserver/database/database.go +++ b/internal/apiserver/database/database.go @@ -3,6 +3,7 @@ package database import ( "context" "database/sql" + "encoding/json" "errors" "fmt" "strings" @@ -53,23 +54,91 @@ func (db *ApiServerDB) ReadDevices(ctx context.Context) ([]*pb.Device, error) { devices := make([]*pb.Device, 0) for _, row := range rows { - devices = append(devices, sqlcDeviceToPbDevice(*row)) + device, err := sqlcDeviceToPbDevice(*row) + if err != nil { + return nil, fmt.Errorf("converting device %v: %w", row.ID, err) + } + devices = append(devices, device) } return devices, nil } +func (db *ApiServerDB) UpdateSingleDevice(ctx context.Context, externalID, serial, platform string, lastSeen *time.Time, issues []*pb.DeviceIssue) (int64, error) { + var ( + b []byte + err error + ) + if len(issues) > 0 { + b, err = json.Marshal(issues) + if err != nil { + return 0, fmt.Errorf("marshal issues: %w", err) + } + } + + lastSeenCol := sql.NullString{} + if lastSeen != nil { + lastSeenCol = sql.NullString{ + String: timeToString(*lastSeen), + Valid: true, + } + } + return db.queries.UpdateDevice(ctx, sqlc.UpdateDeviceParams{ + Healthy: false, + Serial: serial, + Platform: platform, + LastUpdated: sql.NullString{ + String: timeToString(time.Now().UTC()), + Valid: true, + }, + LastSeen: lastSeenCol, + Issues: sql.NullString{ + String: string(b), + Valid: b != nil, + }, + ExternalID: sql.NullString{ + String: externalID, + Valid: externalID != "", + }, + }) +} + func (db *ApiServerDB) UpdateDevices(ctx context.Context, devices []*pb.Device) error { err := db.queries.Transaction(ctx, func(ctx context.Context, queries *sqlc.Queries) error { for _, device := range devices { - err := queries.UpdateDevice(ctx, sqlc.UpdateDeviceParams{ - Healthy: device.Healthy, + lastSeen := sql.NullString{} + if device.LastSeen != nil { + lastSeen = sql.NullString{ + String: timeToString(device.LastSeen.AsTime().UTC()), + Valid: true, + } + } + + issuesCol := sql.NullString{} + if len(device.Issues) > 0 { + issues, err := json.Marshal(device.Issues) + if err != nil { + return fmt.Errorf("marshal issues: %w", err) + } + issuesCol = sql.NullString{ + String: string(issues), + Valid: true, + } + } + + _, err := queries.UpdateDevice(ctx, sqlc.UpdateDeviceParams{ Serial: device.Serial, Platform: device.Platform, LastUpdated: sql.NullString{ String: timeToString(time.Now().UTC()), Valid: true, }, + LastSeen: lastSeen, + Issues: issuesCol, + ExternalID: sql.NullString{ + String: device.ExternalID, + Valid: device.ExternalID != "", + }, }) if err != nil { return err @@ -316,7 +385,7 @@ func (db *ApiServerDB) ReadDevice(ctx context.Context, publicKey string) (*pb.De return nil, err } - return sqlcDeviceToPbDevice(*device), nil + return sqlcDeviceToPbDevice(*device) } func (db *ApiServerDB) ReadDeviceById(ctx context.Context, deviceID int64) (*pb.Device, error) { @@ -325,7 +394,7 @@ func (db *ApiServerDB) ReadDeviceById(ctx context.Context, deviceID int64) (*pb. return nil, err } - return sqlcDeviceToPbDevice(*device), nil + return sqlcDeviceToPbDevice(*device) } func (db *ApiServerDB) ReadGateways(ctx context.Context) ([]*pb.Gateway, error) { @@ -396,7 +465,7 @@ func (db *ApiServerDB) readExistingIPs(ctx context.Context) ([]string, error) { } func (db *ApiServerDB) ReadDeviceBySerialPlatform(ctx context.Context, serial, platform string) (*pb.Device, error) { - gateway, err := db.queries.GetDeviceBySerialAndPlatform(ctx, sqlc.GetDeviceBySerialAndPlatformParams{ + device, err := db.queries.GetDeviceBySerialAndPlatform(ctx, sqlc.GetDeviceBySerialAndPlatformParams{ Serial: serial, Platform: platform, }) @@ -404,7 +473,7 @@ func (db *ApiServerDB) ReadDeviceBySerialPlatform(ctx context.Context, serial, p return nil, err } - return sqlcDeviceToPbDevice(*gateway), nil + return sqlcDeviceToPbDevice(*device) } func (db *ApiServerDB) AddSessionInfo(ctx context.Context, si *pb.Session) error { @@ -448,7 +517,7 @@ func (db *ApiServerDB) ReadSessionInfo(ctx context.Context, key string) (*pb.Ses return nil, err } - return sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs), nil + return sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs) } func (db *ApiServerDB) ReadSessionInfos(ctx context.Context) ([]*pb.Session, error) { @@ -464,7 +533,12 @@ func (db *ApiServerDB) ReadSessionInfos(ctx context.Context) ([]*pb.Session, err return nil, err } - sessions = append(sessions, sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs)) + session, err := sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs) + if err != nil { + return nil, err + } + + sessions = append(sessions, session) } return sessions, nil @@ -481,7 +555,7 @@ func (db *ApiServerDB) ReadMostRecentSessionInfo(ctx context.Context, deviceID i return nil, err } - return sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs), nil + return sqlcSessionAndDeviceToPbSession(row.Session, row.Device, groupIDs) } func (db *ApiServerDB) getNextAvailableIPv4(ctx context.Context) (string, error) { @@ -517,23 +591,37 @@ func (db *ApiServerDB) RemoveExpiredSessions(ctx context.Context) error { return db.queries.RemoveExpiredSessions(ctx) } -func sqlcDeviceToPbDevice(sqlcDevice sqlc.Device) *pb.Device { +func (db *ApiServerDB) ClearDeviceIssuesExceptFor(ctx context.Context, deviceIds []int64) error { + return db.queries.ClearDeviceIssuesExceptFor(ctx, deviceIds) +} + +func sqlcDeviceToPbDevice(sqlcDevice sqlc.Device) (*pb.Device, error) { pbDevice := &pb.Device{ - Id: int64(sqlcDevice.ID), - Serial: sqlcDevice.Serial, - Healthy: sqlcDevice.Healthy, - PublicKey: sqlcDevice.PublicKey, - Ipv4: sqlcDevice.Ipv4, - Ipv6: sqlcDevice.Ipv6, - Username: sqlcDevice.Username, - Platform: string(sqlcDevice.Platform), + Id: int64(sqlcDevice.ID), + Serial: sqlcDevice.Serial, + PublicKey: sqlcDevice.PublicKey, + Ipv4: sqlcDevice.Ipv4, + Ipv6: sqlcDevice.Ipv6, + Username: sqlcDevice.Username, + ExternalID: sqlcDevice.ExternalID.String, + Platform: string(sqlcDevice.Platform), + } + + if sqlcDevice.Issues.Valid { + err := json.Unmarshal([]byte(sqlcDevice.Issues.String), &pbDevice.Issues) + if err != nil { + return nil, fmt.Errorf("unmarshal issues: %w", err) + } } if sqlcDevice.LastUpdated.Valid { pbDevice.LastUpdated = timestamppb.New(stringToTime(sqlcDevice.LastUpdated.String)) } + if sqlcDevice.LastSeen.Valid { + pbDevice.LastSeen = timestamppb.New(stringToTime(sqlcDevice.LastSeen.String)) + } - return pbDevice + return pbDevice, nil } func timeToString(t time.Time) string { @@ -574,12 +662,17 @@ func sqlcGatewayToPbGateway(g sqlc.Gateway, groupIDs []string, routes []*sqlc.Ge } } -func sqlcSessionAndDeviceToPbSession(s sqlc.Session, d sqlc.Device, groupIDs []string) *pb.Session { +func sqlcSessionAndDeviceToPbSession(s sqlc.Session, d sqlc.Device, groupIDs []string) (*pb.Session, error) { + device, err := sqlcDeviceToPbDevice(d) + if err != nil { + return nil, fmt.Errorf("converting device: %w", err) + } + return &pb.Session{ Key: s.Key, - Device: sqlcDeviceToPbDevice(d), + Device: device, ObjectID: s.ObjectID, Expiry: timestamppb.New(stringToTime(s.Expiry)), Groups: groupIDs, - } + }, nil } diff --git a/internal/apiserver/database/database_test.go b/internal/apiserver/database/database_test.go index 48449dbac..5aa64d3de 100644 --- a/internal/apiserver/database/database_test.go +++ b/internal/apiserver/database/database_test.go @@ -96,17 +96,26 @@ func TestAddDevice(t *testing.T) { defer cancel() serial := "serial" + issues := []*pb.DeviceIssue{ + { + Title: "integration test issue", + }, + } d := &pb.Device{Username: "username", PublicKey: "publickey", Serial: serial, Platform: "darwin"} err := db.AddDevice(ctx, d) assert.NoError(t, err) + ls := d.LastSeen.AsTime() + _, err = db.UpdateSingleDevice(ctx, d.ExternalID, d.Serial, d.Platform, &ls, issues) + assert.NoError(t, err) + device, err := db.ReadDevice(ctx, d.PublicKey) assert.NoError(t, err) assert.Equal(t, d.Username, device.Username) assert.Equal(t, d.PublicKey, device.PublicKey) assert.Equal(t, d.Serial, device.Serial) assert.Equal(t, d.Platform, device.Platform) - assert.False(t, device.Healthy) + assert.EqualValues(t, issues, device.Issues) err = db.AddDevice(ctx, d) assert.NoError(t, err) diff --git a/internal/apiserver/database/interface.go b/internal/apiserver/database/interface.go index 483d33abf..bc0e1a9bf 100644 --- a/internal/apiserver/database/interface.go +++ b/internal/apiserver/database/interface.go @@ -2,6 +2,7 @@ package database import ( "context" + "time" "github.com/nais/device/internal/pb" ) @@ -23,4 +24,6 @@ type APIServer interface { ReadSessionInfos(ctx context.Context) ([]*pb.Session, error) RemoveExpiredSessions(ctx context.Context) error ReadMostRecentSessionInfo(ctx context.Context, deviceID int64) (*pb.Session, error) + ClearDeviceIssuesExceptFor(ctx context.Context, deviceIds []int64) error + UpdateSingleDevice(ctx context.Context, externalID, serial, platform string, lastSeen *time.Time, issues []*pb.DeviceIssue) (int64, error) } diff --git a/internal/apiserver/database/mock_api_server.go b/internal/apiserver/database/mock_api_server.go index 75b3c8c15..c9046c315 100644 --- a/internal/apiserver/database/mock_api_server.go +++ b/internal/apiserver/database/mock_api_server.go @@ -7,6 +7,8 @@ import ( pb "github.com/nais/device/internal/pb" mock "github.com/stretchr/testify/mock" + + time "time" ) // MockAPIServer is an autogenerated mock type for the APIServer type @@ -163,6 +165,53 @@ func (_c *MockAPIServer_AddSessionInfo_Call) RunAndReturn(run func(context.Conte return _c } +// ClearDeviceIssuesExceptFor provides a mock function with given fields: ctx, deviceIds +func (_m *MockAPIServer) ClearDeviceIssuesExceptFor(ctx context.Context, deviceIds []int64) error { + ret := _m.Called(ctx, deviceIds) + + if len(ret) == 0 { + panic("no return value specified for ClearDeviceIssuesExceptFor") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []int64) error); ok { + r0 = rf(ctx, deviceIds) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockAPIServer_ClearDeviceIssuesExceptFor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearDeviceIssuesExceptFor' +type MockAPIServer_ClearDeviceIssuesExceptFor_Call struct { + *mock.Call +} + +// ClearDeviceIssuesExceptFor is a helper method to define mock.On call +// - ctx context.Context +// - deviceIds []int64 +func (_e *MockAPIServer_Expecter) ClearDeviceIssuesExceptFor(ctx interface{}, deviceIds interface{}) *MockAPIServer_ClearDeviceIssuesExceptFor_Call { + return &MockAPIServer_ClearDeviceIssuesExceptFor_Call{Call: _e.mock.On("ClearDeviceIssuesExceptFor", ctx, deviceIds)} +} + +func (_c *MockAPIServer_ClearDeviceIssuesExceptFor_Call) Run(run func(ctx context.Context, deviceIds []int64)) *MockAPIServer_ClearDeviceIssuesExceptFor_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]int64)) + }) + return _c +} + +func (_c *MockAPIServer_ClearDeviceIssuesExceptFor_Call) Return(_a0 error) *MockAPIServer_ClearDeviceIssuesExceptFor_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAPIServer_ClearDeviceIssuesExceptFor_Call) RunAndReturn(run func(context.Context, []int64) error) *MockAPIServer_ClearDeviceIssuesExceptFor_Call { + _c.Call.Return(run) + return _c +} + // ReadDevice provides a mock function with given fields: ctx, publicKey func (_m *MockAPIServer) ReadDevice(ctx context.Context, publicKey string) (*pb.Device, error) { ret := _m.Called(ctx, publicKey) @@ -879,6 +928,67 @@ func (_c *MockAPIServer_UpdateGatewayDynamicFields_Call) RunAndReturn(run func(c return _c } +// UpdateSingleDevice provides a mock function with given fields: ctx, externalID, serial, platform, lastSeen, issues +func (_m *MockAPIServer) UpdateSingleDevice(ctx context.Context, externalID string, serial string, platform string, lastSeen *time.Time, issues []*pb.DeviceIssue) (int64, error) { + ret := _m.Called(ctx, externalID, serial, platform, lastSeen, issues) + + if len(ret) == 0 { + panic("no return value specified for UpdateSingleDevice") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *time.Time, []*pb.DeviceIssue) (int64, error)); ok { + return rf(ctx, externalID, serial, platform, lastSeen, issues) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *time.Time, []*pb.DeviceIssue) int64); ok { + r0 = rf(ctx, externalID, serial, platform, lastSeen, issues) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *time.Time, []*pb.DeviceIssue) error); ok { + r1 = rf(ctx, externalID, serial, platform, lastSeen, issues) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockAPIServer_UpdateSingleDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateSingleDevice' +type MockAPIServer_UpdateSingleDevice_Call struct { + *mock.Call +} + +// UpdateSingleDevice is a helper method to define mock.On call +// - ctx context.Context +// - externalID string +// - serial string +// - platform string +// - lastSeen *time.Time +// - issues []*pb.DeviceIssue +func (_e *MockAPIServer_Expecter) UpdateSingleDevice(ctx interface{}, externalID interface{}, serial interface{}, platform interface{}, lastSeen interface{}, issues interface{}) *MockAPIServer_UpdateSingleDevice_Call { + return &MockAPIServer_UpdateSingleDevice_Call{Call: _e.mock.On("UpdateSingleDevice", ctx, externalID, serial, platform, lastSeen, issues)} +} + +func (_c *MockAPIServer_UpdateSingleDevice_Call) Run(run func(ctx context.Context, externalID string, serial string, platform string, lastSeen *time.Time, issues []*pb.DeviceIssue)) *MockAPIServer_UpdateSingleDevice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(*time.Time), args[5].([]*pb.DeviceIssue)) + }) + return _c +} + +func (_c *MockAPIServer_UpdateSingleDevice_Call) Return(_a0 int64, _a1 error) *MockAPIServer_UpdateSingleDevice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockAPIServer_UpdateSingleDevice_Call) RunAndReturn(run func(context.Context, string, string, string, *time.Time, []*pb.DeviceIssue) (int64, error)) *MockAPIServer_UpdateSingleDevice_Call { + _c.Call.Return(run) + return _c +} + // NewMockAPIServer creates a new instance of MockAPIServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockAPIServer(t interface { diff --git a/internal/apiserver/database/queries/devices.sql b/internal/apiserver/database/queries/devices.sql index 9453f4809..253c04dbe 100644 --- a/internal/apiserver/database/queries/devices.sql +++ b/internal/apiserver/database/queries/devices.sql @@ -10,13 +10,20 @@ SELECT * FROM devices WHERE id = @id; -- name: GetDeviceBySerialAndPlatform :one SELECT * from devices WHERE serial = @serial AND platform = @platform; --- name: UpdateDevice :exec +-- name: UpdateDevice :one UPDATE devices -SET healthy = @healthy, last_updated = @last_updated -WHERE serial = @serial AND platform = @platform; +SET external_id = @external_id, healthy = @healthy, last_updated = @last_updated, last_seen = @last_seen, issues = @issues +WHERE serial = @serial AND platform = @platform +RETURNING id; +; -- name: AddDevice :exec INSERT INTO devices (serial, username, public_key, ipv4, ipv6, healthy, platform) VALUES (@serial, @username, @public_key, @ipv4, @ipv6, @healthy, @platform) ON CONFLICT(serial, platform) DO UPDATE SET username = excluded.username, public_key = excluded.public_key, ipv6 = excluded.ipv6; + +-- name: ClearDeviceIssuesExceptFor :exec +UPDATE devices +SET issues = NULL +WHERE id NOT IN (CAST(@unhealthy_device_ids AS INTEGER[])); diff --git a/internal/apiserver/database/schema/0004_device_issues.down.sql b/internal/apiserver/database/schema/0004_device_issues.down.sql new file mode 100644 index 000000000..091de0525 --- /dev/null +++ b/internal/apiserver/database/schema/0004_device_issues.down.sql @@ -0,0 +1,6 @@ +ALTER TABLE devices + DROP COLUMN last_seen; +ALTER TABLE devices + DROP COLUMN issues; +ALTER TABLE devices + DROP COLUMN external_id; diff --git a/internal/apiserver/database/schema/0004_device_issues.up.sql b/internal/apiserver/database/schema/0004_device_issues.up.sql new file mode 100644 index 000000000..6e73a785c --- /dev/null +++ b/internal/apiserver/database/schema/0004_device_issues.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE devices + ADD COLUMN last_seen TEXT; +ALTER TABLE devices + ADD COLUMN issues TEXT; +ALTER TABLE devices + ADD COLUMN external_id TEXT; diff --git a/internal/apiserver/kolide/cache.go b/internal/apiserver/kolide/cache.go index e20f0e880..fd075b692 100644 --- a/internal/apiserver/kolide/cache.go +++ b/internal/apiserver/kolide/cache.go @@ -37,3 +37,9 @@ func (c *Cache[K, V]) MarshalJSON() ([]byte, error) { defer c.mutex.RUnlock() return json.Marshal(c.cache) } + +func (c *Cache[K, V]) Len() int { + c.mutex.RLock() + defer c.mutex.RUnlock() + return len(c.cache) +} diff --git a/internal/apiserver/kolide/check.go b/internal/apiserver/kolide/check.go index 7ee2daedc..bc19d413d 100644 --- a/internal/apiserver/kolide/check.go +++ b/internal/apiserver/kolide/check.go @@ -103,9 +103,13 @@ func (device *Device) Issues() []*pb.DeviceIssue { } } + return convertFailuresToOpenDeviceIssues(device.Failures) +} + +func convertFailuresToOpenDeviceIssues(failures []DeviceFailure) []*pb.DeviceIssue { // Any failure means device failure openIssues := []*pb.DeviceIssue{} - for _, failure := range device.Failures { + for _, failure := range failures { if !failure.Relevant() { continue } @@ -133,6 +137,5 @@ func (device *Device) Issues() []*pb.DeviceIssue { LastUpdated: timestamppb.New(failure.LastUpdated), }) } - return openIssues } diff --git a/internal/apiserver/kolide/client.go b/internal/apiserver/kolide/client.go index 5e13c72e1..71073e84e 100644 --- a/internal/apiserver/kolide/client.go +++ b/internal/apiserver/kolide/client.go @@ -7,25 +7,27 @@ import ( "net/http" "net/url" "strings" + "time" + "github.com/nais/device/internal/apiserver/database" + "github.com/nais/device/internal/pb" "github.com/sirupsen/logrus" ) type Client interface { RefreshCache(ctx context.Context) error - GetDevice(ctx context.Context, email, platform, serial string) (Device, error) - DumpDevices() ([]byte, error) DumpChecks() ([]byte, error) + GetDeviceFailures(ctx context.Context, deviceID string) ([]*pb.DeviceIssue, error) } type client struct { baseUrl string client *http.Client - checks *Cache[uint64, Check] - devices *Cache[string, Device] + checks *Cache[uint64, Check] log logrus.FieldLogger + db database.APIServer } type ClientOption func(*client) @@ -42,9 +44,8 @@ func New(token string, log logrus.FieldLogger, opts ...ClientOption) Client { client: &http.Client{ Transport: NewTransport(token), }, - checks: &Cache[uint64, Check]{}, - devices: &Cache[string, Device]{}, - log: log, + checks: &Cache[uint64, Check]{}, + log: log, } for _, opt := range opts { opt(c) @@ -52,25 +53,25 @@ func New(token string, log logrus.FieldLogger, opts ...ClientOption) Client { return c } -func deviceKey(email, platform, serial string) string { - convertPlatform := func(platform string) string { - switch strings.ToLower(platform) { - case "darwin": - return "darwin" - case "windows": - return "windows" - default: - return "linux" - } +func convertPlatform(platform string) string { + switch strings.ToLower(platform) { + case "darwin": + return "darwin" + case "windows": + return "windows" + default: + return "linux" } - - return strings.ToLower(fmt.Sprintf("%v-%v-%v", email, convertPlatform(platform), serial)) } func (kc *client) RefreshCache(ctx context.Context) error { checks, err := kc.getChecks(ctx) if err != nil { - return fmt.Errorf("getting checks: %w", err) + if kc.checks.Len() == 0 { + return fmt.Errorf("getting checks: %w", err) + } else { + kc.log.Errorf("getting checks: %v", err) + } } checksCache := make(map[uint64]Check, len(checks)) @@ -82,33 +83,77 @@ func (kc *client) RefreshCache(ctx context.Context) error { devices, err := kc.getDevices(ctx) if err != nil { - return fmt.Errorf("getting devices: %w", err) + kc.log.Errorf("getting devices: %v", err) + } else { + for _, device := range devices { + _, err := kc.db.UpdateSingleDevice(ctx, fmt.Sprint(device.ID), device.Serial, device.Platform, device.LastSeenAt, nil) + if err != nil { + kc.log.Errorf("storing device: %v", err) + } + } } - devicesCache := make(map[string]Device, len(devices)) - for _, device := range devices { - devicesCache[deviceKey(device.AssignedOwner.Email, device.Platform, device.Serial)] = device + if err := kc.updateDeviceFailures(ctx); err != nil { + kc.log.Errorf("updating device failures: %v", err) } - kc.devices.Replace(devicesCache) - return nil } -func (kc *client) GetDevice(ctx context.Context, email, platform, serial string) (Device, error) { - key := deviceKey(email, platform, serial) - device, ok := kc.devices.Get(key) - if !ok { - return Device{}, fmt.Errorf("device with key %v not found in cache", key) +func (kc *client) updateDeviceFailures(ctx context.Context) error { + resp, err := kc.getPaginated(ctx, kc.baseUrl+"/failures/open") + if err != nil { + return fmt.Errorf("getting open failures: %w", err) + } + + type deviceKey struct { + deviceID string + platform string + serial string + lastSeenAt *time.Time } - failures, err := kc.getDeviceFailures(ctx, device.ID) - if err != nil { - return Device{}, fmt.Errorf("getting device failures: %w", err) + devices := make(map[deviceKey][]DeviceFailure) + for _, rawFailure := range resp { + failure := DeviceFailureWithDevice{} + err := json.Unmarshal(rawFailure, &failure) + if err != nil { + return fmt.Errorf("unmarshal failure: %w", err) + } + + failure.Check, err = kc.getCheck(ctx, failure.CheckID) + if err != nil { + return fmt.Errorf("getting check: %w", err) + } + + key := deviceKey{ + deviceID: fmt.Sprint(failure.Device.ID), + platform: convertPlatform(failure.Device.Platform), + serial: failure.Device.Serial, + lastSeenAt: failure.Device.LastSeenAt, + } + devices[key] = append(devices[key], failure.DeviceFailure) + } + + checkedDevices := []int64{} + for device, failures := range devices { + issues := convertFailuresToOpenDeviceIssues(failures) + id, err := kc.db.UpdateSingleDevice(ctx, device.deviceID, device.serial, device.platform, device.lastSeenAt, issues) + if err != nil { + kc.log.Errorf("storing device issues: %v", err) + continue + } + checkedDevices = append(checkedDevices, id) } - device.Failures = failures - return device, nil + if len(checkedDevices) > 0 { + err := kc.db.ClearDeviceIssuesExceptFor(ctx, checkedDevices) + if err != nil { + return fmt.Errorf("clearing device issues: %w", err) + } + } + + return nil } func (kc *client) getChecks(ctx context.Context) ([]Check, error) { @@ -210,13 +255,14 @@ func (kc *client) getDevices(ctx context.Context) ([]Device, error) { return nil, fmt.Errorf("unmarshal device: %w", err) } + device.Platform = convertPlatform(device.Platform) devices = append(devices, device) } return devices, nil } -func (kc *client) getDeviceFailures(ctx context.Context, deviceID uint64) ([]DeviceFailure, error) { +func (kc *client) GetDeviceFailures(ctx context.Context, deviceID string) ([]*pb.DeviceIssue, error) { url := fmt.Sprintf(kc.baseUrl+"/devices/%v/failures", deviceID) rawFailures, err := kc.getPaginated(ctx, url) if err != nil { @@ -235,10 +281,11 @@ func (kc *client) getDeviceFailures(ctx context.Context, deviceID uint64) ([]Dev if err != nil { return nil, fmt.Errorf("getting check: %w", err) } + failures = append(failures, failure) } - return failures, nil + return convertFailuresToOpenDeviceIssues(failures), nil } func (kc *client) get(ctx context.Context, url string) (*http.Response, error) { @@ -250,10 +297,6 @@ func (kc *client) get(ctx context.Context, url string) (*http.Response, error) { return kc.client.Do(req) } -func (kc client) DumpDevices() ([]byte, error) { - return json.Marshal(kc.devices) -} - func (kc client) DumpChecks() ([]byte, error) { return json.Marshal(kc.checks) } diff --git a/internal/apiserver/kolide/client_test.go b/internal/apiserver/kolide/client_test.go index 809d78d32..aada627c2 100644 --- a/internal/apiserver/kolide/client_test.go +++ b/internal/apiserver/kolide/client_test.go @@ -24,6 +24,8 @@ func TestClient(t *testing.T) { fmt.Fprintf(w, `{}`) case "/checks": fmt.Fprintf(w, `{}`) + case "/failures/open": + fmt.Fprintf(w, `{}`) default: t.Errorf("unexpected request to %v", r.URL.Path) } diff --git a/internal/apiserver/kolide/event_handler.go b/internal/apiserver/kolide/event_handler.go index c3cf68100..59abe2e12 100644 --- a/internal/apiserver/kolide/event_handler.go +++ b/internal/apiserver/kolide/event_handler.go @@ -52,8 +52,7 @@ func DeviceEventStreamer(ctx context.Context, log *logrus.Entry, grpcAddress, gr dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) } - conn, err := grpc.DialContext( - ctx, + conn, err := grpc.NewClient( grpcAddress, dialOpts..., ) @@ -111,5 +110,9 @@ func LookupDevice(ctx context.Context, db database.APIServer, event *kolidepb.De return nil, fmt.Errorf("read device with serial=%s platform=%s: %w", event.GetSerial(), p, err) } + if device.ExternalID == "" { + device.ExternalID = event.GetExternalID() + } + return device, nil } diff --git a/internal/apiserver/kolide/fakeclient.go b/internal/apiserver/kolide/fakeclient.go index 25b25cd2b..4a500cee2 100644 --- a/internal/apiserver/kolide/fakeclient.go +++ b/internal/apiserver/kolide/fakeclient.go @@ -2,13 +2,11 @@ package kolide import ( "context" - "fmt" - "strings" + + "github.com/nais/device/internal/pb" ) -type FakeClient struct { - devices []Device -} +type FakeClient struct{} var _ Client = &FakeClient{} @@ -17,41 +15,27 @@ func NewFakeClient() *FakeClient { return &FakeClient{} } -func (f *FakeClient) WithDevice(device Device) *FakeClient { - return &FakeClient{ - devices: append(f.devices, device), - } -} - func (f *FakeClient) Build() Client { return f } -// GetDevice implements Client. -func (f *FakeClient) GetDevice(ctx context.Context, email, platform, serial string) (Device, error) { - for _, d := range f.devices { - if strings.EqualFold(d.AssignedOwner.Email, email) && - strings.EqualFold(d.Platform, platform) && - strings.EqualFold(d.Serial, serial) { - return d, nil - } - } - - return Device{}, fmt.Errorf("device (%v, %v, %v) not found in fake client. use WithDevice() before Build() to add it. we currently have: %+v", email, platform, serial, f.devices) -} - // RefreshCache implements Client. func (f *FakeClient) RefreshCache(ctx context.Context) error { // no-op return nil } +// GetDeviceFailures implements Client. +func (f *FakeClient) GetDeviceFailures(ctx context.Context, deviceID string) ([]*pb.DeviceIssue, error) { + panic("unimplemented") +} + // DumpChecks implements Client. func (f *FakeClient) DumpChecks() ([]byte, error) { panic("unimplemented") } -// DumpDevices implements Client. -func (f *FakeClient) DumpDevices() ([]byte, error) { +// UpdateDeviceFailures implements Client. +func (f *FakeClient) UpdateDeviceFailures(ctx context.Context) error { panic("unimplemented") } diff --git a/internal/apiserver/kolide/transport.go b/internal/apiserver/kolide/transport.go index 93063c592..2cf2c3346 100644 --- a/internal/apiserver/kolide/transport.go +++ b/internal/apiserver/kolide/transport.go @@ -6,6 +6,7 @@ import ( "strconv" "time" + "github.com/nais/device/internal/apiserver/metrics" log "github.com/sirupsen/logrus" ) @@ -39,6 +40,8 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { return nil, err } + metrics.IncKolideStatusCode(resp.StatusCode) + if resp.StatusCode != http.StatusTooManyRequests && resp.StatusCode < 500 { return resp, nil } diff --git a/internal/apiserver/kolide/types.go b/internal/apiserver/kolide/types.go index 43a54effa..72dac1abc 100644 --- a/internal/apiserver/kolide/types.go +++ b/internal/apiserver/kolide/types.go @@ -17,6 +17,11 @@ type DeviceFailure struct { Check Check `json:"check"` } +type DeviceFailureWithDevice struct { + DeviceFailure + Device Device `json:"device"` +} + type DeviceOwner struct { Email string `json:"email"` } diff --git a/internal/apiserver/metrics/metrics.go b/internal/apiserver/metrics/metrics.go index 7a6a031f4..2c93df568 100644 --- a/internal/apiserver/metrics/metrics.go +++ b/internal/apiserver/metrics/metrics.go @@ -1,7 +1,8 @@ -package apiserver_metrics +package metrics import ( "net/http" + "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -14,7 +15,8 @@ var ( PrivilegedUsersPerGateway *prometheus.GaugeVec LoginRequests *prometheus.CounterVec - gatewayStatus *prometheus.GaugeVec + gatewayStatus *prometheus.GaugeVec + kolideStatusCodes *prometheus.CounterVec ) func Serve(address string) error { @@ -36,6 +38,10 @@ func SetConnectedGateways(allGateways, connectedGateways []string) { } } +func IncKolideStatusCode(code int) { + kolideStatusCodes.WithLabelValues(strconv.Itoa(code)).Inc() +} + func init() { DevicesConnected = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "naisdevice", @@ -79,6 +85,13 @@ func init() { Help: "Device logins with agent version.", }, []string{"version"}) + kolideStatusCodes = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "naisdevice", + Subsystem: "apiserver", + Name: "kolide_status_codes", + Help: "Kolide status codes from API.", + }, []string{"code"}) + prometheus.MustRegister( DevicesConnected, gatewayStatus, @@ -86,5 +99,6 @@ func init() { DeviceConfigsReturned, GatewayConfigsReturned, LoginRequests, + kolideStatusCodes, ) } diff --git a/internal/apiserver/sqlc/db.go b/internal/apiserver/sqlc/db.go index fe4b91eb7..5b0001889 100644 --- a/internal/apiserver/sqlc/db.go +++ b/internal/apiserver/sqlc/db.go @@ -42,6 +42,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) { if q.addSessionAccessGroupIDStmt, err = db.PrepareContext(ctx, addSessionAccessGroupID); err != nil { return nil, fmt.Errorf("error preparing query AddSessionAccessGroupID: %w", err) } + if q.clearDeviceIssuesExceptForStmt, err = db.PrepareContext(ctx, clearDeviceIssuesExceptFor); err != nil { + return nil, fmt.Errorf("error preparing query ClearDeviceIssuesExceptFor: %w", err) + } if q.deleteGatewayAccessGroupIDsStmt, err = db.PrepareContext(ctx, deleteGatewayAccessGroupIDs); err != nil { return nil, fmt.Errorf("error preparing query DeleteGatewayAccessGroupIDs: %w", err) } @@ -134,6 +137,11 @@ func (q *Queries) Close() error { err = fmt.Errorf("error closing addSessionAccessGroupIDStmt: %w", cerr) } } + if q.clearDeviceIssuesExceptForStmt != nil { + if cerr := q.clearDeviceIssuesExceptForStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing clearDeviceIssuesExceptForStmt: %w", cerr) + } + } if q.deleteGatewayAccessGroupIDsStmt != nil { if cerr := q.deleteGatewayAccessGroupIDsStmt.Close(); cerr != nil { err = fmt.Errorf("error closing deleteGatewayAccessGroupIDsStmt: %w", cerr) @@ -274,6 +282,7 @@ type Queries struct { addGatewayRouteStmt *sql.Stmt addSessionStmt *sql.Stmt addSessionAccessGroupIDStmt *sql.Stmt + clearDeviceIssuesExceptForStmt *sql.Stmt deleteGatewayAccessGroupIDsStmt *sql.Stmt deleteGatewayRoutesStmt *sql.Stmt getDeviceByIDStmt *sql.Stmt @@ -305,6 +314,7 @@ func (q *Queries) WithTx(tx *sql.Tx) *Queries { addGatewayRouteStmt: q.addGatewayRouteStmt, addSessionStmt: q.addSessionStmt, addSessionAccessGroupIDStmt: q.addSessionAccessGroupIDStmt, + clearDeviceIssuesExceptForStmt: q.clearDeviceIssuesExceptForStmt, deleteGatewayAccessGroupIDsStmt: q.deleteGatewayAccessGroupIDsStmt, deleteGatewayRoutesStmt: q.deleteGatewayRoutesStmt, getDeviceByIDStmt: q.getDeviceByIDStmt, diff --git a/internal/apiserver/sqlc/devices.sql.go b/internal/apiserver/sqlc/devices.sql.go index 13da4516c..e5007066d 100644 --- a/internal/apiserver/sqlc/devices.sql.go +++ b/internal/apiserver/sqlc/devices.sql.go @@ -11,6 +11,8 @@ import ( ) const addDevice = `-- name: AddDevice :exec +; + INSERT INTO devices (serial, username, public_key, ipv4, ipv6, healthy, platform) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) ON CONFLICT(serial, platform) DO @@ -40,8 +42,19 @@ func (q *Queries) AddDevice(ctx context.Context, arg AddDeviceParams) error { return err } +const clearDeviceIssuesExceptFor = `-- name: ClearDeviceIssuesExceptFor :exec +UPDATE devices +SET issues = NULL +WHERE id NOT IN (CAST(?1 AS INTEGER[])) +` + +func (q *Queries) ClearDeviceIssuesExceptFor(ctx context.Context, unhealthyDeviceIds interface{}) error { + _, err := q.exec(ctx, q.clearDeviceIssuesExceptForStmt, clearDeviceIssuesExceptFor, unhealthyDeviceIds) + return err +} + const getDeviceByID = `-- name: GetDeviceByID :one -SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6 FROM devices WHERE id = ?1 +SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6, last_seen, issues, external_id FROM devices WHERE id = ?1 ` func (q *Queries) GetDeviceByID(ctx context.Context, id int64) (*Device, error) { @@ -57,12 +70,15 @@ func (q *Queries) GetDeviceByID(ctx context.Context, id int64) (*Device, error) &i.PublicKey, &i.Ipv4, &i.Ipv6, + &i.LastSeen, + &i.Issues, + &i.ExternalID, ) return &i, err } const getDeviceByPublicKey = `-- name: GetDeviceByPublicKey :one -SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6 FROM devices WHERE public_key = ?1 +SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6, last_seen, issues, external_id FROM devices WHERE public_key = ?1 ` func (q *Queries) GetDeviceByPublicKey(ctx context.Context, publicKey string) (*Device, error) { @@ -78,12 +94,15 @@ func (q *Queries) GetDeviceByPublicKey(ctx context.Context, publicKey string) (* &i.PublicKey, &i.Ipv4, &i.Ipv6, + &i.LastSeen, + &i.Issues, + &i.ExternalID, ) return &i, err } const getDeviceBySerialAndPlatform = `-- name: GetDeviceBySerialAndPlatform :one -SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6 from devices WHERE serial = ?1 AND platform = ?2 +SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6, last_seen, issues, external_id from devices WHERE serial = ?1 AND platform = ?2 ` type GetDeviceBySerialAndPlatformParams struct { @@ -104,12 +123,15 @@ func (q *Queries) GetDeviceBySerialAndPlatform(ctx context.Context, arg GetDevic &i.PublicKey, &i.Ipv4, &i.Ipv6, + &i.LastSeen, + &i.Issues, + &i.ExternalID, ) return &i, err } const getDevices = `-- name: GetDevices :many -SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6 FROM devices ORDER BY id +SELECT id, username, serial, platform, healthy, last_updated, public_key, ipv4, ipv6, last_seen, issues, external_id FROM devices ORDER BY id ` func (q *Queries) GetDevices(ctx context.Context) ([]*Device, error) { @@ -131,6 +153,9 @@ func (q *Queries) GetDevices(ctx context.Context) ([]*Device, error) { &i.PublicKey, &i.Ipv4, &i.Ipv6, + &i.LastSeen, + &i.Issues, + &i.ExternalID, ); err != nil { return nil, err } @@ -145,25 +170,34 @@ func (q *Queries) GetDevices(ctx context.Context) ([]*Device, error) { return items, nil } -const updateDevice = `-- name: UpdateDevice :exec +const updateDevice = `-- name: UpdateDevice :one UPDATE devices -SET healthy = ?1, last_updated = ?2 -WHERE serial = ?3 AND platform = ?4 +SET external_id = ?1, healthy = ?2, last_updated = ?3, last_seen = ?4, issues = ?5 +WHERE serial = ?6 AND platform = ?7 +RETURNING id ` type UpdateDeviceParams struct { + ExternalID sql.NullString Healthy bool LastUpdated sql.NullString + LastSeen sql.NullString + Issues sql.NullString Serial string Platform string } -func (q *Queries) UpdateDevice(ctx context.Context, arg UpdateDeviceParams) error { - _, err := q.exec(ctx, q.updateDeviceStmt, updateDevice, +func (q *Queries) UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (int64, error) { + row := q.queryRow(ctx, q.updateDeviceStmt, updateDevice, + arg.ExternalID, arg.Healthy, arg.LastUpdated, + arg.LastSeen, + arg.Issues, arg.Serial, arg.Platform, ) - return err + var id int64 + err := row.Scan(&id) + return id, err } diff --git a/internal/apiserver/sqlc/models.go b/internal/apiserver/sqlc/models.go index f98382716..74e987e66 100644 --- a/internal/apiserver/sqlc/models.go +++ b/internal/apiserver/sqlc/models.go @@ -18,6 +18,9 @@ type Device struct { PublicKey string Ipv4 string Ipv6 string + LastSeen sql.NullString + Issues sql.NullString + ExternalID sql.NullString } type Gateway struct { diff --git a/internal/apiserver/sqlc/querier.go b/internal/apiserver/sqlc/querier.go index 2a7e9d80d..e7d79fbf6 100644 --- a/internal/apiserver/sqlc/querier.go +++ b/internal/apiserver/sqlc/querier.go @@ -15,6 +15,7 @@ type Querier interface { AddGatewayRoute(ctx context.Context, arg AddGatewayRouteParams) error AddSession(ctx context.Context, arg AddSessionParams) error AddSessionAccessGroupID(ctx context.Context, arg AddSessionAccessGroupIDParams) error + ClearDeviceIssuesExceptFor(ctx context.Context, unhealthyDeviceIds interface{}) error DeleteGatewayAccessGroupIDs(ctx context.Context, gatewayName string) error DeleteGatewayRoutes(ctx context.Context, gatewayName string) error GetDeviceByID(ctx context.Context, id int64) (*Device, error) @@ -31,7 +32,7 @@ type Querier interface { GetSessionGroupIDs(ctx context.Context, sessionKey string) ([]string, error) GetSessions(ctx context.Context) ([]*GetSessionsRow, error) RemoveExpiredSessions(ctx context.Context) error - UpdateDevice(ctx context.Context, arg UpdateDeviceParams) error + UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (int64, error) UpdateGateway(ctx context.Context, arg UpdateGatewayParams) error UpdateGatewayDynamicFields(ctx context.Context, arg UpdateGatewayDynamicFieldsParams) error } diff --git a/internal/apiserver/sqlc/sessions.sql.go b/internal/apiserver/sqlc/sessions.sql.go index 438346342..50fbaf726 100644 --- a/internal/apiserver/sqlc/sessions.sql.go +++ b/internal/apiserver/sqlc/sessions.sql.go @@ -47,7 +47,7 @@ func (q *Queries) AddSessionAccessGroupID(ctx context.Context, arg AddSessionAcc } const getMostRecentDeviceSession = `-- name: GetMostRecentDeviceSession :one -SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6 FROM sessions s +SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6, d.last_seen, d.issues, d.external_id FROM sessions s JOIN devices d ON d.id = s.device_id WHERE s.device_id = ?1 ORDER BY s.expiry DESC @@ -76,12 +76,15 @@ func (q *Queries) GetMostRecentDeviceSession(ctx context.Context, sessionDeviceI &i.Device.PublicKey, &i.Device.Ipv4, &i.Device.Ipv6, + &i.Device.LastSeen, + &i.Device.Issues, + &i.Device.ExternalID, ) return &i, err } const getSessionByKey = `-- name: GetSessionByKey :one -SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6 FROM sessions s +SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6, d.last_seen, d.issues, d.external_id FROM sessions s JOIN devices d ON d.id = s.device_id WHERE s.key = ?1 ` @@ -107,6 +110,9 @@ func (q *Queries) GetSessionByKey(ctx context.Context, sessionKey string) (*GetS &i.Device.PublicKey, &i.Device.Ipv4, &i.Device.Ipv6, + &i.Device.LastSeen, + &i.Device.Issues, + &i.Device.ExternalID, ) return &i, err } @@ -139,7 +145,7 @@ func (q *Queries) GetSessionGroupIDs(ctx context.Context, sessionKey string) ([] } const getSessions = `-- name: GetSessions :many -SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6 FROM sessions s +SELECT s."key", s.expiry, s.device_id, s.object_id, d.id, d.username, d.serial, d.platform, d.healthy, d.last_updated, d.public_key, d.ipv4, d.ipv6, d.last_seen, d.issues, d.external_id FROM sessions s JOIN devices d ON d.id = s.device_id ORDER BY s.expiry ` @@ -172,6 +178,9 @@ func (q *Queries) GetSessions(ctx context.Context) ([]*GetSessionsRow, error) { &i.Device.PublicKey, &i.Device.Ipv4, &i.Device.Ipv6, + &i.Device.LastSeen, + &i.Device.Issues, + &i.Device.ExternalID, ); err != nil { return nil, err } diff --git a/internal/controlplane-cli/enrollgateway.go b/internal/controlplane-cli/enrollgateway.go index 1287f8c34..a77e5b1b9 100644 --- a/internal/controlplane-cli/enrollgateway.go +++ b/internal/controlplane-cli/enrollgateway.go @@ -28,8 +28,7 @@ const ( const AdminUsername = "admin" func ListGateways(c *cli.Context) error { - conn, err := grpc.DialContext( - c.Context, + conn, err := grpc.NewClient( c.String(FlagAPIServer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -76,8 +75,7 @@ func HashPassword(c *cli.Context) error { } func EditGateway(c *cli.Context) error { - conn, err := grpc.DialContext( - c.Context, + conn, err := grpc.NewClient( c.String(FlagAPIServer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -156,8 +154,7 @@ func EnrollGateway(c *cli.Context) error { fmt.Fprintf(os.Stderr, "passhash....: %s\n", req.Gateway.PasswordHash) fmt.Fprintf(os.Stderr, "\n") - conn, err := grpc.DialContext( - c.Context, + conn, err := grpc.NewClient( c.String(FlagAPIServer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) diff --git a/internal/controlplane-cli/kolide.go b/internal/controlplane-cli/kolide.go index f90a1470d..7158c9309 100644 --- a/internal/controlplane-cli/kolide.go +++ b/internal/controlplane-cli/kolide.go @@ -11,8 +11,7 @@ import ( ) func GetKolideCache(c *cli.Context) error { - conn, err := grpc.DialContext( - c.Context, + conn, err := grpc.NewClient( c.String(FlagAPIServer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -33,8 +32,7 @@ func GetKolideCache(c *cli.Context) error { Devices json.RawMessage Checks json.RawMessage }{ - Devices: resp.RawDevices, - Checks: resp.RawChecks, + Checks: resp.RawChecks, } return json.NewEncoder(os.Stdout).Encode(out) diff --git a/internal/controlplane-cli/sessions.go b/internal/controlplane-cli/sessions.go index b5e6a5e34..05a56ad02 100644 --- a/internal/controlplane-cli/sessions.go +++ b/internal/controlplane-cli/sessions.go @@ -10,8 +10,7 @@ import ( ) func ListSessions(c *cli.Context) error { - conn, err := grpc.DialContext( - c.Context, + conn, err := grpc.NewClient( c.String(FlagAPIServer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -29,13 +28,14 @@ func ListSessions(c *cli.Context) error { } for _, s := range resp.GetSessions() { - fmt.Printf("user: %s, healthy: %t, ipv4: %s, ipv6: %s, pubkey: %q, expired: %t\n", + fmt.Printf("user: %s, lastSeen: %v, ipv4: %s, ipv6: %s, pubkey: %q, expired: %t, issues: %v\n", s.Device.GetUsername(), - s.Device.GetHealthy(), + s.Device.GetLastSeen(), s.Device.GetIpv4(), s.Device.GetIpv6(), s.Device.GetPublicKey(), s.Expired(), + len(s.Device.GetIssues()), ) } diff --git a/internal/device-agent/runtimeconfig/runtimeconfig.go b/internal/device-agent/runtimeconfig/runtimeconfig.go index 16c90ea72..c43f1e897 100644 --- a/internal/device-agent/runtimeconfig/runtimeconfig.go +++ b/internal/device-agent/runtimeconfig/runtimeconfig.go @@ -75,17 +75,14 @@ type runtimeConfig struct { func (rc *runtimeConfig) DialAPIServer(ctx context.Context) (*grpc.ClientConn, error) { rc.log.Infof("Attempting gRPC connection to API server on %s...", rc.apiServerGRPCAddress()) - return grpc.DialContext( - ctx, + return grpc.NewClient( rc.apiServerGRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithBlock(), grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: time.Second * 10, Timeout: time.Second * 2, PermitWithoutStream: false, }), - grpc.WithReturnConnectionError(), grpc.WithStatsHandler(otel.NewGRPCClientHandler( pb.APIServer_GetDeviceConfiguration_FullMethodName, )), diff --git a/internal/integration_test/device_agent_test.go b/internal/integration_test/device_agent_test.go index 1d2de284d..3aef8752a 100644 --- a/internal/integration_test/device_agent_test.go +++ b/internal/integration_test/device_agent_test.go @@ -18,7 +18,7 @@ import ( ) func NewDeviceAgent(t *testing.T, wg *sync.WaitGroup, ctx context.Context, log *logrus.Entry, helperconn *bufconn.Listener, rc *runtimeconfig.MockRuntimeConfig) *grpc.Server { - helperDial, err := dial(ctx, helperconn) + helperDial, err := dial(helperconn) assert.NoError(t, err) helperClient := pb.NewDeviceHelperClient(helperDial) diff --git a/internal/integration_test/gateway_agent_test.go b/internal/integration_test/gateway_agent_test.go index 73d7a4da3..fa5997d3e 100644 --- a/internal/integration_test/gateway_agent_test.go +++ b/internal/integration_test/gateway_agent_test.go @@ -13,7 +13,7 @@ import ( ) func StartGatewayAgent(t *testing.T, ctx context.Context, log *logrus.Entry, name string, apiserverConn *bufconn.Listener, apiserverPeer *pb.Gateway, networkConfigurer wireguard.NetworkConfigurer) error { - apiserverDial, err := dial(ctx, apiserverConn) + apiserverDial, err := dial(apiserverConn) assert.NoError(t, err) apiserverClient := pb.NewAPIServerClient(apiserverDial) diff --git a/internal/integration_test/integration_test.go b/internal/integration_test/integration_test.go index b24188a1d..97b1eb3f7 100644 --- a/internal/integration_test/integration_test.go +++ b/internal/integration_test/integration_test.go @@ -29,51 +29,36 @@ func TestIntegration(t *testing.T) { now := time.Now() type testCase struct { - name string - device *pb.Device - endState pb.AgentState - expectedGateways map[string]*pb.Gateway - expectedKolideFailures []kolide.DeviceFailure - expectedIssues []*pb.DeviceIssue + name string + device *pb.Device + endState pb.AgentState + expectedGateways map[string]*pb.Gateway } tests := []testCase{ { name: "test happy unhealthy path", device: &pb.Device{ Serial: "test-serial", - Healthy: false, PublicKey: "publicKey", Username: "tester", Platform: "linux", - }, - endState: pb.AgentState_Unhealthy, - expectedGateways: nil, - expectedKolideFailures: []kolide.DeviceFailure{ - { - Title: "Issue used in integration test", - Check: kolide.Check{ - Tags: []string{pb.Severity_Critical.String()}, - Description: "This is just a fake issue used in integration test", + Issues: []*pb.DeviceIssue{ + { + Title: "Issue used in integration test", + Message: "This is just a fake issue used in integration test", + Severity: pb.Severity_Critical, + DetectedAt: timestamppb.New(now.Add(-(2 * time.Hour))), + LastUpdated: timestamppb.New(now), }, - Timestamp: ptrTo(now.Add(-(2 * time.Hour))), - LastUpdated: now, - }, - }, - expectedIssues: []*pb.DeviceIssue{ - { - Title: "Issue used in integration test", - Message: "This is just a fake issue used in integration test", - Severity: pb.Severity_Critical, - DetectedAt: timestamppb.New(now.Add(-(2 * time.Hour))), - LastUpdated: timestamppb.New(now), }, }, + endState: pb.AgentState_Unhealthy, + expectedGateways: nil, }, { name: "test happy healthy path", device: &pb.Device{ Serial: "test-serial", - Healthy: true, PublicKey: "publicKey", Username: "tester", Platform: "linux", @@ -102,31 +87,21 @@ func TestIntegration(t *testing.T) { }, } log := logger.WithField("component", "test") - tableTest(t, log, now, test.device, test.endState, test.expectedGateways, test.expectedIssues, test.expectedKolideFailures) + tableTest(t, log, test.device, test.endState, test.expectedGateways) } } t.Run(test.name, wrap(test)) } } -func tableTest(t *testing.T, log *logrus.Entry, now time.Time, testDevice *pb.Device, endState pb.AgentState, expectedGateways map[string]*pb.Gateway, expectedIssues []*pb.DeviceIssue, expectedKolideFailures []kolide.DeviceFailure) { +func tableTest(t *testing.T, log *logrus.Entry, testDevice *pb.Device, endState pb.AgentState, expectedGateways map[string]*pb.Gateway) { wg := &sync.WaitGroup{} t.Helper() ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() devicePrivateKey := "devicePrivateKey" - kolideDevice := kolide.Device{ - LastSeenAt: &now, - Serial: testDevice.Serial, - Platform: testDevice.Platform, - AssignedOwner: kolide.DeviceOwner{ - Email: testDevice.Username, - }, - Failures: expectedKolideFailures, - FailureCount: len(expectedKolideFailures), - } - kolideClient := kolide.NewFakeClient().WithDevice(kolideDevice).Build() + kolideClient := kolide.NewFakeClient() db := NewDB(t) assert.NoError(t, db.AddDevice(ctx, testDevice)) @@ -226,7 +201,7 @@ func tableTest(t *testing.T, log *logrus.Entry, now time.Time, testDevice *pb.De helperListener, stopHelper := serve(t, NewHelper(t, log.WithField("component", "helper"), osConfigurator), wg) - apiDial, err := dial(ctx, apiserverListener) + apiDial, err := dial(apiserverListener) assert.NoError(t, err) cleanup := func() { apiDial.Close() @@ -304,7 +279,7 @@ func tableTest(t *testing.T, log *logrus.Entry, now time.Time, testDevice *pb.De deviceAgentListener, stopDeviceAgent := serve(t, NewDeviceAgent(t, wg, ctx, log.WithField("component", "device-agent"), helperListener, rc), wg) - deviceAgentConnection, err := dial(ctx, deviceAgentListener) + deviceAgentConnection, err := dial(deviceAgentListener) assert.NoError(t, err) deviceAgentClient := pb.NewDeviceAgentClient(deviceAgentConnection) @@ -366,7 +341,7 @@ func tableTest(t *testing.T, log *logrus.Entry, now time.Time, testDevice *pb.De lastKnownGateways = status.Gateways if status.ConnectionState == endState && matchExactGateways(expectedGateways)(append(status.Gateways, apiserverPeer)) && - len(expectedIssues) == len(status.Issues) { + len(testDevice.Issues) == len(status.Issues) { t.Logf("agent reached expected end state: %v", endState) // Verify all gateways have the exact expected parameters @@ -380,7 +355,7 @@ func tableTest(t *testing.T, log *logrus.Entry, now time.Time, testDevice *pb.De assertEqualGateway(t, expectedGateway, gateway) } - assertEqualIssueLists(t, expectedIssues, status.Issues) + assertEqualIssueLists(t, testDevice.Issues, status.Issues) // test done stopStuff() @@ -534,10 +509,9 @@ func serve(t *testing.T, server *grpc.Server, wg *sync.WaitGroup) (*bufconn.List return lis, server.Stop } -func dial(ctx context.Context, lis *bufconn.Listener) (*grpc.ClientConn, error) { - return grpc.DialContext( - ctx, - "bufnet", +func dial(lis *bufconn.Listener) (*grpc.ClientConn, error) { + return grpc.NewClient( + "passthrough:///bufnet", grpc.WithContextDialer(bufconnDialer(lis)), grpc.WithTransportCredentials(insecure.NewCredentials()), ) @@ -551,7 +525,3 @@ func (t *testLogWriter) Write(p []byte) (n int, err error) { t.t.Logf("%s", p) return len(p), nil } - -func ptrTo[A any](v A) *A { - return &v -} diff --git a/internal/pb/protobuf-api.pb.go b/internal/pb/protobuf-api.pb.go index ef73988ee..869d63a8a 100644 --- a/internal/pb/protobuf-api.pb.go +++ b/internal/pb/protobuf-api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 -// protoc v4.24.4 +// protoc-gen-go v1.31.0 +// protoc v4.23.4 // source: internal/pb/protobuf-api.proto package pb @@ -2124,12 +2124,14 @@ type Device struct { Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Serial string `protobuf:"bytes,2,opt,name=serial,proto3" json:"serial,omitempty"` LastUpdated *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=lastUpdated,proto3" json:"lastUpdated,omitempty"` - Healthy bool `protobuf:"varint,6,opt,name=healthy,proto3" json:"healthy,omitempty"` PublicKey string `protobuf:"bytes,7,opt,name=publicKey,proto3" json:"publicKey,omitempty"` Ipv4 string `protobuf:"bytes,8,opt,name=ipv4,proto3" json:"ipv4,omitempty"` Username string `protobuf:"bytes,9,opt,name=username,proto3" json:"username,omitempty"` Platform string `protobuf:"bytes,10,opt,name=platform,proto3" json:"platform,omitempty"` Ipv6 string `protobuf:"bytes,11,opt,name=ipv6,proto3" json:"ipv6,omitempty"` + Issues []*DeviceIssue `protobuf:"bytes,12,rep,name=issues,proto3" json:"issues,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=lastSeen,proto3" json:"lastSeen,omitempty"` + ExternalID string `protobuf:"bytes,14,opt,name=externalID,proto3" json:"externalID,omitempty"` } func (x *Device) Reset() { @@ -2185,13 +2187,6 @@ func (x *Device) GetLastUpdated() *timestamppb.Timestamp { return nil } -func (x *Device) GetHealthy() bool { - if x != nil { - return x.Healthy - } - return false -} - func (x *Device) GetPublicKey() string { if x != nil { return x.PublicKey @@ -2227,6 +2222,27 @@ func (x *Device) GetIpv6() string { return "" } +func (x *Device) GetIssues() []*DeviceIssue { + if x != nil { + return x.Issues + } + return nil +} + +func (x *Device) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *Device) GetExternalID() string { + if x != nil { + return x.ExternalID + } + return "" +} + type Session struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2544,8 +2560,7 @@ type GetKolideCacheResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RawDevices []byte `protobuf:"bytes,98,opt,name=rawDevices,proto3" json:"rawDevices,omitempty"` - RawChecks []byte `protobuf:"bytes,99,opt,name=rawChecks,proto3" json:"rawChecks,omitempty"` + RawChecks []byte `protobuf:"bytes,99,opt,name=rawChecks,proto3" json:"rawChecks,omitempty"` } func (x *GetKolideCacheResponse) Reset() { @@ -2580,13 +2595,6 @@ func (*GetKolideCacheResponse) Descriptor() ([]byte, []int) { return file_internal_pb_protobuf_api_proto_rawDescGZIP(), []int{43} } -func (x *GetKolideCacheResponse) GetRawDevices() []byte { - if x != nil { - return x.RawDevices - } - return nil -} - func (x *GetKolideCacheResponse) GetRawChecks() []byte { if x != nil { return x.RawChecks @@ -2804,202 +2812,208 @@ var file_internal_pb_protobuf_api_proto_rawDesc = []byte{ 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0xa7, 0x02, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0xa5, 0x03, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 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, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x1a, 0x0a, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, - 0x08, 0x05, 0x10, 0x06, 0x52, 0x03, 0x70, 0x73, 0x6b, 0x52, 0x0e, 0x6b, 0x6f, 0x6c, 0x69, 0x64, - 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x22, 0xaf, 0x01, 0x0a, 0x07, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x18, 0x02, 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, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x06, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x61, - 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, - 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x22, 0x4c, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x46, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2f, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, - 0x68, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x56, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, - 0x63, 0x68, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, - 0x61, 0x77, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x62, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x72, 0x61, 0x77, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, - 0x61, 0x77, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x72, 0x61, 0x77, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x2a, 0xf2, 0x01, 0x0a, 0x0a, 0x41, 0x67, - 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x6f, - 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x0d, 0x0a, - 0x09, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, - 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x12, - 0x0d, 0x0a, 0x09, 0x55, 0x6e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x10, 0x04, 0x12, 0x0c, - 0x0a, 0x08, 0x51, 0x75, 0x69, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, - 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x06, - 0x12, 0x17, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x10, 0x09, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x10, 0x0a, 0x22, 0x04, 0x08, 0x07, 0x10, 0x07, 0x22, 0x04, - 0x08, 0x08, 0x10, 0x08, 0x22, 0x04, 0x08, 0x0b, 0x10, 0x0b, 0x2a, 0x0a, 0x53, 0x79, 0x6e, 0x63, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x2a, 0x09, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x43, 0x65, 0x72, 0x74, 0x2a, 0x57, - 0x0a, 0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x0a, 0x0d, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x79, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0x02, 0x2a, 0x25, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x10, 0x01, 0x2a, 0x47, - 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x6e, - 0x66, 0x6f, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x10, 0x01, - 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x0a, 0x0a, - 0x06, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x72, 0x69, - 0x74, 0x69, 0x63, 0x61, 0x6c, 0x10, 0x04, 0x32, 0xef, 0x02, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x19, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x1a, 0x1d, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x47, 0x0a, 0x08, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1b, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, - 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6e, 0x61, 0x69, - 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x55, 0x70, - 0x67, 0x72, 0x61, 0x64, 0x65, 0x12, 0x1a, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x55, - 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x1c, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x61, - 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x04, - 0x50, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xed, 0x04, 0x0a, 0x0b, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x56, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, - 0x41, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, 0x41, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, 0x41, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x12, 0x18, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6e, 0x61, - 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, - 0x75, 0x74, 0x12, 0x19, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0f, 0x53, - 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x22, - 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x53, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x15, 0x53, 0x65, 0x74, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x53, 0x65, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6e, - 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x15, 0x47, 0x65, 0x74, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6e, - 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xb7, 0x06, 0x0a, 0x09, 0x41, 0x50, - 0x49, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x12, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, 0x50, - 0x49, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x16, 0x47, 0x65, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x70, + 0x76, 0x34, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, + 0x76, 0x36, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x12, 0x2f, + 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x12, + 0x36, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x18, 0x0d, 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, 0x08, 0x6c, + 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x49, 0x44, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x44, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, + 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x52, 0x03, 0x70, 0x73, 0x6b, 0x52, 0x0e, + 0x6b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x52, 0x07, + 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x22, 0xaf, 0x01, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, + 0x02, 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, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x06, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x61, 0x69, 0x73, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x22, 0x4c, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x46, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, + 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x0d, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, + 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x36, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x61, 0x77, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x61, + 0x77, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x2a, 0xf2, 0x01, 0x0a, 0x0a, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x6f, 0x6f, 0x74, + 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x0d, 0x0a, + 0x09, 0x55, 0x6e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, + 0x51, 0x75, 0x69, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x06, 0x12, 0x17, + 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x61, + 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x10, 0x09, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x10, 0x0a, 0x22, 0x04, 0x08, 0x07, 0x10, 0x07, 0x22, 0x04, 0x08, 0x08, + 0x10, 0x08, 0x22, 0x04, 0x08, 0x0b, 0x10, 0x0b, 0x2a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x2a, 0x09, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x43, 0x65, 0x72, 0x74, 0x2a, 0x57, 0x0a, 0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x76, - 0x0a, 0x17, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x6e, 0x61, 0x69, 0x73, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x45, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, - 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x1e, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x0d, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, - 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x10, + 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x10, 0x02, 0x2a, 0x25, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x10, 0x00, + 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x10, 0x01, 0x2a, 0x47, 0x0a, 0x08, + 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, + 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x10, 0x01, 0x12, 0x0b, + 0x0a, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, + 0x61, 0x6e, 0x67, 0x65, 0x72, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x72, 0x69, 0x74, 0x69, + 0x63, 0x61, 0x6c, 0x10, 0x04, 0x32, 0xef, 0x02, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x65, 0x12, 0x19, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1d, + 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x47, 0x0a, 0x08, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1b, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x12, 0x1a, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x67, + 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x61, 0x69, 0x73, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x04, 0x50, 0x69, + 0x6e, 0x67, 0x12, 0x17, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xed, 0x04, 0x0a, 0x0b, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x30, 0x01, 0x12, 0x56, + 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, 0x41, 0x12, + 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x4a, 0x49, 0x54, 0x41, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, + 0x18, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6e, 0x61, 0x69, 0x73, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, + 0x12, 0x19, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, + 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0f, 0x53, 0x65, 0x74, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x6e, + 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, + 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, + 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6e, 0x61, 0x69, + 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6e, 0x61, 0x69, + 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xb7, 0x06, 0x0a, 0x09, 0x41, 0x50, 0x49, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x21, + 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, 0x50, 0x49, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x41, + 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x29, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6e, + 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x76, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x45, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x0d, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, - 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, - 0x20, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, - 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x21, 0x2e, 0x6e, 0x61, 0x69, - 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, - 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x6f, - 0x6c, 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x1f, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6e, 0x61, 0x69, 0x73, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0d, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x20, 0x2e, + 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, + 0x69, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x21, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, 0x64, 0x65, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x61, + 0x69, 0x73, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x6f, 0x6c, 0x69, + 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x1f, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6e, 0x61, 0x69, 0x73, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3091,56 +3105,58 @@ var file_internal_pb_protobuf_api_proto_depIdxs = []int32{ 48, // 20: naisdevice.DeviceIssue.lastUpdated:type_name -> google.protobuf.Timestamp 48, // 21: naisdevice.DeviceIssue.resolveBefore:type_name -> google.protobuf.Timestamp 48, // 22: naisdevice.Device.lastUpdated:type_name -> google.protobuf.Timestamp - 48, // 23: naisdevice.Session.expiry:type_name -> google.protobuf.Timestamp - 40, // 24: naisdevice.Session.device:type_name -> naisdevice.Device - 41, // 25: naisdevice.GetSessionsResponse.sessions:type_name -> naisdevice.Session - 23, // 26: naisdevice.DeviceHelper.Configure:input_type -> naisdevice.Configuration - 4, // 27: naisdevice.DeviceHelper.Teardown:input_type -> naisdevice.TeardownRequest - 10, // 28: naisdevice.DeviceHelper.Upgrade:input_type -> naisdevice.UpgradeRequest - 12, // 29: naisdevice.DeviceHelper.GetSerial:input_type -> naisdevice.GetSerialRequest - 44, // 30: naisdevice.DeviceHelper.Ping:input_type -> naisdevice.PingRequest - 21, // 31: naisdevice.DeviceAgent.Status:input_type -> naisdevice.AgentStatusRequest - 14, // 32: naisdevice.DeviceAgent.ConfigureJITA:input_type -> naisdevice.ConfigureJITARequest - 15, // 33: naisdevice.DeviceAgent.Login:input_type -> naisdevice.LoginRequest - 16, // 34: naisdevice.DeviceAgent.Logout:input_type -> naisdevice.LogoutRequest - 28, // 35: naisdevice.DeviceAgent.SetActiveTenant:input_type -> naisdevice.SetActiveTenantRequest - 17, // 36: naisdevice.DeviceAgent.SetAgentConfiguration:input_type -> naisdevice.SetAgentConfigurationRequest - 19, // 37: naisdevice.DeviceAgent.GetAgentConfiguration:input_type -> naisdevice.GetAgentConfigurationRequest - 35, // 38: naisdevice.APIServer.Login:input_type -> naisdevice.APIServerLoginRequest - 34, // 39: naisdevice.APIServer.GetDeviceConfiguration:input_type -> naisdevice.GetDeviceConfigurationRequest - 32, // 40: naisdevice.APIServer.GetGatewayConfiguration:input_type -> naisdevice.GetGatewayConfigurationRequest - 24, // 41: naisdevice.APIServer.GetGateway:input_type -> naisdevice.ModifyGatewayRequest - 39, // 42: naisdevice.APIServer.ListGateways:input_type -> naisdevice.ListGatewayRequest - 24, // 43: naisdevice.APIServer.EnrollGateway:input_type -> naisdevice.ModifyGatewayRequest - 24, // 44: naisdevice.APIServer.UpdateGateway:input_type -> naisdevice.ModifyGatewayRequest - 42, // 45: naisdevice.APIServer.GetSessions:input_type -> naisdevice.GetSessionsRequest - 46, // 46: naisdevice.APIServer.GetKolideCache:input_type -> naisdevice.GetKolideCacheRequest - 6, // 47: naisdevice.DeviceHelper.Configure:output_type -> naisdevice.ConfigureResponse - 5, // 48: naisdevice.DeviceHelper.Teardown:output_type -> naisdevice.TeardownResponse - 11, // 49: naisdevice.DeviceHelper.Upgrade:output_type -> naisdevice.UpgradeResponse - 13, // 50: naisdevice.DeviceHelper.GetSerial:output_type -> naisdevice.GetSerialResponse - 45, // 51: naisdevice.DeviceHelper.Ping:output_type -> naisdevice.PingResponse - 22, // 52: naisdevice.DeviceAgent.Status:output_type -> naisdevice.AgentStatus - 7, // 53: naisdevice.DeviceAgent.ConfigureJITA:output_type -> naisdevice.ConfigureJITAResponse - 8, // 54: naisdevice.DeviceAgent.Login:output_type -> naisdevice.LoginResponse - 9, // 55: naisdevice.DeviceAgent.Logout:output_type -> naisdevice.LogoutResponse - 29, // 56: naisdevice.DeviceAgent.SetActiveTenant:output_type -> naisdevice.SetActiveTenantResponse - 18, // 57: naisdevice.DeviceAgent.SetAgentConfiguration:output_type -> naisdevice.SetAgentConfigurationResponse - 20, // 58: naisdevice.DeviceAgent.GetAgentConfiguration:output_type -> naisdevice.GetAgentConfigurationResponse - 36, // 59: naisdevice.APIServer.Login:output_type -> naisdevice.APIServerLoginResponse - 37, // 60: naisdevice.APIServer.GetDeviceConfiguration:output_type -> naisdevice.GetDeviceConfigurationResponse - 33, // 61: naisdevice.APIServer.GetGatewayConfiguration:output_type -> naisdevice.GetGatewayConfigurationResponse - 26, // 62: naisdevice.APIServer.GetGateway:output_type -> naisdevice.Gateway - 26, // 63: naisdevice.APIServer.ListGateways:output_type -> naisdevice.Gateway - 25, // 64: naisdevice.APIServer.EnrollGateway:output_type -> naisdevice.ModifyGatewayResponse - 25, // 65: naisdevice.APIServer.UpdateGateway:output_type -> naisdevice.ModifyGatewayResponse - 43, // 66: naisdevice.APIServer.GetSessions:output_type -> naisdevice.GetSessionsResponse - 47, // 67: naisdevice.APIServer.GetKolideCache:output_type -> naisdevice.GetKolideCacheResponse - 47, // [47:68] is the sub-list for method output_type - 26, // [26:47] is the sub-list for method input_type - 26, // [26:26] is the sub-list for extension type_name - 26, // [26:26] is the sub-list for extension extendee - 0, // [0:26] is the sub-list for field type_name + 38, // 23: naisdevice.Device.issues:type_name -> naisdevice.DeviceIssue + 48, // 24: naisdevice.Device.lastSeen:type_name -> google.protobuf.Timestamp + 48, // 25: naisdevice.Session.expiry:type_name -> google.protobuf.Timestamp + 40, // 26: naisdevice.Session.device:type_name -> naisdevice.Device + 41, // 27: naisdevice.GetSessionsResponse.sessions:type_name -> naisdevice.Session + 23, // 28: naisdevice.DeviceHelper.Configure:input_type -> naisdevice.Configuration + 4, // 29: naisdevice.DeviceHelper.Teardown:input_type -> naisdevice.TeardownRequest + 10, // 30: naisdevice.DeviceHelper.Upgrade:input_type -> naisdevice.UpgradeRequest + 12, // 31: naisdevice.DeviceHelper.GetSerial:input_type -> naisdevice.GetSerialRequest + 44, // 32: naisdevice.DeviceHelper.Ping:input_type -> naisdevice.PingRequest + 21, // 33: naisdevice.DeviceAgent.Status:input_type -> naisdevice.AgentStatusRequest + 14, // 34: naisdevice.DeviceAgent.ConfigureJITA:input_type -> naisdevice.ConfigureJITARequest + 15, // 35: naisdevice.DeviceAgent.Login:input_type -> naisdevice.LoginRequest + 16, // 36: naisdevice.DeviceAgent.Logout:input_type -> naisdevice.LogoutRequest + 28, // 37: naisdevice.DeviceAgent.SetActiveTenant:input_type -> naisdevice.SetActiveTenantRequest + 17, // 38: naisdevice.DeviceAgent.SetAgentConfiguration:input_type -> naisdevice.SetAgentConfigurationRequest + 19, // 39: naisdevice.DeviceAgent.GetAgentConfiguration:input_type -> naisdevice.GetAgentConfigurationRequest + 35, // 40: naisdevice.APIServer.Login:input_type -> naisdevice.APIServerLoginRequest + 34, // 41: naisdevice.APIServer.GetDeviceConfiguration:input_type -> naisdevice.GetDeviceConfigurationRequest + 32, // 42: naisdevice.APIServer.GetGatewayConfiguration:input_type -> naisdevice.GetGatewayConfigurationRequest + 24, // 43: naisdevice.APIServer.GetGateway:input_type -> naisdevice.ModifyGatewayRequest + 39, // 44: naisdevice.APIServer.ListGateways:input_type -> naisdevice.ListGatewayRequest + 24, // 45: naisdevice.APIServer.EnrollGateway:input_type -> naisdevice.ModifyGatewayRequest + 24, // 46: naisdevice.APIServer.UpdateGateway:input_type -> naisdevice.ModifyGatewayRequest + 42, // 47: naisdevice.APIServer.GetSessions:input_type -> naisdevice.GetSessionsRequest + 46, // 48: naisdevice.APIServer.GetKolideCache:input_type -> naisdevice.GetKolideCacheRequest + 6, // 49: naisdevice.DeviceHelper.Configure:output_type -> naisdevice.ConfigureResponse + 5, // 50: naisdevice.DeviceHelper.Teardown:output_type -> naisdevice.TeardownResponse + 11, // 51: naisdevice.DeviceHelper.Upgrade:output_type -> naisdevice.UpgradeResponse + 13, // 52: naisdevice.DeviceHelper.GetSerial:output_type -> naisdevice.GetSerialResponse + 45, // 53: naisdevice.DeviceHelper.Ping:output_type -> naisdevice.PingResponse + 22, // 54: naisdevice.DeviceAgent.Status:output_type -> naisdevice.AgentStatus + 7, // 55: naisdevice.DeviceAgent.ConfigureJITA:output_type -> naisdevice.ConfigureJITAResponse + 8, // 56: naisdevice.DeviceAgent.Login:output_type -> naisdevice.LoginResponse + 9, // 57: naisdevice.DeviceAgent.Logout:output_type -> naisdevice.LogoutResponse + 29, // 58: naisdevice.DeviceAgent.SetActiveTenant:output_type -> naisdevice.SetActiveTenantResponse + 18, // 59: naisdevice.DeviceAgent.SetAgentConfiguration:output_type -> naisdevice.SetAgentConfigurationResponse + 20, // 60: naisdevice.DeviceAgent.GetAgentConfiguration:output_type -> naisdevice.GetAgentConfigurationResponse + 36, // 61: naisdevice.APIServer.Login:output_type -> naisdevice.APIServerLoginResponse + 37, // 62: naisdevice.APIServer.GetDeviceConfiguration:output_type -> naisdevice.GetDeviceConfigurationResponse + 33, // 63: naisdevice.APIServer.GetGatewayConfiguration:output_type -> naisdevice.GetGatewayConfigurationResponse + 26, // 64: naisdevice.APIServer.GetGateway:output_type -> naisdevice.Gateway + 26, // 65: naisdevice.APIServer.ListGateways:output_type -> naisdevice.Gateway + 25, // 66: naisdevice.APIServer.EnrollGateway:output_type -> naisdevice.ModifyGatewayResponse + 25, // 67: naisdevice.APIServer.UpdateGateway:output_type -> naisdevice.ModifyGatewayResponse + 43, // 68: naisdevice.APIServer.GetSessions:output_type -> naisdevice.GetSessionsResponse + 47, // 69: naisdevice.APIServer.GetKolideCache:output_type -> naisdevice.GetKolideCacheResponse + 49, // [49:70] is the sub-list for method output_type + 28, // [28:49] is the sub-list for method input_type + 28, // [28:28] is the sub-list for extension type_name + 28, // [28:28] is the sub-list for extension extendee + 0, // [0:28] is the sub-list for field type_name } func init() { file_internal_pb_protobuf_api_proto_init() } diff --git a/internal/pb/protobuf-api.proto b/internal/pb/protobuf-api.proto index c796495c9..47340ab58 100644 --- a/internal/pb/protobuf-api.proto +++ b/internal/pb/protobuf-api.proto @@ -324,12 +324,16 @@ message Device { google.protobuf.Timestamp lastUpdated = 4; reserved 'kolideLastSeen'; reserved 5; - bool healthy = 6; + reserved 'healthy'; + reserved 6; string publicKey = 7; string ipv4 = 8; string username = 9; string platform = 10; string ipv6 = 11; + repeated DeviceIssue issues = 12; + google.protobuf.Timestamp lastSeen = 13; + string externalID = 14; } message Session { @@ -360,6 +364,5 @@ message GetKolideCacheRequest { } message GetKolideCacheResponse { - bytes rawDevices = 98; bytes rawChecks = 99; } diff --git a/internal/pb/protobuf-api_grpc.pb.go b/internal/pb/protobuf-api_grpc.pb.go index 20702b3b7..6cda11751 100644 --- a/internal/pb/protobuf-api_grpc.pb.go +++ b/internal/pb/protobuf-api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.23.4 // source: internal/pb/protobuf-api.proto package pb diff --git a/internal/systray/systray.go b/internal/systray/systray.go index 1c9ba1f4b..a5a0ee6a4 100644 --- a/internal/systray/systray.go +++ b/internal/systray/systray.go @@ -27,7 +27,7 @@ type trayState struct { func (s *trayState) onReady() { var err error - s.connection, err = grpc.Dial( + s.connection, err = grpc.NewClient( "unix:"+s.cfg.GrpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithStatsHandler(otel.NewGRPCClientHandler(pb.DeviceAgent_Status_FullMethodName)),